From 86cca0804cc301d7c35bcf8b576ef6a1f97d1e55 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Wed, 18 May 2022 13:28:37 -0700 Subject: [PATCH] Print algorithm information in detail output (#75) * Print algorithm information in detail output * Update documentation with information about algorithm listing --- man/stenc.rst | 3 +- src/main.cpp | 92 +++++++++++++++++++++ src/scsiencrypt.cpp | 14 ++++ src/scsiencrypt.h | 2 + tests/output.cpp | 26 ++++++ tests/scsi.cpp | 196 +++++++++++++++++++++++++++++++++++++++----- 6 files changed, 313 insertions(+), 20 deletions(-) diff --git a/man/stenc.rst b/man/stenc.rst index 8c7186b..4963740 100644 --- a/man/stenc.rst +++ b/man/stenc.rst @@ -96,7 +96,8 @@ OPTIONS be changed using the --with-default-algorithm configure option). 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 - index of 1). + index of 1). A list of supported algorithms can be obtained by requesting + drive status information with the **--detail** option. **--ckod** Only valid when setting encryption (see the **-e** option). Instructs diff --git a/src/main.cpp b/src/main.cpp index 4d247a4..3e1b8ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -86,6 +86,91 @@ static void errorOut(const std::string& message) { 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(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(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) { 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(buffer)}; + + print_algorithms(std::cout, page); + } exit(EXIT_SUCCESS); } catch (const scsi::scsi_error& err) { scsi::print_sense_data(std::cerr, err.get_sense()); diff --git a/src/scsiencrypt.cpp b/src/scsiencrypt.cpp index b2e5d14..4e16d11 100644 --- a/src/scsiencrypt.cpp +++ b/src/scsiencrypt.cpp @@ -336,4 +336,18 @@ void print_sense_data(std::ostream& os, const sense_data& sd) { #endif } +std::vector read_algorithms(const page_dec& page) +{ + auto it {reinterpret_cast(&page.ads[0])}; + const auto end {reinterpret_cast(&page) + ntohs(page.length) + sizeof(page_header)}; + std::vector v {}; + + while (it < end) { + auto elem {reinterpret_cast(it)}; + v.push_back(elem); + it += ntohs(elem->length) + 4u; // length field + preceding 4 byte header + } + return v; +} + } diff --git a/src/scsiencrypt.h b/src/scsiencrypt.h index 37cda59..edba22b 100644 --- a/src/scsiencrypt.h +++ b/src/scsiencrypt.h @@ -354,6 +354,8 @@ std::unique_ptr make_sde(encrypt_mode enc_mode, // 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); +std::vector read_algorithms(const page_dec& page); + } #endif diff --git a/tests/output.cpp b/tests/output.cpp index 6e0628d..d6aaf29 100644 --- a/tests/output.cpp +++ b/tests/output.cpp @@ -124,3 +124,29 @@ Volume Algorithm: 1\n"s}; print_volume_status(oss, reinterpret_cast(page)); 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(page)); + REQUIRE(oss.str() == expected_output); +} diff --git a/tests/scsi.cpp b/tests/scsi.cpp index 62e0b3f..b309cea 100644 --- a/tests/scsi.cpp +++ b/tests/scsi.cpp @@ -15,9 +15,9 @@ using namespace std::literals::string_literals; * This checks that the program can correctly format command buffers that * reflect available input and program options. */ -TEST_CASE("Disable encryption command", "[scsi]") { - uint8_t buffer[1024] {}; - const uint8_t expected[] { +TEST_CASE("Disable encryption command", "[scsi]") +{ + const std::uint8_t expected[] { 0x00, 0x10, // page code 0x00, 0x10, // page length 0x40, // scope @@ -38,11 +38,12 @@ TEST_CASE("Disable encryption command", "[scsi]") { false)}; auto& page {reinterpret_cast(*page_buffer.get())}; REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected)); - REQUIRE(memcmp(&page, expected, sizeof(expected)) == 0); + REQUIRE(std::memcmp(&page, expected, sizeof(expected)) == 0); } -TEST_CASE("Enable encryption command", "[scsi]") { - const uint8_t expected[] { +TEST_CASE("Enable encryption command", "[scsi]") +{ + const std::uint8_t expected[] { 0x00, 0x10, // page code 0x00, 0x30, // page length 0x40, // scope @@ -72,11 +73,12 @@ TEST_CASE("Enable encryption command", "[scsi]") { false)}; auto& page {reinterpret_cast(*page_buffer.get())}; REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected)); - REQUIRE(memcmp(&page, expected, sizeof(expected)) == 0); + REQUIRE(std::memcmp(&page, expected, sizeof(expected)) == 0); } -TEST_CASE("Enable encryption command with options", "[scsi]") { - const uint8_t expected[] { +TEST_CASE("Enable encryption command with options", "[scsi]") +{ + const std::uint8_t expected[] { 0x00, 0x10, // page code 0x00, 0x30, // page length 0x40, // scope @@ -106,11 +108,12 @@ TEST_CASE("Enable encryption command with options", "[scsi]") { true)}; auto& page {reinterpret_cast(*page_buffer.get())}; REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected)); - REQUIRE(memcmp(&page, expected, sizeof(expected)) == 0); + REQUIRE(std::memcmp(&page, expected, sizeof(expected)) == 0); } -TEST_CASE("Enable encryption command with key name", "[scsi]") { - const uint8_t expected[] { +TEST_CASE("Enable encryption command with key name", "[scsi]") +{ + const std::uint8_t expected[] { 0x00, 0x10, // page code 0x00, 0x40, // page length 0x40, // scope @@ -146,7 +149,7 @@ TEST_CASE("Enable encryption command with key name", "[scsi]") { false)}; auto& page {reinterpret_cast(*page_buffer.get())}; REQUIRE(sizeof(scsi::page_header) + ntohs(page.length) == sizeof(expected)); - REQUIRE(memcmp(&page, expected, sizeof(expected)) == 0); + 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 * with regard to byte ordering and bitfield positions. */ -TEST_CASE("Interpret device encryption status page", "[scsi]") { - const uint8_t buffer[] { +TEST_CASE("Interpret device encryption status page", "[scsi]") +{ + const std::uint8_t buffer[] { 0x00, 0x20, // page code 0x00, 0x24, // length 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); 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(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]") { - const uint8_t buffer[] { +TEST_CASE("Interpret next block encryption status page", "[scsi]") +{ + const std::uint8_t buffer[] { 0x00, 0x21, // page code 0x00, 0x1c, // length 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); 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(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(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); }