Print algorithm information in detail output (#75)

* Print algorithm information in detail output
* Update documentation with information about algorithm listing
This commit is contained in:
James Wilson
2022-05-18 13:28:37 -07:00
committed by GitHub
parent a7a7c4750c
commit 86cca0804c
6 changed files with 313 additions and 20 deletions

View File

@@ -96,7 +96,8 @@ OPTIONS
be changed using the --with-default-algorithm configure option). be changed using the --with-default-algorithm configure option).
Setting encryption on/off may fail on some devices if this is not the Setting encryption on/off may fail on some devices if this is not the
correct algorithm for the drive (i.e. HP drives use an algorithm correct algorithm for the drive (i.e. HP drives use an algorithm
index of 1). index of 1). A list of supported algorithms can be obtained by requesting
drive status information with the **--detail** option.
**--ckod** **--ckod**
Only valid when setting encryption (see the **-e** option). Instructs Only valid when setting encryption (see the **-e** option). Instructs

View File

@@ -86,6 +86,91 @@ static void errorOut(const std::string& message) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
static void print_algorithm_name(std::ostream& os, const uint32_t code)
{
// Reference: SFSC / INCITS 501-2016
if (0x80010400 <= code && code <= 0x8001FFFF) {
os << "Vendor specific 0x" << std::setw(8) << std::setfill('0')
<< std::hex << code;
}
switch (code) {
case 0x0001000C:
os << "AES-256-CBC-HMAC-SHA-1";
break;
case 0x00010010:
os << "AES-256-CCM-128";
break;
case 0x00010014:
os << "AES-256-GCM-128";
break;
case 0x00010016:
os << "AES-256-XTS-HMAC-SHA-512";
break;
default:
os << "Unknown 0x" << std::setw(8) << std::setfill('0')
<< std::hex << code;
}
}
static void print_algorithms(std::ostream& os, const scsi::page_dec &page)
{
auto algorithms {scsi::read_algorithms(page)};
os << "Supported algorithms:\n";
for (auto ad_ptr: algorithms) {
auto& ad {*ad_ptr};
os << std::left << std::setw(5) << (unsigned int) {ad.algorithm_index};
print_algorithm_name(os, ntohl(ad.security_algorithm_code));
os.put('\n');
// Print KAD capabilities and size
auto dkad_c {
static_cast<unsigned int>(ad.flags3 & scsi::algorithm_descriptor::flags3_dkad_c_mask)
};
if (dkad_c == 1u << scsi::algorithm_descriptor::flags3_dkad_c_pos) {
os << std::left << std::setw(5) << "" << "Key descriptors not allowed\n";
} else if (dkad_c) {
os << std::left << std::setw(5) << "";
if (dkad_c == 1u << scsi::algorithm_descriptor::flags3_dkad_c_pos) {
os << "Key descriptors required, ";
} else {
os << "Key descriptors allowed, ";
}
if ((ad.flags2 & scsi::algorithm_descriptor::flags2_ukadf_mask) ==
scsi::algorithm_descriptor::flags2_ukadf_mask) {
os << "fixed ";
} else {
os << "maximum ";
}
os << ntohs(ad.maximum_ukad_length) << " bytes\n";
}
// Print raw decryption mode capability:
auto rdmc_c {
static_cast<unsigned int>(ad.flags3 & scsi::algorithm_descriptor::flags3_rdmc_c_mask)
};
switch (rdmc_c) {
case 1u << scsi::algorithm_descriptor::flags3_rdmc_c_pos:
case 6u << scsi::algorithm_descriptor::flags3_rdmc_c_pos:
os << std::left << std::setw(5) << "";
os << "Raw decryption mode not allowed\n";
break;
case 4u << scsi::algorithm_descriptor::flags3_rdmc_c_pos:
case 5u << scsi::algorithm_descriptor::flags3_rdmc_c_pos:
case 7u << scsi::algorithm_descriptor::flags3_rdmc_c_pos:
os << std::left << std::setw(5) << "";
os << "Raw decryption mode allowed, raw read ";
if (rdmc_c == 4u << scsi::algorithm_descriptor::flags3_rdmc_c_pos) {
os << "disabled by default\n";
} else {
os << "enabled by default\n";
}
break;
}
}
}
static void print_device_inquiry(std::ostream& os, const scsi::inquiry_data& iresult) static void print_device_inquiry(std::ostream& os, const scsi::inquiry_data& iresult)
{ {
os << std::left << std::setw(25) << "Vendor:"; os << std::left << std::setw(25) << "Vendor:";
@@ -434,6 +519,13 @@ int main(int argc, const char **argv) {
} }
} }
} }
if (detail) {
alignas(4) scsi::page_buffer buffer {};
scsi::get_dec(tapeDrive, buffer, sizeof(buffer));
auto& page {reinterpret_cast<const scsi::page_dec&>(buffer)};
print_algorithms(std::cout, page);
}
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} catch (const scsi::scsi_error& err) { } catch (const scsi::scsi_error& err) {
scsi::print_sense_data(std::cerr, err.get_sense()); scsi::print_sense_data(std::cerr, err.get_sense());

View File

@@ -336,4 +336,18 @@ void print_sense_data(std::ostream& os, const sense_data& sd) {
#endif #endif
} }
std::vector<const algorithm_descriptor*> read_algorithms(const page_dec& page)
{
auto it {reinterpret_cast<const uint8_t*>(&page.ads[0])};
const auto end {reinterpret_cast<const uint8_t*>(&page) + ntohs(page.length) + sizeof(page_header)};
std::vector<const algorithm_descriptor*> v {};
while (it < end) {
auto elem {reinterpret_cast<const algorithm_descriptor*>(it)};
v.push_back(elem);
it += ntohs(elem->length) + 4u; // length field + preceding 4 byte header
}
return v;
}
} }

View File

@@ -354,6 +354,8 @@ std::unique_ptr<const std::uint8_t[]> make_sde(encrypt_mode enc_mode,
// Write set data encryption parameters to device // Write set data encryption parameters to device
void write_sde(const std::string& device, const std::uint8_t *sde_buffer); void write_sde(const std::string& device, const std::uint8_t *sde_buffer);
void print_sense_data(std::ostream& os, const sense_data& sd); void print_sense_data(std::ostream& os, const sense_data& sd);
std::vector<const algorithm_descriptor*> read_algorithms(const page_dec& page);
} }
#endif #endif

View File

@@ -124,3 +124,29 @@ Volume Algorithm: 1\n"s};
print_volume_status(oss, reinterpret_cast<const scsi::page_nbes&>(page)); print_volume_status(oss, reinterpret_cast<const scsi::page_nbes&>(page));
REQUIRE(oss.str() == expected_output); REQUIRE(oss.str() == expected_output);
} }
TEST_CASE("Test SCSI get data encryption capabilities output", "[output]")
{
const std::uint8_t page[] {
0x00, 0x10, 0x00, 0x3c, 0x09, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x14,
0x8a, 0x8c, 0x00, 0x20, 0x00, 0x3c, 0x00, 0x20,
0xed, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x14, 0x02, 0x00, 0x00, 0x14,
0x8a, 0x8f, 0x00, 0x20, 0x00, 0x3c, 0x00, 0x20,
0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x10,
};
const std::string expected_output {"\
Supported algorithms:\n\
1 AES-256-GCM-128\n\
Key descriptors allowed, maximum 32 bytes\n\
Raw decryption mode not allowed\n\
2 AES-256-CCM-128\n\
Key descriptors allowed, fixed 32 bytes\n\
Raw decryption mode allowed, raw read disabled by default\n"s};
std::ostringstream oss;
print_algorithms(oss, reinterpret_cast<const scsi::page_dec&>(page));
REQUIRE(oss.str() == expected_output);
}

View File

@@ -15,9 +15,9 @@ using namespace std::literals::string_literals;
* This checks that the program can correctly format command buffers that * This checks that the program can correctly format command buffers that
* reflect available input and program options. * reflect available input and program options.
*/ */
TEST_CASE("Disable encryption command", "[scsi]") { TEST_CASE("Disable encryption command", "[scsi]")
uint8_t buffer[1024] {}; {
const uint8_t expected[] { const std::uint8_t expected[] {
0x00, 0x10, // page code 0x00, 0x10, // page code
0x00, 0x10, // page length 0x00, 0x10, // page length
0x40, // scope 0x40, // scope
@@ -38,11 +38,12 @@ TEST_CASE("Disable encryption command", "[scsi]") {
false)}; false)};
auto& page {reinterpret_cast<const scsi::page_sde&>(*page_buffer.get())}; auto& page {reinterpret_cast<const scsi::page_sde&>(*page_buffer.get())};
REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected)); REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected));
REQUIRE(memcmp(&page, expected, sizeof(expected)) == 0); REQUIRE(std::memcmp(&page, expected, sizeof(expected)) == 0);
} }
TEST_CASE("Enable encryption command", "[scsi]") { TEST_CASE("Enable encryption command", "[scsi]")
const uint8_t expected[] { {
const std::uint8_t expected[] {
0x00, 0x10, // page code 0x00, 0x10, // page code
0x00, 0x30, // page length 0x00, 0x30, // page length
0x40, // scope 0x40, // scope
@@ -72,11 +73,12 @@ TEST_CASE("Enable encryption command", "[scsi]") {
false)}; false)};
auto& page {reinterpret_cast<const scsi::page_sde&>(*page_buffer.get())}; auto& page {reinterpret_cast<const scsi::page_sde&>(*page_buffer.get())};
REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected)); REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected));
REQUIRE(memcmp(&page, expected, sizeof(expected)) == 0); REQUIRE(std::memcmp(&page, expected, sizeof(expected)) == 0);
} }
TEST_CASE("Enable encryption command with options", "[scsi]") { TEST_CASE("Enable encryption command with options", "[scsi]")
const uint8_t expected[] { {
const std::uint8_t expected[] {
0x00, 0x10, // page code 0x00, 0x10, // page code
0x00, 0x30, // page length 0x00, 0x30, // page length
0x40, // scope 0x40, // scope
@@ -106,11 +108,12 @@ TEST_CASE("Enable encryption command with options", "[scsi]") {
true)}; true)};
auto& page {reinterpret_cast<const scsi::page_sde&>(*page_buffer.get())}; auto& page {reinterpret_cast<const scsi::page_sde&>(*page_buffer.get())};
REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected)); REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected));
REQUIRE(memcmp(&page, expected, sizeof(expected)) == 0); REQUIRE(std::memcmp(&page, expected, sizeof(expected)) == 0);
} }
TEST_CASE("Enable encryption command with key name", "[scsi]") { TEST_CASE("Enable encryption command with key name", "[scsi]")
const uint8_t expected[] { {
const std::uint8_t expected[] {
0x00, 0x10, // page code 0x00, 0x10, // page code
0x00, 0x40, // page length 0x00, 0x40, // page length
0x40, // scope 0x40, // scope
@@ -146,7 +149,7 @@ TEST_CASE("Enable encryption command with key name", "[scsi]") {
false)}; false)};
auto& page {reinterpret_cast<const scsi::page_sde&>(*page_buffer.get())}; auto& page {reinterpret_cast<const scsi::page_sde&>(*page_buffer.get())};
REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected)); REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected));
REQUIRE(memcmp(&page, expected, sizeof(expected)) == 0); REQUIRE(std::memcmp(&page, expected, sizeof(expected)) == 0);
} }
/** /**
@@ -157,8 +160,9 @@ TEST_CASE("Enable encryption command with key name", "[scsi]") {
* This checks the SSP_DES structure layout matches the spec, especially * This checks the SSP_DES structure layout matches the spec, especially
* with regard to byte ordering and bitfield positions. * with regard to byte ordering and bitfield positions.
*/ */
TEST_CASE("Interpret device encryption status page", "[scsi]") { TEST_CASE("Interpret device encryption status page", "[scsi]")
const uint8_t buffer[] { {
const std::uint8_t buffer[] {
0x00, 0x20, // page code 0x00, 0x20, // page code
0x00, 0x24, // length 0x00, 0x24, // length
0x42, // nexus = 2h, key scope = 2h 0x42, // nexus = 2h, key scope = 2h
@@ -198,12 +202,14 @@ TEST_CASE("Interpret device encryption status page", "[scsi]") {
auto kads = read_page_kads(page_des); auto kads = read_page_kads(page_des);
REQUIRE(kads.size() == 1u); REQUIRE(kads.size() == 1u);
REQUIRE((kads[0]->flags & scsi::kad::flags_authenticated_mask) == std::byte {1u});
REQUIRE(ntohs(kads[0]->length) == std::strlen("Hello world!")); REQUIRE(ntohs(kads[0]->length) == std::strlen("Hello world!"));
REQUIRE(memcmp(kads[0]->descriptor, "Hello world!", ntohs(kads[0]->length)) == 0); REQUIRE(std::memcmp(kads[0]->descriptor, "Hello world!", ntohs(kads[0]->length)) == 0);
} }
TEST_CASE("Interpret next block encryption status page", "[scsi]") { TEST_CASE("Interpret next block encryption status page", "[scsi]")
const uint8_t buffer[] { {
const std::uint8_t buffer[] {
0x00, 0x21, // page code 0x00, 0x21, // page code
0x00, 0x1c, // length 0x00, 0x1c, // length
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
@@ -230,6 +236,158 @@ TEST_CASE("Interpret next block encryption status page", "[scsi]") {
auto kads = read_page_kads(page_nbes); auto kads = read_page_kads(page_nbes);
REQUIRE(kads.size() == 1u); REQUIRE(kads.size() == 1u);
REQUIRE((kads[0]->flags & scsi::kad::flags_authenticated_mask) == std::byte {1u});
REQUIRE(ntohs(kads[0]->length) == std::strlen("Hello world!")); REQUIRE(ntohs(kads[0]->length) == std::strlen("Hello world!"));
REQUIRE(memcmp(kads[0]->descriptor, "Hello world!", ntohs(kads[0]->length)) == 0); REQUIRE(std::memcmp(kads[0]->descriptor, "Hello world!", ntohs(kads[0]->length)) == 0);
}
TEST_CASE("Interpret data encryption capabilties page", "[scsi]")
{
const std::uint8_t buffer[] {
0x00, 0x10, // page code
0x00, 0x3c, // length
0x09, // EXTDECC and CFG_P
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// algorithm 1
0x01,
0x00,
0x00, 0x14,
0x8a, // capabilties
0x8c,
0x00, 0x20,
0x00, 0x3c,
0x00, 0x20,
0xed,
0x00,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0x00, 0x01, 0x00, 0x14,
// algorithm 2
0x02,
0x00,
0x00, 0x14,
0x8a, // capabilties
0x8f,
0x00, 0x20,
0x00, 0x3c,
0x00, 0x20,
0xd9,
0x00,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0x00, 0x01, 0x00, 0x10,
};
static_assert(sizeof(buffer) == sizeof(scsi::page_dec) +
2 * sizeof(scsi::algorithm_descriptor));
auto& page_dec {reinterpret_cast<const scsi::page_dec&>(buffer)};
REQUIRE(ntohs(page_dec.page_code) == 0x10u);
REQUIRE(ntohs(page_dec.length) == 60u);
REQUIRE((page_dec.flags & scsi::page_dec::flags_extdecc_mask) ==
std::byte {2u} << scsi::page_dec::flags_extdecc_pos);
REQUIRE((page_dec.flags & scsi::page_dec::flags_cfg_p_mask) ==
std::byte {1u} << scsi::page_dec::flags_cfg_p_pos);
auto algorithms {read_algorithms(page_dec)};
REQUIRE(algorithms.size() == 2u);
auto& algo1 {*algorithms[0]};
REQUIRE(algo1.algorithm_index == 1u);
REQUIRE(ntohs(algo1.length) == 20u);
REQUIRE((algo1.flags1 & scsi::algorithm_descriptor::flags1_avfmv_mask) ==
scsi::algorithm_descriptor::flags1_avfmv_mask);
REQUIRE((algo1.flags1 & scsi::algorithm_descriptor::flags1_sdk_c_mask) ==
std::byte {});
REQUIRE((algo1.flags1 & scsi::algorithm_descriptor::flags1_mac_c_mask) ==
std::byte {});
REQUIRE((algo1.flags1 & scsi::algorithm_descriptor::flags1_delb_c_mask) ==
std::byte {});
REQUIRE((algo1.flags1 & scsi::algorithm_descriptor::flags1_decrypt_c_mask) ==
std::byte {2u} << scsi::algorithm_descriptor::flags1_decrypt_c_pos);
REQUIRE((algo1.flags1 & scsi::algorithm_descriptor::flags1_encrypt_c_mask) ==
std::byte {2u} << scsi::algorithm_descriptor::flags1_encrypt_c_pos);
REQUIRE((algo1.flags2 & scsi::algorithm_descriptor::flags2_avfcp_mask) ==
std::byte {2u} << scsi::algorithm_descriptor::flags2_avfcp_pos);
REQUIRE((algo1.flags2 & scsi::algorithm_descriptor::flags2_nonce_mask) ==
std::byte {});
REQUIRE((algo1.flags2 & scsi::algorithm_descriptor::flags2_kadf_c_mask) ==
scsi::algorithm_descriptor::flags2_kadf_c_mask);
REQUIRE((algo1.flags2 & scsi::algorithm_descriptor::flags2_vcelb_c_mask) ==
scsi::algorithm_descriptor::flags2_vcelb_c_mask);
REQUIRE((algo1.flags2 & scsi::algorithm_descriptor::flags2_ukadf_mask) ==
std::byte {});
REQUIRE((algo1.flags2 & scsi::algorithm_descriptor::flags2_akadf_mask) ==
std::byte {});
REQUIRE(ntohs(algo1.maximum_ukad_length) == 32u);
REQUIRE(ntohs(algo1.maximum_akad_length) == 60u);
REQUIRE(ntohs(algo1.key_length) == 32u);
REQUIRE((algo1.flags3 & scsi::algorithm_descriptor::flags3_dkad_c_mask) ==
std::byte {3u} << scsi::algorithm_descriptor::flags3_dkad_c_pos);
REQUIRE((algo1.flags3 & scsi::algorithm_descriptor::flags3_eemc_c_mask) ==
std::byte {2u} << scsi::algorithm_descriptor::flags3_eemc_c_pos);
REQUIRE((algo1.flags3 & scsi::algorithm_descriptor::flags3_rdmc_c_mask) ==
std::byte {6u} << scsi::algorithm_descriptor::flags3_rdmc_c_pos);
REQUIRE((algo1.flags3 & scsi::algorithm_descriptor::flags3_earem_mask) ==
scsi::algorithm_descriptor::flags3_earem_mask);
REQUIRE((algo1.maximum_eedk_count & scsi::algorithm_descriptor::maximum_eedk_count_mask) ==
0u);
REQUIRE(ntohs(algo1.msdk_count) == 0u);
REQUIRE(ntohs(algo1.maximum_eedk_size) == 0u);
REQUIRE(ntohl(algo1.security_algorithm_code) == 0x00010014u);
auto& algo2 {*algorithms[1]};
REQUIRE(algo2.algorithm_index == 2u);
REQUIRE(ntohs(algo2.length) == 20u);
REQUIRE((algo2.flags1 & scsi::algorithm_descriptor::flags1_avfmv_mask) ==
scsi::algorithm_descriptor::flags1_avfmv_mask);
REQUIRE((algo2.flags1 & scsi::algorithm_descriptor::flags1_sdk_c_mask) ==
std::byte {});
REQUIRE((algo2.flags1 & scsi::algorithm_descriptor::flags1_mac_c_mask) ==
std::byte {});
REQUIRE((algo2.flags1 & scsi::algorithm_descriptor::flags1_delb_c_mask) ==
std::byte {});
REQUIRE((algo2.flags1 & scsi::algorithm_descriptor::flags1_decrypt_c_mask) ==
std::byte {2u} << scsi::algorithm_descriptor::flags1_decrypt_c_pos);
REQUIRE((algo2.flags1 & scsi::algorithm_descriptor::flags1_encrypt_c_mask) ==
std::byte {2u} << scsi::algorithm_descriptor::flags1_encrypt_c_pos);
REQUIRE((algo2.flags2 & scsi::algorithm_descriptor::flags2_avfcp_mask) ==
std::byte {2u} << scsi::algorithm_descriptor::flags2_avfcp_pos);
REQUIRE((algo2.flags2 & scsi::algorithm_descriptor::flags2_nonce_mask) ==
std::byte {});
REQUIRE((algo2.flags2 & scsi::algorithm_descriptor::flags2_kadf_c_mask) ==
scsi::algorithm_descriptor::flags2_kadf_c_mask);
REQUIRE((algo2.flags2 & scsi::algorithm_descriptor::flags2_vcelb_c_mask) ==
scsi::algorithm_descriptor::flags2_vcelb_c_mask);
REQUIRE((algo2.flags2 & scsi::algorithm_descriptor::flags2_ukadf_mask) ==
scsi::algorithm_descriptor::flags2_ukadf_mask);
REQUIRE((algo2.flags2 & scsi::algorithm_descriptor::flags2_akadf_mask) ==
scsi::algorithm_descriptor::flags2_akadf_mask);
REQUIRE(ntohs(algo2.maximum_ukad_length) == 32u);
REQUIRE(ntohs(algo2.maximum_akad_length) == 60u);
REQUIRE(ntohs(algo2.key_length) == 32u);
REQUIRE((algo2.flags3 & scsi::algorithm_descriptor::flags3_dkad_c_mask) ==
std::byte {3u} << scsi::algorithm_descriptor::flags3_dkad_c_pos);
REQUIRE((algo2.flags3 & scsi::algorithm_descriptor::flags3_eemc_c_mask) ==
std::byte {1u} << scsi::algorithm_descriptor::flags3_eemc_c_pos);
REQUIRE((algo2.flags3 & scsi::algorithm_descriptor::flags3_rdmc_c_mask) ==
std::byte {4u} << scsi::algorithm_descriptor::flags3_rdmc_c_pos);
REQUIRE((algo2.flags3 & scsi::algorithm_descriptor::flags3_earem_mask) ==
scsi::algorithm_descriptor::flags3_earem_mask);
REQUIRE((algo2.maximum_eedk_count & scsi::algorithm_descriptor::maximum_eedk_count_mask) ==
0u);
REQUIRE(ntohs(algo2.msdk_count) == 0u);
REQUIRE(ntohs(algo2.maximum_eedk_size) == 0u);
REQUIRE(ntohl(algo2.security_algorithm_code) == 0x00010010u);
} }