diff --git a/src/main.cpp b/src/main.cpp index 3c8f12c..0929341 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,28 +43,6 @@ GNU General Public License for more details. #include "scsiencrypt.h" -typedef struct { -#if STENC_BIG_ENDIAN == 0 - unsigned char bit1 : 1; - unsigned char bit2 : 1; - unsigned char bit3 : 1; - unsigned char bit4 : 1; - unsigned char bit5 : 1; - unsigned char bit6 : 1; - unsigned char bit7 : 1; - unsigned char bit8 : 1; -#else - unsigned char bit8 : 1; - unsigned char bit7 : 1; - unsigned char bit6 : 1; - unsigned char bit5 : 1; - unsigned char bit4 : 1; - unsigned char bit3 : 1; - unsigned char bit2 : 1; - unsigned char bit1 : 1; -#endif -} bitcheck; - void showUsage(); void errorOut(const std::string& message); void inquiryDrive(const std::string& tapeDevice); @@ -101,40 +79,18 @@ static std::optional> key_from_hex_chars(const std::string& #if !defined(CATCH_CONFIG_MAIN) int main(int argc, const char **argv) { - bitcheck bc; - memset(&bc, 0, 1); - bc.bit2 = 1; - bc.bit5 = 1; - unsigned char check; - memcpy(&check, &bc, 1); - - switch ((int)check) { - case 0x12: - // this is good - break; - case 0x48: -#if STENC_BIG_ENDIAN == 1 - errorOut("Swapped bit ordering detected(BI). Program needs to be " - "configured without the --enable-swapendian option in order to " - "function properly on your system"); -#else - errorOut("Swapped bit ordering detected(LI). Program needs to be " - "configured with the --enable-swapendian option in order to " - "function properly on your system"); -#endif - break; - default: - std::cerr << "Unknown bit check result " << std::hex << check << "\n"; - errorOut("Exiting program because it will not run properly"); - break; - } - std::string tapeDrive; int action = 0; // 0 = status, 1 =setting param, 2 = generating key std::string keyFile, keyDesc; - int keyLength = 0; bool detail = false; - SCSIEncryptOptions drvOptions; + + scsi::encrypt_mode enc_mode; + scsi::decrypt_mode dec_mode; + std::uint8_t algorithm_index; + std::vector key; + std::string key_name; + scsi::sde_rdmc rdmc {}; + bool ckod {}; // First load all of the options for (int i = 1; i < argc; i++) { @@ -150,23 +106,29 @@ int main(int argc, const char **argv) { exit(EXIT_SUCCESS); } if (thisCmd == "-e") { - if (nextCmd == "") + if (nextCmd == ""){ errorOut("Key file not specified after -k option"); - if (nextCmd == "on") - drvOptions.cryptMode = CRYPTMODE_ON; // encrypt, read only encrypted - // data - else if (nextCmd == "mixed") - drvOptions.cryptMode = - CRYPTMODE_MIXED; // encrypt, read encrypted and unencrypted data - else if (nextCmd == "rawread") - drvOptions.cryptMode = - CRYPTMODE_RAWREAD; // encrypt, read encrypted and unencrypted data - else if (nextCmd == "off") - drvOptions.cryptMode = - CRYPTMODE_OFF; // encrypt, read encrypted and unencrypted data - else + } + if (nextCmd == "on"){ + // encrypt, read only encrypted data + enc_mode = scsi::encrypt_mode::on; + dec_mode = scsi::decrypt_mode::on; + } else if (nextCmd == "mixed") { + // encrypt, read encrypted and unencrypted data + enc_mode = scsi::encrypt_mode::on; + dec_mode = scsi::decrypt_mode::mixed; + } else if (nextCmd == "rawread") { + // encrypt, read encrypted and unencrypted data + enc_mode = scsi::encrypt_mode::on; + dec_mode = scsi::decrypt_mode::raw; + } else if (nextCmd == "off") { + // encrypt, read encrypted and unencrypted data + enc_mode = scsi::encrypt_mode::off; + dec_mode = scsi::decrypt_mode::off; + } else{ errorOut("Unknown encryption mode '" + nextCmd + "'"); // encrypt, read encrypted and unencrypted data + } i++; // skip the next argument action = 1; } else if (thisCmd == "-f") { @@ -188,24 +150,26 @@ int main(int argc, const char **argv) { } i++; // skip the next argument } else if (thisCmd == "--protect") { - if (drvOptions.rdmc == RDMC_UNPROTECT) + if (rdmc == scsi::sde_rdmc::enabled) { errorOut("'--protect' cannot be specified at the same time as " "'--unprotect'"); - drvOptions.rdmc = RDMC_PROTECT; + } + rdmc = scsi::sde_rdmc::disabled; } else if (thisCmd == "--unprotect") { - if (drvOptions.rdmc == RDMC_PROTECT) + if (rdmc == scsi::sde_rdmc::disabled){ errorOut("'--unprotect' cannot be specified at the same time as " "'--protect'"); - drvOptions.rdmc = RDMC_UNPROTECT; + } + rdmc = scsi::sde_rdmc::enabled; } else if (thisCmd == "--ckod") { - drvOptions.CKOD = true; + ckod = true; } else if (thisCmd == "--detail") { detail = true; } else if (thisCmd == "-a") { if (nextCmd == "") errorOut("You must specify a numeric algorithm index when using the -a " "flag"); - drvOptions.algorithmIndex = std::atoi(nextCmd.c_str()); + algorithm_index = std::atoi(nextCmd.c_str()); i++; // skip the next argument } else { errorOut("Unknown command '" + thisCmd + "'"); @@ -221,8 +185,7 @@ int main(int argc, const char **argv) { tapeDrive = DEFTAPE; } } - if (drvOptions.cryptMode == CRYPTMODE_RAWREAD && - drvOptions.rdmc == RDMC_PROTECT) { + if (dec_mode == scsi::decrypt_mode::raw && rdmc == scsi::sde_rdmc::disabled) { errorOut( "'--protect' is not valid when setting encryption mode to 'rawread'"); } @@ -233,16 +196,26 @@ int main(int argc, const char **argv) { std::cout << "Status for " << tapeDrive << "\n" << "--------------------------------------------------\n"; - if (detail) - inquiryDrive(tapeDrive); - showDriveStatus(tapeDrive, detail); - if (detail) - showVolumeStatus(tapeDrive); - exit(EXIT_SUCCESS); + try { + if (detail){ + inquiryDrive(tapeDrive); + } + showDriveStatus(tapeDrive, detail); + if (detail){ + showVolumeStatus(tapeDrive); + } + exit(EXIT_SUCCESS); + } catch (const scsi::scsi_error& err) { + scsi::print_sense_data(std::cerr, err.get_sense()); + exit(EXIT_FAILURE); + } catch (const std::runtime_error& err) { + std::cerr << err.what() << '\n'; + exit(EXIT_FAILURE); + } } - if (drvOptions.cryptMode != CRYPTMODE_OFF) { - if (keyFile == "") { + if (enc_mode == scsi::encrypt_mode::on) { + if (keyFile.empty()) { std::string p1; std::string p2; bool done = false; @@ -266,7 +239,7 @@ int main(int argc, const char **argv) { std::string ans = ""; getline(std::cin, ans); if (ans == "y") { - drvOptions.cryptoKey = *key_bytes; + key = *key_bytes; done = true; } } else { @@ -274,8 +247,7 @@ int main(int argc, const char **argv) { } } } - drvOptions.keyName = keyDesc; - + key_name = keyDesc; } else { // set keyInput here std::string keyInput; @@ -285,11 +257,11 @@ int main(int argc, const char **argv) { getline(myfile, keyDesc); myfile.close(); if (auto key_bytes = key_from_hex_chars(keyInput)) { - drvOptions.cryptoKey = *key_bytes; + key = *key_bytes; } else { errorOut("Invalid key found in '" + keyFile + "'"); } - drvOptions.keyName = keyDesc; + key_name = keyDesc; } else errorOut("Could not open '" + keyFile + "' for reading"); } @@ -297,27 +269,31 @@ int main(int argc, const char **argv) { // Write the options to the tape device std::cout << "Turning " - << ((drvOptions.cryptMode != CRYPTMODE_OFF) ? "on" : "off") + << (enc_mode != scsi::encrypt_mode::off ? "on" : "off") << " encryption on device '" << tapeDrive << "'..." << std::endl; - bool res = SCSIWriteEncryptOptions(tapeDrive, &drvOptions); - if (res) { + try { + auto sde_buffer {scsi::make_sde(enc_mode, dec_mode, algorithm_index, + key, key_name, rdmc, ckod)}; + scsi::write_sde(tapeDrive, sde_buffer.get()); - SSP_DES *opt = SSPGetDES(tapeDrive); - if (drvOptions.cryptMode != CRYPTMODE_OFF && opt->des.encryptionMode != 2) { + alignas(4) scsi::page_buffer buffer; + scsi::get_des(tapeDrive, buffer, sizeof(buffer)); + auto& opt {reinterpret_cast(buffer)}; + + if (enc_mode != scsi::encrypt_mode::off && opt.encryption_mode == scsi::encrypt_mode::off) { errorOut("Turning encryption on for '" + tapeDrive + "' failed!"); } - if (drvOptions.cryptMode == CRYPTMODE_OFF && opt->des.encryptionMode != 0) { + if (enc_mode == scsi::encrypt_mode::off && opt.encryption_mode != scsi::encrypt_mode::off) { errorOut("Turning encryption off for '" + tapeDrive + "' failed!"); } - delete opt; - if (drvOptions.cryptMode != CRYPTMODE_OFF) { + if (enc_mode != scsi::encrypt_mode::off) { std::stringstream msg; msg << "Encryption turned on for device '" << tapeDrive << "'. "; - if (!drvOptions.keyName.empty()) { - msg << "Key Descriptor: '" << drvOptions.keyName << "'"; + if (!key_name.empty()) { + msg << "Key Descriptor: '" << key_name << "'"; } - msg << " Key Instance: " << std::dec << BSLONG(opt->des.keyInstance) + msg << " Key Instance: " << std::dec << ntohl(opt.key_instance_counter) << std::endl; syslog(LOG_NOTICE, "%s", msg.str().c_str()); @@ -325,15 +301,20 @@ int main(int argc, const char **argv) { std::stringstream msg{}; msg << "Encryption turned off for device '" << tapeDrive << "'."; - msg << " Key Instance: " << std::dec << BSLONG(opt->des.keyInstance) + msg << " Key Instance: " << std::dec << ntohl(opt.key_instance_counter) << std::endl; syslog(LOG_NOTICE, "%s", msg.str().c_str()); } std::cout << "Success! See system logs for a key change audit log.\n"; exit(EXIT_SUCCESS); + } catch (const scsi::scsi_error& err) { + scsi::print_sense_data(std::cerr, err.get_sense()); + } catch (const std::runtime_error& err) { + std::cerr << err.what() << '\n'; } - if (drvOptions.cryptMode != CRYPTMODE_OFF) { + + if (enc_mode != scsi::encrypt_mode::off) { errorOut("Turning encryption on for '" + tapeDrive + "' failed!"); } else { errorOut("Turning encryption off for '" + tapeDrive + "' failed!"); @@ -357,213 +338,204 @@ void showUsage() { "Type 'man stenc' for more information.\n"; } -static void print_device_inquiry(std::ostream& os, const SCSI_PAGE_INQ *iresult) +static void print_device_inquiry(std::ostream& os, const scsi::inquiry_data& iresult) { os << std::left << std::setw(25) << "Vendor:"; - os.write((const char *)iresult->vender, 8); + os.write(iresult.vendor, 8); os.put('\n'); os << std::left << std::setw(25) << "Product ID:"; - os.write((const char *)iresult->productID, 16); + os.write(iresult.product_id, 16); os.put('\n'); os << std::left << std::setw(25) << "Product Revision:"; - os.write((const char *)iresult->productRev, 4); + os.write(iresult.product_rev, 4); os.put('\n'); } void inquiryDrive(const std::string& tapeDevice) { // todo: std::cout should not be used outside main() - SCSI_PAGE_INQ *const iresult = SCSIGetInquiry(tapeDevice); + auto iresult {scsi::get_inquiry(tapeDevice)}; print_device_inquiry(std::cout, iresult); - delete iresult; } -static void print_device_status(std::ostream& os, const SSP_DES *opt, bool detail) +static void print_device_status(std::ostream& os, const scsi::page_des& opt, bool detail) { std::string emode = "unknown"; os << std::left << std::setw(25) << "Drive Encryption:"; - if ((int)opt->des.encryptionMode == 0x2 && // encrypt - (int)opt->des.decryptionMode == 0x2 // read only encrypted data + if (opt.encryption_mode == scsi::encrypt_mode::on && // encrypt + opt.decryption_mode == scsi::decrypt_mode::on // read only encrypted data ) emode = "on"; - if ((int)opt->des.encryptionMode == 0x2 && // encrypt - (int)opt->des.decryptionMode == 0x3 // read encrypted and unencrypted + if (opt.encryption_mode == scsi::encrypt_mode::on && // encrypt + opt.decryption_mode == scsi::decrypt_mode::mixed // read encrypted and unencrypted ) emode = "mixed"; - if ((int)opt->des.encryptionMode == 0x2 && // encrypt - (int)opt->des.decryptionMode == 0x1 // read encrypted and unencrypted + if (opt.encryption_mode == scsi::encrypt_mode::on && // encrypt + opt.decryption_mode == scsi::decrypt_mode::raw // read encrypted and unencrypted ) emode = "rawread"; - if ((int)opt->des.encryptionMode == 0x0 && // encrypt - (int)opt->des.decryptionMode == 0x0 // read encrypted and unencrypted + if (opt.encryption_mode == scsi::encrypt_mode::off && // encrypt + opt.decryption_mode == scsi::decrypt_mode::off // read encrypted and unencrypted ) emode = "off"; os << emode << "\n"; if (detail) { os << std::left << std::setw(25) << "Drive Output:"; - switch ((int)opt->des.decryptionMode) { - case 0x0: + switch (opt.decryption_mode) { + case scsi::decrypt_mode::off: os << "Not decrypting\n"; os << std::setw(25) << " " << "Raw encrypted data not outputted\n"; break; - case 0x1: + case scsi::decrypt_mode::raw: os << "Not decrypting\n"; os << std::setw(25) << " " << "Raw encrypted data outputted\n"; break; - case 0x2: + case scsi::decrypt_mode::on: os << "Decrypting\n"; os << std::setw(25) << " " << "Unencrypted data not outputted\n"; break; - case 0x3: + case scsi::decrypt_mode::mixed: os << "Decrypting\n"; os << std::setw(25) << " " << "Unencrypted data outputted\n"; break; default: - os << "Unknown '0x" << std::hex << (int)opt->des.decryptionMode + os << "Unknown '0x" << std::hex << static_cast(opt.decryption_mode) << "' \n"; break; } os << std::setw(25) << "Drive Input:"; - switch ((int)opt->des.encryptionMode) { - case 0x0: + switch (opt.encryption_mode) { + case scsi::encrypt_mode::off: os << "Not encrypting\n"; break; - case 0x2: + case scsi::encrypt_mode::on: os << "Encrypting\n"; break; default: os << "Unknown result '0x" << std::hex - << (int)opt->des.encryptionMode << "'\n"; + << static_cast(opt.encryption_mode) << "'\n"; break; } - if (opt->des.RDMD == 1) { + if ((opt.flags & scsi::page_des::flags_rdmd_mask) == scsi::page_des::flags_rdmd_mask) { os << std::setw(25) << " " << "Protecting from raw read\n"; } os << std::setw(25) << "Key Instance Counter:" << std::dec - << BSLONG(opt->des.keyInstance) << "\n"; - if (opt->des.algorithmIndex != 0) { + << ntohl(opt.key_instance_counter) << "\n"; + if (opt.algorithm_index != 0) { os << std::setw(25) << "Encryption Algorithm:" << std::hex - << (int)opt->des.algorithmIndex << "\n"; + << static_cast(opt.algorithm_index) << "\n"; } } - if (opt->kads.size() > 0) { - for (unsigned int i = 0; i < opt->kads.size(); i++) { - std::stringstream lbl{}; - lbl << "Drive Key Desc.("; - switch (opt->kads[i].type) { - case KAD_TYPE_UKAD: - lbl << "uKAD): "; - os << std::setw(25) << lbl.str(); - os.write((const char *)&opt->kads[i].descriptor, - BSSHORT(opt->kads[i].descriptorLength)); - os.put('\n'); - break; - case KAD_TYPE_AKAD: - lbl << "aKAD): "; - os << std::setw(25) << lbl.str(); - os.write((const char *)&opt->kads[i].descriptor, - BSSHORT(opt->kads[i].descriptorLength)); - os.put('\n'); - break; - } + auto kads {scsi::read_page_kads(opt)}; + for (auto kd: kads) { + switch (kd->type) { + case KAD_TYPE_UKAD: + os << std::setw(25) << "Drive Key Desc.(uKAD): "; + os.write(reinterpret_cast(kd->descriptor), ntohs(kd->length)); + os.put('\n'); + break; + case KAD_TYPE_AKAD: + os << std::setw(25) << "Drive Key Desc.(aKAD): "; + os.write(reinterpret_cast(kd->descriptor), ntohs(kd->length)); + os.put('\n'); + break; } } } void showDriveStatus(const std::string& tapeDrive, bool detail) { - SSP_DES *opt = SSPGetDES(tapeDrive); - if (opt == NULL) - return; + alignas(4) scsi::page_buffer buffer; + scsi::get_des(tapeDrive, buffer, sizeof(buffer)); + auto& opt {reinterpret_cast(buffer)}; print_device_status(std::cout, opt, detail); - delete opt; } -static void print_volume_status(std::ostream& os, const SSP_NBES *opt) +static void print_volume_status(std::ostream& os, const scsi::page_nbes& opt) { - if (opt->nbes.compressionStatus != 0) { + auto compression_status { + static_cast((opt.status & scsi::page_nbes::status_compression_mask) + >> scsi::page_nbes::status_compression_pos) + }; + if (compression_status != 0u) { os << std::left << std::setw(25) << "Volume Compressed:"; - switch (opt->nbes.compressionStatus) { - case 0x00: + switch (compression_status) { + case 0u: os << "Drive cannot determine\n"; break; default: os << "Unknown result '" << std::hex - << (int)opt->nbes.compressionStatus << "'\n"; + << static_cast(compression_status) << "'\n"; break; } } os << std::left << std::setw(25) << "Volume Encryption:"; - switch ((int)opt->nbes.encryptionStatus) { - case 0x01: + auto encryption_status { + static_cast((opt.status & scsi::page_nbes::status_encryption_mask) + >> scsi::page_nbes::status_encryption_pos) + }; + auto kads {read_page_kads(opt)}; + switch (encryption_status) { + case 1u: os << "Unable to determine\n"; break; - case 0x02: + case 2u: os << "Logical block is not a logical block\n"; break; - case 0x03: + case 3u: os << "Not encrypted\n"; break; - case 0x05: + case 5u: os << "Encrypted and able to decrypt\n"; - if (opt->nbes.RDMDS == 1) - os << std::left << std::setw(25) - << " Protected from raw read\n"; + if ((opt.flags & scsi::page_nbes::flags_rdmds_mask) == scsi::page_nbes::flags_rdmds_mask) { + os << std::left << std::setw(25) << " Protected from raw read\n"; + } break; - case 0x06: + case 6u: os << "Encrypted, but unable to decrypt due to invalid key.\n"; - if (opt->kads.size() > 0) { - for (unsigned int i = 0; i < opt->kads.size(); i++) { - std::stringstream lbl; - lbl << "Volume Key Desc.("; - switch (opt->kads[i].type) { - case KAD_TYPE_UKAD: - lbl << "uKAD): "; - os << std::setw(25) << lbl.str(); - os.write((const char *)&opt->kads[i].descriptor, - BSSHORT(opt->kads[i].descriptorLength)); - os.put('\n'); - break; - case KAD_TYPE_AKAD: - lbl << "aKAD): "; - os << std::setw(25) << lbl.str(); - os.write((const char *)&opt->kads[i].descriptor, - BSSHORT(opt->kads[i].descriptorLength)); - os.put('\n'); - break; - } + for (auto kd: kads) { + switch (kd->type) { + case KAD_TYPE_UKAD: + os << std::setw(25) << "Volume Key Desc.(uKAD): "; + os.write(reinterpret_cast(kd->descriptor), ntohs(kd->length)); + os.put('\n'); + break; + case KAD_TYPE_AKAD: + os << std::setw(25) << "Volume Key Desc.(aKAD): "; + os.write(reinterpret_cast(kd->descriptor), ntohs(kd->length)); + os.put('\n'); + break; } } - if (opt->nbes.RDMDS == 1) + if ((opt.flags & scsi::page_nbes::flags_rdmds_mask) == scsi::page_nbes::flags_rdmds_mask) { os << std::left << std::setw(25) << " Protected from raw read\n"; + } break; - default: os << "Unknown result '" << std::hex - << (int)opt->nbes.encryptionStatus << "'\n"; + << static_cast(encryption_status) << "'\n"; break; } - if (opt->nbes.algorithmIndex != 0) { + if (opt.algorithm_index != 0) { os << std::left << std::setw(25) - << "Volume Algorithm:" << (int)opt->nbes.algorithmIndex << "\n"; + << "Volume Algorithm:" << static_cast(opt.algorithm_index) << "\n"; } } void showVolumeStatus(const std::string& tapeDrive) { - SSP_NBES *opt = SSPGetNBES(tapeDrive, true); - if (opt == NULL) - return; + alignas(4) scsi::page_buffer buffer; + scsi::get_nbes(tapeDrive, buffer, sizeof(buffer)); + auto& opt {reinterpret_cast(buffer)}; print_volume_status(std::cout, opt); - delete opt; } void echo(bool on) { diff --git a/src/scsiencrypt.cpp b/src/scsiencrypt.cpp index e454188..8e103f4 100644 --- a/src/scsiencrypt.cpp +++ b/src/scsiencrypt.cpp @@ -14,8 +14,8 @@ GNU General Public License for more details. */ #include -#include #include +#include #include #include #include @@ -49,179 +49,213 @@ GNU General Public License for more details. #include "scsiencrypt.h" -constexpr uint8_t SSP_SPIN_OPCODE = 0xa2; -constexpr uint8_t SSP_SPOUT_OPCODE = 0xb5; -constexpr uint8_t SSP_SP_CMD_LEN = 12; -constexpr uint8_t SSP_SP_PROTOCOL_TDE = 0x20; +constexpr std::uint8_t SSP_SPIN_OPCODE = 0xa2; +constexpr std::uint8_t SSP_SPOUT_OPCODE = 0xb5; +constexpr std::uint8_t SSP_SP_CMD_LEN = 12; +constexpr std::uint8_t SSP_SP_PROTOCOL_TDE = 0x20; constexpr int RETRYCOUNT = 1; #define BSINTTOCHAR(x) \ - static_cast((x) >> 24), \ - static_cast((x) >> 16), \ - static_cast((x) >> 8), \ - static_cast((x)) + static_cast((x) >> 24), \ + static_cast((x) >> 16), \ + static_cast((x) >> 8), \ + static_cast((x)) -void byteswap(unsigned char *array, int size, int value); -bool moveTape(const std::string& tapeDevice, int count, bool dirForward); -void outputSense(SCSI_PAGE_SENSE *sd); -bool SCSIExecute(const std::string& tapedevice, unsigned char *cmd_p, int cmd_len, - unsigned char *dxfer_p, int dxfer_len, bool cmd_to_device, - bool show_error); +// generic_deleter permits the use of std::unique_ptr for RAII on non-pointer +// types like file descriptors. +template +struct generic_deleter { + class pointer { + T t; + public: + pointer() : t {null_value} {} + pointer(T t) : t {t} {} + pointer(std::nullptr_t) : t {null_value} {} + explicit operator bool() const noexcept { return t != null_value; } + friend bool operator ==(pointer lhs, pointer rhs) noexcept { return lhs.t == rhs.t; } + friend bool operator !=(pointer lhs, pointer rhs) noexcept { return !(lhs == rhs); } + operator T() const noexcept { return t; } + }; -// Gets encryption options on the tape drive -SSP_DES *SSPGetDES(const std::string& tapeDevice) { - const uint8_t spin_des_command[] { + void operator()(pointer p) const noexcept { d(p); } +}; +using unique_fd = std::unique_ptr>; + +enum class scsi_direction { to_device, from_device }; + +static void scsi_execute(const std::string& device, const std::uint8_t *cmd_p, + std::size_t cmd_len, const std::uint8_t *dxfer_p, + std::size_t dxfer_len, scsi_direction direction) +{ +#if defined(OS_LINUX) + unique_fd fd {open(device.c_str(), O_RDONLY)}; + if (!fd) { + std::ostringstream oss; + oss << "Cannot open device " << device; + throw std::system_error {errno, std::generic_category(), oss.str()}; + } + + sg_io_hdr cmdio {}; + auto sense_buf {std::make_unique()}; + + cmdio.cmd_len = cmd_len; + cmdio.dxfer_direction = (direction == scsi_direction::to_device) + ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV; + cmdio.dxfer_len = dxfer_len; + cmdio.dxferp = const_cast(dxfer_p); + cmdio.cmdp = const_cast(cmd_p); + cmdio.sbp = sense_buf->data(); + cmdio.mx_sb_len = sizeof(decltype(sense_buf)::element_type); + cmdio.timeout = SCSI_TIMEOUT; + cmdio.interface_id = 'S'; + + if (ioctl(fd.get(), SG_IO, &cmdio)) { + throw std::system_error {errno, std::generic_category()}; + } + if (cmdio.status) { + throw scsi::scsi_error {std::move(sense_buf)}; + } +#elif defined(OS_FREEBSD) + auto dev = std::unique_ptr + {cam_open_device(device.c_str(), O_RDWR), &cam_close_device}; + if (dev == nullptr) { + std::ostringstream oss; + oss << "Cannot open device " << device << ": " << cam_errbuf; + throw std::runtime_error {oss.str()}; + } + auto ccb = std::unique_ptr + {cam_getccb(dev.get()), &cam_freeccb}; + if (ccb == nullptr) { + throw std::bad_alloc {}; + } + CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio); + + cam_fill_csio(&ccb->csio, RETRYCOUNT, nullptr, + CAM_PASS_ERR_RECOVER | CAM_CDB_POINTER | + (direction == scsi_direction::to_device ? CAM_DIR_OUT : CAM_DIR_IN), + MSG_SIMPLE_Q_TAG, const_cast(dxfer_p), + dxfer_len, SSD_FULL_SIZE, cmd_len, SCSI_TIMEOUT); + ccb->csio.cdb_io.cdb_ptr = const_cast(cmd_p); + if (cam_send_ccb(dev.get(), ccb.get())) { + throw std::system_error {errno, std::generic_category()}; + } + if (ccb->csio.scsi_status) { + auto sense_buf {std::make_unique()}; + std::memcpy(sense_buf->data(), &ccb->csio.sense_data, sizeof(scsi::sense_buffer)); + throw scsi::scsi_error {std::move(sense_buf)}; + } +#else +#error "OS type is not set" +#endif +} + +namespace scsi { + +void get_des(const std::string& device, const std::uint8_t *buffer, + std::size_t length) +{ + const std::uint8_t spin_des_command[] { SSP_SPIN_OPCODE, SSP_SP_PROTOCOL_TDE, 0, 0X20, 0, 0, - BSINTTOCHAR(sizeof(SSP_PAGE_BUFFER)), + BSINTTOCHAR(length), 0, 0, }; - SSP_PAGE_BUFFER buffer; - memset(&buffer, 0, sizeof(SSP_PAGE_BUFFER)); - if (!SCSIExecute(tapeDevice, (unsigned char *)&spin_des_command, - sizeof(spin_des_command), (unsigned char *)&buffer, - sizeof(SSP_PAGE_BUFFER), false, true)) { - return NULL; - } - SSP_DES *status = new SSP_DES(&buffer); - return status; + scsi_execute(device, spin_des_command, sizeof(spin_des_command), + buffer, length, scsi_direction::from_device); } -// Gets encryption options on the tape drive -SSP_NBES *SSPGetNBES(const std::string& tapeDevice, bool retry) { - const uint8_t spin_nbes_command[] { +void get_nbes(const std::string& device, const std::uint8_t *buffer, + std::size_t length) +{ + const std::uint8_t spin_nbes_command[] { SSP_SPIN_OPCODE, SSP_SP_PROTOCOL_TDE, 0, 0X21, 0, 0, - BSINTTOCHAR(sizeof(SSP_PAGE_BUFFER)), + BSINTTOCHAR(length), 0, 0, }; - SSP_PAGE_BUFFER buffer; - memset(&buffer, 0, sizeof(SSP_PAGE_BUFFER)); - if (!SCSIExecute(tapeDevice, (unsigned char *)&spin_nbes_command, - sizeof(spin_nbes_command), (unsigned char *)&buffer, - sizeof(SSP_PAGE_BUFFER), false, false)) { - return NULL; - } - SSP_NBES *status = new SSP_NBES(&buffer); - if (status->nbes.encryptionStatus == 0x01 && retry) { - // move to the start of the tape and try again - int moves = 0; - while (true) { - if (status == NULL) - break; - if (status->nbes.encryptionStatus != 0x01) - break; - if (moves >= MAX_TAPE_READ_BLOCKS) - break; - delete status; - status = NULL; // double free bug fix provided by Adam Nielsen - if (!moveTape(tapeDevice, 1, true)) - break; - moves++; - status = SSPGetNBES(tapeDevice, false); - } - moveTape(tapeDevice, moves, false); - } - return status; + scsi_execute(device, spin_nbes_command, sizeof(spin_nbes_command), + buffer, length, scsi_direction::from_device); } -// Sends and inquiry to the device -SCSI_PAGE_INQ *SCSIGetInquiry(const std::string& tapeDevice) { - const uint8_t scsi_inq_command[] {0x12, 0, 0, 0, sizeof(SCSI_PAGE_INQ), 0}; - SCSI_PAGE_INQ *status = new SCSI_PAGE_INQ; - memset(status, 0, sizeof(SCSI_PAGE_INQ)); - if (!SCSIExecute(tapeDevice, (unsigned char *)&scsi_inq_command, - sizeof(scsi_inq_command), (unsigned char *)status, - sizeof(SCSI_PAGE_INQ), false, true)) { - exit(EXIT_FAILURE); - } - return status; +void get_dec(const std::string& device, const std::uint8_t *buffer, + std::size_t length) +{ + const uint8_t spin_dec_command[] { + SSP_SPIN_OPCODE, + SSP_SP_PROTOCOL_TDE, + 0x00, 0x10, + 0, + 0, + BSINTTOCHAR(length), + 0, + 0, + }; + scsi_execute(device, spin_dec_command, sizeof(spin_dec_command), + buffer, length, scsi_direction::from_device); } -int SCSIInitSDEPage(SCSIEncryptOptions *eOptions, - uint8_t *buffer) { - SSP_PAGE_SDE options; - // copy the template over the options - memset(&options, 0, sizeof(SSP_PAGE_SDE)); - byteswap((unsigned char *)&options.pageCode, 2, 0x10); - int pagelen = sizeof(SSP_PAGE_SDE); - options.scope = 2; // all IT nexus = 10b - options.RDMC = eOptions->rdmc; - options.ckod = (eOptions->CKOD) ? 1 : 0; - options.CEEM = DEFAULT_CEEM; - options.algorithmIndex = eOptions->algorithmIndex; - // set the specific options - switch (eOptions->cryptMode) { - case CRYPTMODE_ON: // encrypt, read only encrypted data - options.encryptionMode = 2; - options.decryptionMode = 2; - break; - case CRYPTMODE_MIXED: // encrypt, read all data - options.encryptionMode = 2; - options.decryptionMode = 3; - break; - case CRYPTMODE_RAWREAD: - options.encryptionMode = 2; - options.decryptionMode = 1; - break; - default: - byteswap((unsigned char *)options.keyLength, 2, DEFAULT_KEYSIZE); - eOptions->cryptoKey.clear(); // blank the key - eOptions->keyName.clear(); // blank the key name, not supported when turned off - break; - } - - if (!eOptions->cryptoKey.empty()) { - // byte swap the keylength - byteswap((unsigned char *)&options.keyLength, 2, - eOptions->cryptoKey.size()); - // copy the crypto key into the options - std::copy(eOptions->cryptoKey.begin(), eOptions->cryptoKey.end(), options.keyData); - } - // create the key descriptor - if (!eOptions->keyName.empty()) { - SSP_KAD kad; - memset(&kad, 0, sizeof(kad)); - // set the descriptor length to the length of the keyName - byteswap((unsigned char *)&kad.descriptorLength, 2, - eOptions->keyName.size()); - - // get the size of the kad object - int kadlen = eOptions->keyName.size() + SSP_KAD_HEAD_LENGTH; - // increment the SPOUT page len - pagelen += kadlen; - // increase the page size - eOptions->keyName.copy((char *)&kad.descriptor, eOptions->keyName.size(), - 0); - // copy the kad after the SDE command - memcpy(&buffer[sizeof(SSP_PAGE_SDE)], &kad, kadlen); - } - // update the pagelen in options - byteswap((unsigned char *)&options.length, 2, - pagelen - 4); // set the page length, minus the length and pageCode - - // copy the options to the beginning of the buffer - memcpy(buffer, &options, sizeof(SSP_PAGE_SDE)); - return pagelen; +inquiry_data get_inquiry(const std::string& device) +{ + const uint8_t scsi_inq_command[] {0x12, 0, 0, 0, sizeof(inquiry_data), 0}; + inquiry_data inq; + scsi_execute(device, scsi_inq_command, sizeof(scsi_inq_command), + reinterpret_cast(&inq), sizeof(inq), + scsi_direction::from_device); + return inq; } -// Writes encryption options to the tape drive -bool SCSIWriteEncryptOptions(const std::string& tapeDevice, - SCSIEncryptOptions *eOptions) { - uint8_t buffer[1024] {}; - int pagelen = SCSIInitSDEPage(eOptions, buffer); +std::unique_ptr make_sde(encrypt_mode enc_mode, + decrypt_mode dec_mode, + std::uint8_t algorithm_index, + const std::vector key, + const std::string& key_name, + sde_rdmc rdmc, bool ckod) +{ + std::size_t length {sizeof(page_sde) + key.size()}; + if (!key_name.empty()) { + length += sizeof(kad) + key_name.size(); + } + auto buffer {std::make_unique(length)}; + auto& page {reinterpret_cast(*buffer.get())}; + page.page_code = htons(0x10); + page.length = htons(length - sizeof(page_header)); + page.control = std::byte {2u} << page_sde::control_scope_pos; // all IT nexus = 10b + page.flags |= std::byte {DEFAULT_CEEM} << page_sde::flags_ceem_pos; + page.flags |= std::byte {rdmc}; + if (ckod) { + page.flags |= page_sde::flags_ckod_mask; + } + page.encryption_mode = enc_mode; + page.decryption_mode = dec_mode; + page.algorithm_index = algorithm_index; + page.key_length = htons(key.size()); + std::memcpy(page.key, key.data(), key.size()); + + if (!key_name.empty()) { + auto &ukad {reinterpret_cast(*(buffer.get() + sizeof(page_sde) + key.size()))}; + ukad.length = htons(key_name.size()); + std::memcpy(ukad.descriptor, key_name.data(), key_name.size()); + } + + return buffer; +} + +void write_sde(const std::string& device, const std::uint8_t *sde_buffer) +{ + auto& page {reinterpret_cast(*sde_buffer)}; + std::size_t length {sizeof(page_header) + ntohs(page.length)}; const uint8_t spout_sde_command[] { SSP_SPOUT_OPCODE, SSP_SP_PROTOCOL_TDE, @@ -229,254 +263,73 @@ bool SCSIWriteEncryptOptions(const std::string& tapeDevice, 0X10, 0, 0, - BSINTTOCHAR(pagelen), - 0, + BSINTTOCHAR(length), 0, + 0 }; - // return whether or not the command executed - return SCSIExecute(tapeDevice, (unsigned char *)&spout_sde_command, - sizeof(spout_sde_command), (unsigned char *)&buffer, - pagelen, true, true); + scsi_execute(device, spout_sde_command, sizeof(spout_sde_command), + sde_buffer, length, scsi_direction::to_device); } -bool SCSIExecute(const std::string& tapedrive, unsigned char *cmd_p, int cmd_len, - unsigned char *dxfer_p, int dxfer_len, bool cmd_to_device, - bool show_error) { - const char *tapedevice = tapedrive.c_str(); - int sg_fd, eresult, sresult, ioerr, retries; - SCSI_PAGE_SENSE *sd = new SCSI_PAGE_SENSE; - memset(sd, 0, sizeof(SCSI_PAGE_SENSE)); +void print_sense_data(std::ostream& os, const sense_data& sd) { + os << std::left << std::setw(25) << "Sense Code: "; -#if defined(OS_LINUX) - sg_fd = open(tapedevice, O_RDONLY); - if (sg_fd == -1) { - std::cerr << "Could not open device '" << tapedevice << "': " - << strerror(errno) << "\n"; - exit(EXIT_FAILURE); + auto sense_key {static_cast(sd.flags & sense_data::flags_sense_key_mask)}; + + switch (sense_key) { + case 0u: + os << "No specific error"; + break; + case 2u: + os << "Device not ready"; + break; + case 3u: + os << "Medium Error"; + break; + case 4u: + os << "Hardware Error"; + break; + case 5u: + os << "Illegal Request"; + break; + case 6u: + os << "Unit Attention"; + break; + case 7u: + os << "Data protect"; + break; + case 8u: + os << "Blank tape"; + break; } - sg_io_hdr cmdio; - memset(&cmdio, 0, sizeof(sg_io_hdr)); - cmdio.cmd_len = cmd_len; - cmdio.dxfer_direction = (cmd_to_device) ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV; - cmdio.dxfer_len = dxfer_len; - cmdio.dxferp = dxfer_p; - cmdio.cmdp = cmd_p; - cmdio.sbp = (unsigned char *)sd; - cmdio.mx_sb_len = sizeof(SCSI_PAGE_SENSE); - cmdio.timeout = SCSI_TIMEOUT; - cmdio.interface_id = 'S'; - retries = 0; - do { - errno = 0; - eresult = ioctl(sg_fd, SG_IO, &cmdio); - if (eresult != 0) - ioerr = errno; - retries++; - } while (errno != 0 && retries <= RETRYCOUNT); + os << " (0x" << HEX(sense_key) << ")\n"; - sresult = cmdio.status; - close(sg_fd); -#elif defined(OS_FREEBSD) - auto dev = cam_open_device(tapedevice, O_RDWR); - auto ccb = dev ? cam_getccb(dev) : nullptr; + os << std::left << std::setw(25) << " ASC:" + << "0x" << HEX(sd.additional_sense_code) << "\n"; - if (dev == nullptr || ccb == nullptr) { - std::cerr << "Could not open device '" << tapedevice << "': " << cam_errbuf << "\n"; - exit(EXIT_FAILURE); - } - CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio); + os << std::left << std::setw(25) << " ASCQ:" + << "0x" << HEX(sd.additional_sense_qualifier) << "\n"; - cam_fill_csio(&ccb->csio, RETRYCOUNT, nullptr, - CAM_PASS_ERR_RECOVER | CAM_CDB_POINTER | - (cmd_to_device ? CAM_DIR_OUT : CAM_DIR_IN), - MSG_SIMPLE_Q_TAG, dxfer_p, dxfer_len, SSD_FULL_SIZE, cmd_len, - SCSI_TIMEOUT); - ccb->csio.cdb_io.cdb_ptr = cmd_p; - eresult = cam_send_ccb(dev, ccb); - if (eresult != 0) { - ioerr = errno; - } - sresult = ccb->csio.scsi_status; - memcpy(sd, &ccb->csio.sense_data, sizeof(SCSI_PAGE_SENSE)); + if (sd.additional_sense_length > 0) { + os << std::left << std::setw(25) << " Additional data: " << "0x"; - cam_freeccb(ccb); - cam_close_device(dev); -#else -#error "OS type is not set" -#endif -#ifdef DEBUGSCSI - std::cout << "SCSI Command: "; - for (int i = 0; i < cmd_len; i++) { - std::cout << HEX(cmd_p[i]); - } - std::cout << "\n"; - - std::cout << "SCSI Data: "; - for (int i = 0; i < dxfer_len; i++) { - std::cout << HEX(dxfer_p[i]); - } - std::cout << std::endl; -#endif - - bool retval = true; - - if (eresult != 0) { - if (show_error) { - std::cerr << "ERROR: " << strerror(ioerr) << "\n"; + for (int i = 0; i < sd.additional_sense_length; i++) { + os << HEX(sd.additional_sense_bytes[i]); } - retval = false; - } - - if (sresult != 0) { - if (show_error) - outputSense(sd); - retval = false; - } - delete sd; - return retval; -} - -void byteswap(unsigned char *array, int size, int value) { - switch (size) { - case 2: - array[0] = (unsigned char)((value & 0xff00) >> 8); - array[1] = (unsigned char)(value & 0x00ff); - break; - case 4: - array[0] = (unsigned char)((value & 0xff000000) >> 24); - array[1] = (unsigned char)((value & 0x00ff0000) >> 16); - array[2] = (unsigned char)((value & 0x0000ff00) >> 8); - array[3] = (unsigned char)(value & 0x000000ff); - - break; - default: - std::cout << "Unhandled byte swap length of " << size << std::endl; - break; - } -} - -SCSIEncryptOptions::SCSIEncryptOptions() { - cryptMode = CRYPTMODE_OFF; - algorithmIndex = DEFAULT_ALGORITHM; - cryptoKey = {}; - CKOD = false; - keyName = ""; - rdmc = RDMC_DEFAULT; -} - -SSP_NBES::SSP_NBES(const SSP_PAGE_BUFFER *buffer) { - memset(&nbes, 0, sizeof(SSP_PAGE_NBES)); - memcpy(&nbes, buffer, sizeof(SSP_PAGE_NBES)); - loadKADs(buffer, sizeof(SSP_PAGE_NBES)); -} - -SSP_DES::SSP_DES(const SSP_PAGE_BUFFER *buffer) { - memset(&des, 0, sizeof(SSP_PAGE_DES)); - memcpy(&des, buffer, sizeof(SSP_PAGE_DES)); - loadKADs(buffer, sizeof(SSP_PAGE_DES)); -} - -void KAD_CLASS::loadKADs(const SSP_PAGE_BUFFER *buffer, int start) { - const char *rawbuff = (const char *)buffer; - int length = BSSHORT(buffer->length) + 4; - int pos = start; - while (pos < length) { - SSP_KAD kad; - memset(&kad, 0, sizeof(SSP_KAD)); - memcpy(&kad, rawbuff + pos, SSP_KAD_HEAD_LENGTH); - pos += SSP_KAD_HEAD_LENGTH; - if (pos >= length) - break; - unsigned short kadDesLen = BSSHORT(kad.descriptorLength); - if (kadDesLen > 0) { - memcpy(&kad.descriptor, rawbuff + pos, kadDesLen); - pos += kadDesLen; - } else - pos++; - kads.push_back(kad); - } -} - -bool moveTape(const std::string& tapeDevice, int count, bool dirForward) { - struct mtop mt_command; - int sg_fd = open(tapeDevice.c_str(), O_RDONLY); - if (!sg_fd || sg_fd == -1) { - return false; - } - errno = 0; - bool retval = true; -#if defined(OS_LINUX) || defined(OS_FREEBSD) // Linux or FreeBSD System - - mt_command.mt_op = (dirForward) ? MTFSR : MTBSR; - mt_command.mt_count = count; - ioctl(sg_fd, MTIOCTOP, &mt_command); -#else -#error "OS type is not set" -#endif - if (errno != 0) - retval = false; - - close(sg_fd); - errno = 0; - return retval; -} - -void outputSense(SCSI_PAGE_SENSE *sd) { - std::cerr << std::left << std::setw(25) << "Sense Code: "; - - switch ((int)sd->senseKey) { - case 0: - std::cerr << "No specific error"; - break; - case 2: - std::cerr << "Device not ready"; - break; - case 3: - std::cerr << "Medium Error"; - break; - case 4: - std::cerr << "Hardware Error"; - break; - case 5: - std::cerr << "Illegal Request"; - break; - case 6: - std::cerr << "Unit Attention"; - break; - case 7: - std::cerr << "Data protect"; - break; - case 8: - std::cerr << "Blank tape"; - break; - } - - std::cerr << " (0x" << HEX(sd->senseKey) << ")\n"; - - std::cerr << std::left << std::setw(25) << " ASC:" - << "0x" << HEX(sd->addSenseCode) << "\n"; - - std::cerr << std::left << std::setw(25) << " ASCQ:" - << "0x" << HEX(sd->addSenseCodeQual) << "\n"; - - if (sd->addSenseLen > 0) { - std::cerr << std::left << std::setw(25) << " Additional data: 0x"; - - for (int i = 0; i < sd->addSenseLen; i++) { - std::cerr << HEX(sd->addSenseData[i]); - } - std::cerr << "\n"; + os << "\n"; } #ifdef DEBUGSCSI - std::cerr << std::left << std::setw(25) << " Raw Sense:" - << "0x"; - char *rawsense = (char *)sd; + os << std::left << std::setw(25) << " Raw Sense:" + << "0x"; + char *rawsense = (char *)&sd; - for (int i = 0; i < sizeof(SCSI_PAGE_SENSE); i++) { - std::cerr << HEX(rawsense[i]); + for (int i = 0; i < sense_data::maximum_size; i++) { + os << HEX(rawsense[i]); } - std::cerr << "\n"; + os << "\n"; #endif } + +} diff --git a/src/scsiencrypt.h b/src/scsiencrypt.h index ad26b60..337908e 100644 --- a/src/scsiencrypt.h +++ b/src/scsiencrypt.h @@ -16,10 +16,16 @@ GNU General Public License for more details. #ifndef _SCSIENC_H #define _SCSIENC_H +#include #include +#include +#include +#include #include #include +#include + #ifdef HAVE_SYS_MACHINE_H #include #endif @@ -46,364 +52,301 @@ constexpr uint8_t RDMC_DEFAULT = 0x00; // outputs hex in a 2 digit pair #define HEX(x) \ std::right << std::setw(2) << std::setfill('0') << std::hex << (int)(x) << std::setfill(' ') -// macro for a byte swapped short -constexpr uint16_t BSSHORT(const uint8_t *p) + +namespace scsi { + +enum class encrypt_mode: std::uint8_t { + off = 0u, + external = 1u, + on = 2u, +}; + +enum class decrypt_mode: std::uint8_t { + off = 0u, + raw = 1u, + on = 2u, + mixed = 3u, +}; + +// key-associated data +struct __attribute__((packed)) kad { + std::uint8_t type; + std::byte flags; + static constexpr auto flags_authenticated_pos {0u}; + static constexpr std::byte flags_authenticated_mask {7u << flags_authenticated_pos}; + std::uint16_t length; + std::uint8_t descriptor[]; +}; +static_assert(sizeof(kad) == 4u); + +// common 4-byte header of all SP-IN and SP-OUT pages +struct __attribute__((packed)) page_header { + std::uint16_t page_code; + std::uint16_t length; +}; +static_assert(sizeof(page_header) == 4u); + +// device encryption status page +struct __attribute__((packed)) page_des { + std::uint16_t page_code; + std::uint16_t length; + std::byte scope; + static constexpr auto scope_it_nexus_pos {5u}; + static constexpr std::byte scope_it_nexus_mask {7u << scope_it_nexus_pos}; + static constexpr auto scope_encryption_pos {0u}; + static constexpr std::byte scope_encryption_mask {7u << scope_encryption_pos}; + encrypt_mode encryption_mode; + decrypt_mode decryption_mode; + std::uint8_t algorithm_index; + std::uint32_t key_instance_counter; + std::byte flags; + static constexpr auto flags_parameters_control_pos {4u}; + static constexpr std::byte flags_parameters_control_mask {7u << flags_parameters_control_pos}; + static constexpr auto flags_vcelb_pos {3u}; // volume contains encrypted logical blocks + static constexpr std::byte flags_vcelb_mask {1u << flags_vcelb_pos}; + static constexpr auto flags_ceems_pos {1u}; // check external encryption mode status + static constexpr std::byte flags_ceems_mask {3u << flags_ceems_pos}; + static constexpr auto flags_rdmd_pos {0u}; // raw decryption mode disabled + static constexpr std::byte flags_rdmd_mask {1u << flags_rdmd_pos}; + std::uint8_t kad_format; + std::uint16_t asdk_count; + std::byte reserved[8]; + kad kads[]; +}; +static_assert(sizeof(page_des) == 24u); + +using page_buffer = std::uint8_t[SSP_PAGE_ALLOCATION]; + +// set data encryption page +struct __attribute__((packed)) page_sde { + std::uint16_t page_code; + std::uint16_t length; + std::byte control; + static constexpr auto control_scope_pos {5u}; + static constexpr std::byte control_scope_mask {7u << control_scope_pos}; + static constexpr auto control_lock_pos {0u}; + static constexpr std::byte control_lock_mask {1u << control_lock_pos}; + std::byte flags; + static constexpr auto flags_ceem_pos {6u}; // check external encryption mode + static constexpr std::byte flags_ceem_mask {3u << flags_ceem_pos}; + static constexpr auto flags_rdmc_pos {4u}; // raw decryption mode control + static constexpr std::byte flags_rdmc_mask {3u << flags_rdmc_pos}; + static constexpr auto flags_sdk_pos {3u}; // supplemental decryption key + static constexpr std::byte flags_sdk_mask {1u << flags_sdk_pos}; + static constexpr auto flags_ckod_pos {2u}; // clear key on demount + static constexpr std::byte flags_ckod_mask {1u << flags_ckod_pos}; + static constexpr auto flags_ckorp_pos {1u}; // clear key on reservation preempt + static constexpr std::byte flags_ckorp_mask {1u << flags_ckorp_pos}; + static constexpr auto flags_ckorl_pos {0u}; // clear key on reservation loss + static constexpr std::byte flags_ckorl_mask {1u << flags_ckorl_pos}; + encrypt_mode encryption_mode; + decrypt_mode decryption_mode; + std::uint8_t algorithm_index; + std::uint8_t key_format; + std::uint8_t kad_format; + std::byte reserved[7]; + std::uint16_t key_length; + std::uint8_t key[]; +}; +static_assert(sizeof(page_sde) == 20u); + +enum class sde_rdmc: std::uint8_t { + algorithm_default = 0u << page_sde::flags_rdmc_pos, + enabled = 2u << page_sde::flags_rdmc_pos, + disabled = 3u << page_sde::flags_rdmc_pos, +}; + +// next block encryption status page +struct __attribute__((packed)) page_nbes { + std::uint16_t page_code; + std::uint16_t length; + std::uint64_t logical_object_number; + std::byte status; + static constexpr auto status_compression_pos {4u}; + static constexpr std::byte status_compression_mask {15u << status_compression_pos}; + static constexpr auto status_encryption_pos {0u}; + static constexpr std::byte status_encryption_mask {15u << status_encryption_pos}; + std::uint8_t algorithm_index; + std::byte flags; + static constexpr auto flags_emes_pos {1u}; // encryption mode external status + static constexpr std::byte flags_emes_mask {1u << flags_emes_pos}; + static constexpr auto flags_rdmds_pos {0u}; // raw decryption mode disabled status + static constexpr std::byte flags_rdmds_mask {1u << flags_rdmds_pos}; + std::uint8_t kad_format; + kad kads[]; +}; +static_assert(sizeof(page_nbes) == 16u); + +struct __attribute__((packed)) algorithm_descriptor { + std::uint8_t algorithm_index; + std::byte reserved1; + std::uint16_t length; + std::byte flags1; + static constexpr auto flags1_avfmv_pos {7u}; // algorithm valid for mounted volume + static constexpr std::byte flags1_avfmv_mask {1u << flags1_avfmv_pos}; + static constexpr auto flags1_sdk_c_pos {6u}; // supplemental decryption key capable + static constexpr std::byte flags1_sdk_c_mask {1u << flags1_sdk_c_pos}; + static constexpr auto flags1_mac_c_pos {5u}; // message authentication code capable + static constexpr std::byte flags1_mac_c_mask {1u << flags1_mac_c_pos}; + static constexpr auto flags1_delb_c_pos {4u}; // distinguish encrypted logical block capable + static constexpr std::byte flags1_delb_c_mask {1u << flags1_delb_c_pos}; + static constexpr auto flags1_decrypt_c_pos {2u}; // decryption capabilities + static constexpr std::byte flags1_decrypt_c_mask {3u << flags1_decrypt_c_pos}; + static constexpr auto flags1_encrypt_c_pos {0u}; // encryption capabilities + static constexpr std::byte flags1_encrypt_c_mask {3u << flags1_encrypt_c_pos}; + std::byte flags2; + static constexpr auto flags2_avfcp_pos {6u}; // algorithm valid for current logical position + static constexpr std::byte flags2_avfcp_mask {3u << flags2_avfcp_pos}; + static constexpr auto flags2_nonce_pos {4u}; // nonce capabilities + static constexpr std::byte flags2_nonce_mask {3u << flags2_nonce_pos}; + static constexpr auto flags2_kadf_c_pos {3u}; // KAD format capable + static constexpr std::byte flags2_kadf_c_mask {1u << flags2_kadf_c_pos}; + static constexpr auto flags2_vcelb_c_pos {2u}; // volume contains encrypted logical blocks capable + static constexpr std::byte flags2_vcelb_c_mask {1u << flags2_vcelb_c_pos}; + static constexpr auto flags2_ukadf_pos {1u}; // U-KAD fixed + static constexpr std::byte flags2_ukadf_mask {1u << flags2_ukadf_pos}; + static constexpr auto flags2_akadf_pos {0u}; // A-KAD fixed + static constexpr std::byte flags2_akadf_mask {1u << flags2_akadf_pos}; + std::uint16_t maximum_ukad_length; + std::uint16_t maximum_akad_length; + std::uint16_t key_length; + std::byte flags3; + static constexpr auto flags3_dkad_c_pos {6u}; // decryption capabilities + static constexpr std::byte flags3_dkad_c_mask {3u << flags3_dkad_c_pos}; + static constexpr auto flags3_eemc_c_pos {4u}; // external encryption mode control capabilities + static constexpr std::byte flags3_eemc_c_mask {3u << flags3_eemc_c_pos}; + static constexpr auto flags3_rdmc_c_pos {1u}; // raw decryption mode control capabilities + static constexpr std::byte flags3_rdmc_c_mask {7u << flags3_rdmc_c_pos}; + static constexpr auto flags3_earem_pos {0u}; // encryption algorithm records encryption mode + static constexpr std::byte flags3_earem_mask {1u << flags3_earem_pos}; + std::uint8_t maximum_eedk_count; + static constexpr auto maximum_eedk_count_pos {0u}; + static constexpr std::uint8_t maximum_eedk_count_mask {15u << maximum_eedk_count_pos}; + std::uint16_t msdk_count; + std::uint16_t maximum_eedk_size; + std::byte reserved2[2]; + std::uint32_t security_algorithm_code; +}; +static_assert(sizeof(algorithm_descriptor) == 24u); + +// device encryption capabilities page +struct __attribute__((packed)) page_dec { + std::uint16_t page_code; + std::uint16_t length; + std::byte flags; + static constexpr auto flags_extdecc_pos {2u}; // external data encryption control capable + static constexpr std::byte flags_extdecc_mask {3u << flags_extdecc_pos}; + static constexpr auto flags_cfg_p_pos {0u}; // configuration prevented + static constexpr std::byte flags_cfg_p_mask {3u << flags_cfg_p_pos}; + std::byte reserved[15]; + algorithm_descriptor ads[]; +}; +static_assert(sizeof(page_dec) == 20u); + +struct __attribute__((packed)) inquiry_data { + // bitfield definitions omitted since stenc only uses vendor and product info + std::byte peripheral; + std::byte flags1; + std::uint8_t version; + std::byte flags2; + std::uint8_t additional_length; + std::byte flags3; + std::byte flags4; + std::byte flags5; + char vendor[8]; + char product_id[16]; + char product_rev[4]; + std::uint8_t vendor_specific[20]; + std::byte reserved1[2]; + std::uint16_t version_descriptor[8]; + std::byte reserved2[22]; +}; +static_assert(sizeof(inquiry_data) == 96u); + +struct __attribute__((packed)) sense_data { + std::byte response; + static constexpr auto response_valid_pos {7u}; + static constexpr std::byte response_valid_mask {1u << response_valid_pos}; + static constexpr auto response_code_pos {0u}; + static constexpr std::byte response_code_mask {127u << response_code_pos}; + std::byte reserved; + std::byte flags; + static constexpr auto flags_filemark_pos {7u}; + static constexpr std::byte flags_filemark_mask {1u << flags_filemark_pos}; + static constexpr auto flags_eom_pos {6u}; // end of medium + static constexpr std::byte flags_eom_mask {1u << flags_eom_pos}; + static constexpr auto flags_ili_pos {5u}; // incorrect length indicator + static constexpr std::byte flags_ili_mask {1u << flags_ili_pos}; + static constexpr auto flags_sdat_ovfl_pos {4u}; // sense data overflow + static constexpr std::byte flags_sdat_ovfl_mask {1u << flags_sdat_ovfl_pos}; + static constexpr auto flags_sense_key_pos {0u}; + static constexpr std::byte flags_sense_key_mask {15u << flags_sense_key_pos}; + std::uint8_t information[4]; + std::uint8_t additional_sense_length; + std::uint8_t command_specific_information[4]; + std::uint8_t additional_sense_code; + std::uint8_t additional_sense_qualifier; + std::uint8_t field_replaceable_unit_code; + std::uint8_t sense_key_specific[3]; + std::uint8_t additional_sense_bytes[]; + static constexpr auto maximum_size {252u}; // per SPC-5 +}; +static_assert(sizeof(sense_data) == 18u); + +// declared as std::array instead of std::uint8_t[] because +// std::unique_ptr does not allow construction of fixed-sized arrays +using sense_buffer = std::array; + +class scsi_error: public std::runtime_error { + public: + explicit scsi_error(std::unique_ptr&& buf) : + sense_buf {std::move(buf)}, std::runtime_error {""} {} + const sense_data& get_sense() const { return reinterpret_cast(*sense_buf->data()); } + + private: + std::unique_ptr sense_buf; +}; + +// Extract pointers to kad structures within a variable-length page. +// Page must have a page_header layout +template +std::vector read_page_kads(const Page& page) { - return static_cast(p[0]) << 8 | p[1]; -} -// macro for a byte swapped int -constexpr uint32_t BSLONG(const uint8_t *p) -{ - return static_cast(p[0]) << 24 | - static_cast(p[1]) << 16 | - static_cast(p[2]) << 8 | - static_cast(p[3]); + const auto start {reinterpret_cast(&page)}; + auto it {start + sizeof(Page)}; + const auto end {start + ntohs(page.length) + sizeof(page_header)}; + std::vector v {}; + + while (it < end) { + auto elem {reinterpret_cast(it)}; + v.push_back(elem); + it += ntohs(elem->length) + sizeof(kad); + } + return v; +} + +inquiry_data get_inquiry(const std::string& device); +// Get data encryption status page +void get_des(const std::string& device, const std::uint8_t *buffer, + std::size_t length); +// Get next block encryption status page +void get_nbes(const std::string& device, const std::uint8_t *buffer, + std::size_t length); +// Get device encryption capabilities +void get_dec(const std::string& device, const std::uint8_t *buffer, + std::size_t length); +// Fill out a set data encryption page with parameters. +// Result is allocated and returned as a std::unique_ptr and should +// be sent to the device using scsi::write_sde +std::unique_ptr make_sde(encrypt_mode enc_mode, + decrypt_mode dec_mode, + std::uint8_t algorithm_index, + const std::vector key, + const std::string& key_name, + sde_rdmc rdmc, bool ckod); +// Write set data encryption parameters to device +void write_sde(const std::string& device, const std::uint8_t *sde_buffer); +void print_sense_data(std::ostream& os, const sense_data& sd); } -#ifdef BYTE_ORDER -#define STENC_BYTE_ORDER BYTE_ORDER -#endif -#ifndef STENC_BYTE_ORDER -#ifdef __BYTE_ORDER -#define STENC_BYTE_ORDER __BYTE_ORDER -#endif -#endif - -#ifdef BIG_ENDIAN -#define STENC_TYPE_BIG_ENDIAN BIG_ENDIAN -#endif -#ifndef STENC_TYPE_BIG_ENDIAN -#ifdef __BIG_ENDIAN -#define STENC_TYPE_BIG_ENDIAN __BIG_ENDIAN -#endif -#endif - -#if STENC_BYTE_ORDER == STENC_TYPE_BIG_ENDIAN -#define STENC_BIG_ENDIAN 1 -#else -#define STENC_BIG_ENDIAN 0 -#endif - -struct SSP_PAGE_DES { - unsigned char pageCode[2]; - unsigned char length[2]; - -#if STENC_BIG_ENDIAN == 1 - unsigned char nexusScope : 3; - unsigned char res_bits_1 : 2; - unsigned char keyScope : 3; -#else - unsigned char keyScope : 3; - unsigned char res_bits_1 : 2; - unsigned char nexusScope : 3; -#endif - unsigned char encryptionMode; - unsigned char decryptionMode; - unsigned char algorithmIndex; - unsigned char keyInstance[4]; -#if STENC_BIG_ENDIAN == 1 - unsigned char res_bits_2 : 1; - unsigned char parametersControl : 3; - unsigned char VCELB : 1; - unsigned char CEEMS : 2; - unsigned char RDMD : 1; -#else - - unsigned char RDMD : 1; - unsigned char CEEMS : 2; - unsigned char VCELB : 1; - unsigned char parametersControl : 3; - unsigned char res_bits_2 : 1; -#endif - unsigned char res_bits_3; - unsigned char ASDKCount[2]; - unsigned char res_bits_4[8]; - -}; // device encryption status page - -struct SSP_KAD{ - unsigned char type; -#if STENC_BIG_ENDIAN == 1 - unsigned char res_bits_1 : 5; - unsigned char authenticated : 3; -#else - unsigned char authenticated : 3; - unsigned char res_bits_1 : 5; -#endif - unsigned char descriptorLength[2]; - unsigned char descriptor[SSP_DESCRIPTOR_LENGTH]; // will actually be the size - // of descriptorLength -}; - -struct SSP_PAGE_BUFFER { - unsigned char pageCode[2]; - unsigned char length[2]; - unsigned char buffer[SSP_PAGE_ALLOCATION]; -}; // generic ssp page buffer - -struct SSP_PAGE_SDE { // structure for setting data encryption - unsigned char pageCode[2]; - unsigned char length[2]; - -#if STENC_BIG_ENDIAN == 1 - unsigned char scope : 3; - unsigned char res_bits_1 : 4; - unsigned char lock : 1; -#else - unsigned char lock : 1; - unsigned char res_bits_1 : 4; - unsigned char scope : 3; -#endif - -#if STENC_BIG_ENDIAN == 1 - unsigned char CEEM : 2; - unsigned char RDMC : 2; - unsigned char sdk : 1; - unsigned char ckod : 1; - unsigned char ckorp : 1; - unsigned char ckorl : 1; -#else - unsigned char ckorl : 1; - unsigned char ckorp : 1; - unsigned char ckod : 1; - unsigned char sdk : 1; - unsigned char RDMC : 2; - unsigned char CEEM : 2; -#endif - unsigned char encryptionMode; - unsigned char decryptionMode; - unsigned char algorithmIndex; - unsigned char keyFormat; - unsigned char res_bits_2[8]; - unsigned char keyLength[2]; - unsigned char keyData[SSP_KEY_LENGTH]; -}; - -struct SSP_PAGE_NBES { - unsigned char pageCode[2]; - unsigned char length[2]; - unsigned char log_obj_num[8]; -#if STENC_BIG_ENDIAN == 1 - unsigned char compressionStatus : 4; - unsigned char encryptionStatus : 4; -#else - unsigned char encryptionStatus : 4; - unsigned char compressionStatus : 4; -#endif - - unsigned char algorithmIndex; -#if STENC_BIG_ENDIAN == 1 - unsigned char res_bits_1 : 6; - unsigned char EMES : 1; - unsigned char RDMDS : 1; -#else - unsigned char RDMDS : 1; - unsigned char EMES : 1; - unsigned char res_bits_1 : 6; -#endif - - unsigned char res_bits_2; -}; // next block encryption status page - -struct SCSI_PAGE_INQ { - -#if STENC_BIG_ENDIAN == 0 - unsigned char peripheralQualifier : 3; - unsigned char periphrealDeviceType : 5; -#else - unsigned char periphrealDeviceType : 5; - unsigned char peripheralQualifier : 3; -#endif - -#if STENC_BIG_ENDIAN == 0 - unsigned char RMB : 1; - unsigned char res_bits_1 : 7; -#else - unsigned char res_bits_1 : 7; - unsigned char RMB : 1; -#endif - unsigned char Version[1]; - -#if STENC_BIG_ENDIAN == 0 - unsigned char obs_bits_1 : 2; - unsigned char NORMACA : 1; - unsigned char HISUP : 1; - unsigned char responseDataFormat : 4; -#else - unsigned char responseDataFormat : 4; - unsigned char HISUP : 1; - unsigned char NORMACA : 1; - unsigned char obs_bits_1 : 2; -#endif - - unsigned char additionalLength[1]; - -#if STENC_BIG_ENDIAN == 0 - unsigned char SCCS : 1; - unsigned char ACC : 1; - unsigned char TPGS : 2; - unsigned char threePC : 1; - unsigned char res_bits_2 : 2; - unsigned char protect : 1; -#else - unsigned char protect : 1; - unsigned char res_bits_2 : 2; - unsigned char threePC : 1; - unsigned char TPGS : 2; - unsigned char ACC : 1; - unsigned char SCCS : 1; -#endif - -#if STENC_BIG_ENDIAN == 0 - unsigned char obs_bits_2 : 1; - unsigned char ENCSERV : 1; - unsigned char VS : 1; - unsigned char MULTIP : 1; - unsigned char MCHNGR : 1; - unsigned char obs_bits_3 : 2; - unsigned char ADDR16 : 1; -#else - unsigned char ADDR16 : 1; - unsigned char obs_bits_3 : 2; - unsigned char MCHNGR : 1; - unsigned char MULTIP : 1; - unsigned char VS : 1; - unsigned char ENCSERV : 1; - unsigned char obs_bits_2 : 1; -#endif - -#if STENC_BIG_ENDIAN == 0 - unsigned char obs_bits_4 : 2; - unsigned char WBUS16 : 1; - unsigned char SYNC : 1; - unsigned char obs_bits_5 : 2; - unsigned char CMDQUE : 1; - unsigned char VS2 : 1; -#else - unsigned char VS2 : 1; - unsigned char CMDQUE : 1; - unsigned char obs_bits_5 : 2; - unsigned char SYNC : 1; - unsigned char WBUS16 : 1; - unsigned char obs_bits_4 : 2; -#endif - - unsigned char vender[8]; - unsigned char productID[16]; - unsigned char productRev[4]; - unsigned char SN[7]; - unsigned char venderUnique[12]; - -#if STENC_BIG_ENDIAN == 0 - unsigned char res_bits_3 : 4; - unsigned char CLOCKING : 2; - unsigned char QAS : 1; - unsigned char IUS : 1; -#else - unsigned char IUS : 1; - unsigned char QAS : 1; - unsigned char CLOCKING : 2; - unsigned char res_bits_3 : 4; -#endif - - unsigned char res_bits_4[1]; - unsigned char versionDescriptor[16]; - unsigned char res_bits_5[22]; - unsigned char copyright[1]; -}; // device inquiry response - -struct SCSI_PAGE_SENSE { -#if STENC_BIG_ENDIAN == 1 - unsigned char valid : 1; - unsigned char responseCode : 7; -#else - unsigned char responseCode : 7; - unsigned char valid : 1; -#endif - unsigned char res_bits_1; - -#if STENC_BIG_ENDIAN == 1 - unsigned char filemark : 1; - unsigned char EOM : 1; - unsigned char ILI : 1; - unsigned char res_bits_2 : 1; - unsigned char senseKey : 4; -#else - unsigned char senseKey : 4; - unsigned char res_bits_2 : 1; - unsigned char ILI : 1; - unsigned char EOM : 1; - unsigned char filemark : 1; -#endif - unsigned char information[4]; - unsigned char addSenseLen; - unsigned char cmdSpecificInfo[4]; - unsigned char addSenseCode; - unsigned char addSenseCodeQual; - unsigned char fieldRepUnitCode; -#if STENC_BIG_ENDIAN == 1 - unsigned char sim : 3; // system information message - unsigned char bpv : 1; // bit pointer valid - unsigned char resvd2 : 2; // reserved - unsigned char cd : 1; // control/data - unsigned char SKSV : 1; - -#else - unsigned char SKSV : 1; - unsigned char cd : 1; // control/data - unsigned char resvd2 : 2; // reserved - unsigned char bpv : 1; // bit pointer valid - unsigned char sim : 3; // system information message -#endif - unsigned char field[2]; // field pointer - unsigned char addSenseData[109]; -}; // sense data response - -class KAD_CLASS { -public: - std::vector kads; - -protected: - void loadKADs(const SSP_PAGE_BUFFER *buffer, int start); -}; - -// class used to parse next block encryption status page -class SSP_NBES : public KAD_CLASS { -public: - SSP_PAGE_NBES nbes; - SSP_NBES(const SSP_PAGE_BUFFER *buffer); -}; -// class used to parse data encryption status page -class SSP_DES : public KAD_CLASS { -public: - SSP_PAGE_DES des; - SSP_DES(const SSP_PAGE_BUFFER *buffer); -}; - -// enum for SCSIEncryptOptions.cryptMode -enum { CRYPTMODE_OFF, CRYPTMODE_MIXED, CRYPTMODE_ON, CRYPTMODE_RAWREAD }; - -// used to pass parameters to SCSIWriteEncryptOptions -class SCSIEncryptOptions { -public: - int rdmc; - bool CKOD; - int cryptMode; - unsigned int algorithmIndex; - std::vector cryptoKey; - std::string keyName; - SCSIEncryptOptions(); -}; - -// Gets encryption options on the tape drive -SSP_DES *SSPGetDES(const std::string& tapeDevice); -// Gets the encryption status from the tape volume -SSP_NBES *SSPGetNBES(const std::string& tapeDevice, bool retry); -// Writes encryption options to the tape drive -int SCSIInitSDEPage(SCSIEncryptOptions *eOptions, - uint8_t *buffer); -bool SCSIWriteEncryptOptions(const std::string& tapeDevice, - SCSIEncryptOptions *eOptions); -// Gets device inquiry -SCSI_PAGE_INQ *SCSIGetInquiry(const std::string& tapeDevice); #endif diff --git a/tests/output.cpp b/tests/output.cpp index 549535e..dbe3865 100644 --- a/tests/output.cpp +++ b/tests/output.cpp @@ -38,7 +38,7 @@ Vendor: ACME \n\ Product ID: Ultrium-1000 \n\ Product Revision: 1234\n"s}; std::ostringstream oss; - print_device_inquiry(oss, reinterpret_cast(response)); + print_device_inquiry(oss, reinterpret_cast(response)); REQUIRE(oss.str() == expected_output); } @@ -56,7 +56,7 @@ Drive Output: Not decrypting\n\ Drive Input: Not encrypting\n\ Key Instance Counter: 0\n"s}; std::ostringstream oss; - print_device_status(oss, std::make_unique(reinterpret_cast(page)).get(), true); + print_device_status(oss, reinterpret_cast(page), true); REQUIRE(oss.str() == expected_output); } @@ -78,7 +78,7 @@ Key Instance Counter: 1\n\ Encryption Algorithm: 1\n\ Drive Key Desc.(uKAD): Hello world!\n"s}; std::ostringstream oss; - print_device_status(oss, std::make_unique(reinterpret_cast(page)).get(), true); + print_device_status(oss, reinterpret_cast(page), true); REQUIRE(oss.str() == expected_output); } @@ -91,7 +91,7 @@ TEST_CASE("Test SCSI get next block encryption status output 1", "[output]") const std::string expected_output {"\ Volume Encryption: Not encrypted\n"s}; std::ostringstream oss; - print_volume_status(oss, std::make_unique(reinterpret_cast(page)).get()); + print_volume_status(oss, reinterpret_cast(page)); REQUIRE(oss.str() == expected_output); } @@ -107,6 +107,6 @@ TEST_CASE("Test SCSI get next block encryption status output 2", "[output]") Volume Encryption: Encrypted and able to decrypt\n\ Volume Algorithm: 1\n"s}; std::ostringstream oss; - print_volume_status(oss, std::make_unique(reinterpret_cast(page)).get()); + print_volume_status(oss, reinterpret_cast(page)); REQUIRE(oss.str() == expected_output); } diff --git a/tests/scsi.cpp b/tests/scsi.cpp index 520daf5..62e0b3f 100644 --- a/tests/scsi.cpp +++ b/tests/scsi.cpp @@ -1,8 +1,10 @@ #define CATCH_CONFIG_MAIN #include "catch.hpp" + #include "config.h" #include "scsiencrypt.h" +#include using namespace std::literals::string_literals; @@ -14,11 +16,10 @@ using namespace std::literals::string_literals; * reflect available input and program options. */ TEST_CASE("Disable encryption command", "[scsi]") { - SCSIEncryptOptions opt; uint8_t buffer[1024] {}; const uint8_t expected[] { 0x00, 0x10, // page code - 0x00, 0x30, // page length + 0x00, 0x10, // page length 0x40, // scope DEFAULT_CEEM << 6, // CEEM, CKOD, RDMC, et al. 0x00, // encyption mode @@ -26,23 +27,21 @@ TEST_CASE("Disable encryption command", "[scsi]") { 0x01, // algorithm index 0x00, // key format 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved [8] - 0x00, 0x20, // key length - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00 // key length }; - opt.cryptMode = CRYPTMODE_OFF; - opt.algorithmIndex = 1; - int pagelen = SCSIInitSDEPage(&opt, buffer); - REQUIRE(pagelen == sizeof(expected)); - REQUIRE(memcmp(buffer, expected, sizeof(expected)) == 0); + std::vector key {}; + std::string key_name {}; + + auto page_buffer {scsi::make_sde(scsi::encrypt_mode::off, scsi::decrypt_mode::off, + 1u, key, key_name, scsi::sde_rdmc::algorithm_default, + false)}; + auto& page {reinterpret_cast(*page_buffer.get())}; + REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected)); + REQUIRE(memcmp(&page, expected, sizeof(expected)) == 0); } TEST_CASE("Enable encryption command", "[scsi]") { - SCSIEncryptOptions opt; - uint8_t buffer[1024] {}; const uint8_t expected[] { 0x00, 0x10, // page code 0x00, 0x30, // page length @@ -60,24 +59,23 @@ TEST_CASE("Enable encryption command", "[scsi]") { 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, }; - opt.cryptMode = CRYPTMODE_ON; - opt.algorithmIndex = 1; - opt.cryptoKey = { + std::vector key { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, }; - opt.keyName = ""s; + std::string key_name {}; - int pagelen = SCSIInitSDEPage(&opt, buffer); - REQUIRE(pagelen == sizeof(expected)); - REQUIRE(memcmp(buffer, expected, sizeof(expected)) == 0); + auto page_buffer {scsi::make_sde(scsi::encrypt_mode::on, scsi::decrypt_mode::on, + 1u, key, key_name, scsi::sde_rdmc::algorithm_default, + false)}; + auto& page {reinterpret_cast(*page_buffer.get())}; + REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected)); + REQUIRE(memcmp(&page, expected, sizeof(expected)) == 0); } TEST_CASE("Enable encryption command with options", "[scsi]") { - SCSIEncryptOptions opt; - uint8_t buffer[1024] {}; const uint8_t expected[] { 0x00, 0x10, // page code 0x00, 0x30, // page length @@ -95,26 +93,23 @@ TEST_CASE("Enable encryption command with options", "[scsi]") { 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, }; - opt.rdmc = 2; - opt.CKOD = true; - opt.cryptMode = CRYPTMODE_ON; - opt.algorithmIndex = 1; - opt.cryptoKey = { + std::vector key { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, }; - opt.keyName = ""s; + std::string key_name {}; - int pagelen = SCSIInitSDEPage(&opt, buffer); - REQUIRE(pagelen == sizeof(expected)); - REQUIRE(memcmp(buffer, expected, sizeof(expected)) == 0); + auto page_buffer {scsi::make_sde(scsi::encrypt_mode::on, scsi::decrypt_mode::on, + 1u, key, key_name, scsi::sde_rdmc::enabled, + true)}; + auto& page {reinterpret_cast(*page_buffer.get())}; + REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected)); + REQUIRE(memcmp(&page, expected, sizeof(expected)) == 0); } TEST_CASE("Enable encryption command with key name", "[scsi]") { - SCSIEncryptOptions opt; - uint8_t buffer[1024] {}; const uint8_t expected[] { 0x00, 0x10, // page code 0x00, 0x40, // page length @@ -137,19 +132,21 @@ TEST_CASE("Enable encryption command with key name", "[scsi]") { 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, }; - opt.cryptMode = CRYPTMODE_ON; - opt.algorithmIndex = 1; - opt.cryptoKey = { + std::vector key { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, }; - opt.keyName = "Hello world!"s; + std::string key_name {"Hello world!"s}; - int pagelen = SCSIInitSDEPage(&opt, buffer); - REQUIRE(pagelen == sizeof(expected)); - REQUIRE(memcmp(buffer, expected, sizeof(expected)) == 0); + auto page_buffer {scsi::make_sde(scsi::encrypt_mode::on, scsi::decrypt_mode::on, + 1u, key, key_name, + scsi::sde_rdmc::algorithm_default, + false)}; + auto& page {reinterpret_cast(*page_buffer.get())}; + REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected)); + REQUIRE(memcmp(&page, expected, sizeof(expected)) == 0); } /** @@ -181,24 +178,28 @@ TEST_CASE("Interpret device encryption status page", "[scsi]") { 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, }; - SSP_DES page(reinterpret_cast(buffer)); - REQUIRE(BSSHORT(page.des.pageCode) == 0x20); - REQUIRE(BSSHORT(page.des.length) == 0x24); - REQUIRE(page.des.nexusScope == 2); - REQUIRE(page.des.keyScope == 2); - REQUIRE(page.des.encryptionMode == 2); - REQUIRE(page.des.decryptionMode == 2); - REQUIRE(page.des.algorithmIndex == 1); - REQUIRE(BSLONG(page.des.keyInstance) == 1); - REQUIRE(page.des.parametersControl == 1); - REQUIRE(page.des.VCELB == 1); - REQUIRE(page.des.CEEMS == 0); - REQUIRE(page.des.RDMD == 0); + auto& page_des {reinterpret_cast(buffer)}; + REQUIRE(ntohs(page_des.page_code) == 0x20u); + REQUIRE(ntohs(page_des.length) == 36u); + REQUIRE((page_des.scope & scsi::page_des::scope_it_nexus_mask) + >> scsi::page_des::scope_it_nexus_pos == std::byte {2u}); + REQUIRE((page_des.scope & scsi::page_des::scope_encryption_mask) + >> scsi::page_des::scope_encryption_pos == std::byte {2u}); + REQUIRE(page_des.encryption_mode == scsi::encrypt_mode::on); + REQUIRE(page_des.decryption_mode == scsi::decrypt_mode::on); + REQUIRE(page_des.algorithm_index == 1u); + REQUIRE(ntohl(page_des.key_instance_counter) == 1u); + REQUIRE((page_des.flags & scsi::page_des::flags_parameters_control_mask) + == std::byte {1u} << scsi::page_des::flags_parameters_control_pos); + REQUIRE((page_des.flags & scsi::page_des::flags_vcelb_mask) == + scsi::page_des::flags_vcelb_mask); + REQUIRE((page_des.flags & scsi::page_des::flags_ceems_mask) == std::byte {}); + REQUIRE((page_des.flags & scsi::page_des::flags_rdmd_mask) == std::byte {}); - REQUIRE(page.kads.size() == 1); - REQUIRE(page.kads[0].authenticated == 1); - REQUIRE(BSSHORT(page.kads[0].descriptorLength) == std::strlen("Hello world!")); - REQUIRE(memcmp(page.kads[0].descriptor, "Hello world!", BSSHORT(page.kads[0].descriptorLength)) == 0); + auto kads = read_page_kads(page_des); + REQUIRE(kads.size() == 1u); + REQUIRE(ntohs(kads[0]->length) == std::strlen("Hello world!")); + REQUIRE(memcmp(kads[0]->descriptor, "Hello world!", ntohs(kads[0]->length)) == 0); } TEST_CASE("Interpret next block encryption status page", "[scsi]") { @@ -217,17 +218,18 @@ TEST_CASE("Interpret next block encryption status page", "[scsi]") { 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, }; - SSP_NBES page(reinterpret_cast(buffer)); - REQUIRE(BSSHORT(page.nbes.pageCode) == 0x21); - REQUIRE(BSSHORT(page.nbes.length) == 0x1c); - REQUIRE(page.nbes.compressionStatus == 0); - REQUIRE(page.nbes.encryptionStatus == 5); - REQUIRE(page.nbes.algorithmIndex == 1); - REQUIRE(page.nbes.EMES == 0); - REQUIRE(page.nbes.RDMDS == 0); + auto& page_nbes {reinterpret_cast(buffer)}; + REQUIRE(ntohs(page_nbes.page_code) == 0x21u); + REQUIRE(ntohs(page_nbes.length) == 28u); + REQUIRE((page_nbes.status & scsi::page_nbes::status_compression_mask) == std::byte {}); + REQUIRE((page_nbes.status & scsi::page_nbes::status_encryption_mask) + == std::byte {5u} << scsi::page_nbes::status_encryption_pos); + REQUIRE(page_nbes.algorithm_index == 1u); + REQUIRE((page_nbes.flags & scsi::page_nbes::flags_emes_mask) == std::byte {}); + REQUIRE((page_nbes.flags & scsi::page_nbes::flags_rdmds_mask) == std::byte {}); - REQUIRE(page.kads.size() == 1); - REQUIRE(page.kads[0].authenticated == 1); - REQUIRE(BSSHORT(page.kads[0].descriptorLength) == std::strlen("Hello world!")); - REQUIRE(memcmp(page.kads[0].descriptor, "Hello world!", BSSHORT(page.kads[0].descriptorLength)) == 0); + auto kads = read_page_kads(page_nbes); + REQUIRE(kads.size() == 1u); + REQUIRE(ntohs(kads[0]->length) == std::strlen("Hello world!")); + REQUIRE(memcmp(kads[0]->descriptor, "Hello world!", ntohs(kads[0]->length)) == 0); }