mirror of
https://github.com/scsitape/stenc.git
synced 2026-04-22 01:20:41 +00:00
Introduce portable, endian-clean structures using shifts and masks instead of bitfields (#66)
* Introduce portable, endian-clean structures using shifts and masks instead of bitfields * Modernize SCSIExecute with RAII and exceptions * Convert SP-IN calls to use the new portable SCSI structures and functions * Convert SP-OUT code to use the new portable SCSI structures and functions * Delete bitfield-based code and remove runtime endian check Closes: https://github.com/scsitape/stenc/issues/63
This commit is contained in:
364
src/main.cpp
364
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<std::vector<uint8_t>> 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<uint8_t> 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<const scsi::page_des&>(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<unsigned int>(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<unsigned int>(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<unsigned int>(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<const char *>(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<const char *>(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<const scsi::page_des&>(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<std::uint8_t>((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<unsigned int>(compression_status) << "'\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
os << std::left << std::setw(25) << "Volume Encryption:";
|
||||
switch ((int)opt->nbes.encryptionStatus) {
|
||||
case 0x01:
|
||||
auto encryption_status {
|
||||
static_cast<std::uint8_t>((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<const char *>(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<const char *>(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<unsigned int>(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<unsigned int>(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<const scsi::page_nbes&>(buffer)};
|
||||
|
||||
print_volume_status(std::cout, opt);
|
||||
delete opt;
|
||||
}
|
||||
|
||||
void echo(bool on) {
|
||||
|
||||
@@ -14,8 +14,8 @@ GNU General Public License for more details.
|
||||
*/
|
||||
#include <config.h>
|
||||
|
||||
#include <bitset>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
@@ -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<uint8_t>((x) >> 24), \
|
||||
static_cast<uint8_t>((x) >> 16), \
|
||||
static_cast<uint8_t>((x) >> 8), \
|
||||
static_cast<uint8_t>((x))
|
||||
static_cast<std::uint8_t>((x) >> 24), \
|
||||
static_cast<std::uint8_t>((x) >> 16), \
|
||||
static_cast<std::uint8_t>((x) >> 8), \
|
||||
static_cast<std::uint8_t>((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<typename T, T null_value, typename Deleter, Deleter d>
|
||||
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<int, generic_deleter<int, -1, decltype(&close), &close>>;
|
||||
|
||||
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<scsi::sense_buffer>()};
|
||||
|
||||
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<unsigned char*>(dxfer_p);
|
||||
cmdio.cmdp = const_cast<unsigned char*>(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<struct cam_device, decltype(&cam_close_device)>
|
||||
{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<union ccb, decltype(&cam_freeccb)>
|
||||
{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<u_int8_t*>(dxfer_p),
|
||||
dxfer_len, SSD_FULL_SIZE, cmd_len, SCSI_TIMEOUT);
|
||||
ccb->csio.cdb_io.cdb_ptr = const_cast<u_int8_t*>(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<scsi::sense_buffer>()};
|
||||
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<const std::uint8_t*>(&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<const std::uint8_t[]> make_sde(encrypt_mode enc_mode,
|
||||
decrypt_mode dec_mode,
|
||||
std::uint8_t algorithm_index,
|
||||
const std::vector<std::uint8_t> 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<std::uint8_t[]>(length)};
|
||||
auto& page {reinterpret_cast<page_sde&>(*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<kad&>(*(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<const page_sde&>(*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<unsigned int>(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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,10 +16,16 @@ GNU General Public License for more details.
|
||||
#ifndef _SCSIENC_H
|
||||
#define _SCSIENC_H
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#ifdef HAVE_SYS_MACHINE_H
|
||||
#include <sys/machine.h>
|
||||
#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<std::uint8_t, sense_data::maximum_size>;
|
||||
|
||||
class scsi_error: public std::runtime_error {
|
||||
public:
|
||||
explicit scsi_error(std::unique_ptr<sense_buffer>&& buf) :
|
||||
sense_buf {std::move(buf)}, std::runtime_error {""} {}
|
||||
const sense_data& get_sense() const { return reinterpret_cast<sense_data&>(*sense_buf->data()); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<sense_buffer> sense_buf;
|
||||
};
|
||||
|
||||
// Extract pointers to kad structures within a variable-length page.
|
||||
// Page must have a page_header layout
|
||||
template<typename Page>
|
||||
std::vector<const kad *> read_page_kads(const Page& page)
|
||||
{
|
||||
return static_cast<uint16_t>(p[0]) << 8 | p[1];
|
||||
}
|
||||
// macro for a byte swapped int
|
||||
constexpr uint32_t BSLONG(const uint8_t *p)
|
||||
{
|
||||
return static_cast<uint32_t>(p[0]) << 24 |
|
||||
static_cast<uint32_t>(p[1]) << 16 |
|
||||
static_cast<uint32_t>(p[2]) << 8 |
|
||||
static_cast<uint32_t>(p[3]);
|
||||
const auto start {reinterpret_cast<const uint8_t*>(&page)};
|
||||
auto it {start + sizeof(Page)};
|
||||
const auto end {start + ntohs(page.length) + sizeof(page_header)};
|
||||
std::vector<const kad *> v {};
|
||||
|
||||
while (it < end) {
|
||||
auto elem {reinterpret_cast<const kad *>(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<const std::uint8_t[]> make_sde(encrypt_mode enc_mode,
|
||||
decrypt_mode dec_mode,
|
||||
std::uint8_t algorithm_index,
|
||||
const std::vector<std::uint8_t> 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<SSP_KAD> 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<uint8_t> 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
|
||||
|
||||
@@ -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<const SCSI_PAGE_INQ*>(response));
|
||||
print_device_inquiry(oss, reinterpret_cast<const scsi::inquiry_data&>(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<SSP_DES>(reinterpret_cast<const SSP_PAGE_BUFFER*>(page)).get(), true);
|
||||
print_device_status(oss, reinterpret_cast<const scsi::page_des&>(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<SSP_DES>(reinterpret_cast<const SSP_PAGE_BUFFER*>(page)).get(), true);
|
||||
print_device_status(oss, reinterpret_cast<const scsi::page_des&>(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<SSP_NBES>(reinterpret_cast<const SSP_PAGE_BUFFER*>(page)).get());
|
||||
print_volume_status(oss, reinterpret_cast<const scsi::page_nbes&>(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<SSP_NBES>(reinterpret_cast<const SSP_PAGE_BUFFER*>(page)).get());
|
||||
print_volume_status(oss, reinterpret_cast<const scsi::page_nbes&>(page));
|
||||
REQUIRE(oss.str() == expected_output);
|
||||
}
|
||||
|
||||
142
tests/scsi.cpp
142
tests/scsi.cpp
@@ -1,8 +1,10 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch.hpp"
|
||||
|
||||
#include "config.h"
|
||||
#include "scsiencrypt.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
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<std::uint8_t> 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<const scsi::page_sde&>(*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<std::uint8_t> 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<const scsi::page_sde&>(*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<std::uint8_t> 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<const scsi::page_sde&>(*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<std::uint8_t> 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<const scsi::page_sde&>(*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<const SSP_PAGE_BUFFER*>(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<const scsi::page_des&>(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<const SSP_PAGE_BUFFER*>(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<const scsi::page_nbes&>(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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user