Files
stenc/tests/scsi.cpp
James Wilson eeb7d72686 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
2022-05-13 22:43:58 +02:00

236 lines
9.1 KiB
C++

#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "config.h"
#include "scsiencrypt.h"
#include <arpa/inet.h>
using namespace std::literals::string_literals;
/**
* Compare the SPOUT Set Data Encryption pages generated by stenc to an
* expected output buffer based on the SCSI command spec.
*
* 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[] {
0x00, 0x10, // page code
0x00, 0x10, // page length
0x40, // scope
DEFAULT_CEEM << 6, // CEEM, CKOD, RDMC, et al.
0x00, // encyption mode
0x00, // decryption mode
0x01, // algorithm index
0x00, // key format
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved [8]
0x00, 0x00 // key length
};
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]") {
const uint8_t expected[] {
0x00, 0x10, // page code
0x00, 0x30, // page length
0x40, // scope
DEFAULT_CEEM << 6, // CEEM, CKOD, RDMC, et al.
0x02, // encyption mode
0x02, // decryption mode
0x01, // algorithm index
0x00, // key format
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved [8]
0x00, 0x20, // key length
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,
};
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,
};
std::string key_name {};
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]") {
const uint8_t expected[] {
0x00, 0x10, // page code
0x00, 0x30, // page length
0x40, // scope
DEFAULT_CEEM << 6 | 0x24, // CEEM, CKOD, RDMC, et al.
0x02, // encyption mode
0x02, // decryption mode
0x01, // algorithm index
0x00, // key format
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved [8]
0x00, 0x20, // key length
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,
};
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,
};
std::string key_name {};
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]") {
const uint8_t expected[] {
0x00, 0x10, // page code
0x00, 0x40, // page length
0x40, // scope
DEFAULT_CEEM << 6, // CEEM, CKOD, RDMC, et al.
0x02, // encyption mode
0x02, // decryption mode
0x01, // algorithm index
0x00, // key format
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved [8]
0x00, 0x20, // key length
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,
// KAD
0x00, // type
0x00, // authenticated
0x00, 0x0c, // length
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
};
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,
};
std::string key_name {"Hello world!"s};
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);
}
/**
* Check the representation of the SPIN Device Encryption Status page
* matches the values from the raw buffer. Input buffers were observed
* from device traffic.
*
* 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[] {
0x00, 0x20, // page code
0x00, 0x24, // length
0x42, // nexus = 2h, key scope = 2h
0x02, // encryption mode
0x02, // decryption mode
0x01, // algorithm index
0x00, 0x00, 0x00, 0x01, // key instance counter
0x18, // parameters control = 1, VCELB = 1, CEEMS = 0, RDMD = 0
0x00, // KAD format
0x00, 0x00, // ADSK count
0x00, 0x00, 0x00, 0x00, // reserved[8]
0x00, 0x00, 0x00, 0x00,
// KAD descriptor
0x00, // descriptor type
0x01, // authenticated
0x00, 0x0c, // length
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
};
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 {});
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]") {
const uint8_t buffer[] {
0x00, 0x21, // page code
0x00, 0x1c, // length
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x05, // compression status = 0, encryption status = 5h
0x01, // algorithm index
0x00, // EMES = 0, RDMDS = 0
0x00, // KAD format
// KAD descriptor
0x00, // descriptor type
0x01, // authenticated
0x00, 0x0c, // length
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
};
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 {});
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);
}