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:
James Wilson
2022-05-13 13:43:58 -07:00
committed by GitHub
parent 1508f432ad
commit eeb7d72686
5 changed files with 771 additions and 1001 deletions

View File

@@ -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) {

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}