438 lines
15 KiB
C++
438 lines
15 KiB
C++
/*
|
|
Header file to send and recieve SPIN/SPOUT commands to SCSI device
|
|
|
|
Original program copyright 2010 John D. Coleman
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
*/
|
|
|
|
#ifndef _SCSIENC_H
|
|
#define _SCSIENC_H
|
|
|
|
#include <array>
|
|
#include <bitset>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <ostream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#ifdef HAVE_SYS_MACHINE_H
|
|
#include <sys/machine.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
|
|
constexpr std::size_t SSP_PAGE_ALLOCATION = 8192;
|
|
|
|
// outputs hex in a 2 digit pair
|
|
#define HEX(x) \
|
|
std::right << std::setw(2) << std::setfill('0') << std::hex << (int)(x) \
|
|
<< std::setfill(' ')
|
|
|
|
namespace scsi {
|
|
|
|
enum class encrypt_mode : std::uint8_t {
|
|
off = 0u,
|
|
external = 1u,
|
|
on = 2u,
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, encrypt_mode m)
|
|
{
|
|
if (m == encrypt_mode::off) {
|
|
os << "off";
|
|
} else if (m == encrypt_mode::external) {
|
|
os << "external";
|
|
} else {
|
|
os << "on";
|
|
}
|
|
return os;
|
|
}
|
|
|
|
enum class decrypt_mode : std::uint8_t {
|
|
off = 0u,
|
|
raw = 1u,
|
|
on = 2u,
|
|
mixed = 3u,
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, decrypt_mode m)
|
|
{
|
|
if (m == decrypt_mode::off) {
|
|
os << "off";
|
|
} else if (m == decrypt_mode::raw) {
|
|
os << "raw";
|
|
} else if (m == decrypt_mode::on) {
|
|
os << "on";
|
|
} else {
|
|
os << "mixed";
|
|
}
|
|
return os;
|
|
}
|
|
|
|
enum class kad_type : std::uint8_t {
|
|
ukad = 0u, // unauthenticated key-associated data
|
|
akad = 1u, // authenticated key-associated data
|
|
nonce = 2u, // nonce value
|
|
mkad = 3u, // metadata key-associated data
|
|
wkkad = 4u, // wrapped key key-associated data
|
|
};
|
|
|
|
enum class kadf : std::uint8_t {
|
|
unspecified = 0u,
|
|
binary_key_name = 1u,
|
|
ascii_key_name = 2u,
|
|
};
|
|
|
|
// key-associated data
|
|
struct __attribute__((packed)) kad {
|
|
kad_type 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};
|
|
// volume contains encrypted logical blocks
|
|
static constexpr auto flags_vcelb_pos {3u};
|
|
static constexpr std::byte flags_vcelb_mask {1u << flags_vcelb_pos};
|
|
// check external encryption mode status
|
|
static constexpr auto flags_ceems_pos {1u};
|
|
static constexpr std::byte flags_ceems_mask {3u << flags_ceems_pos};
|
|
// raw decryption mode disabled
|
|
static constexpr auto flags_rdmd_pos {0u};
|
|
static constexpr std::byte flags_rdmd_mask {1u << flags_rdmd_pos};
|
|
kadf 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;
|
|
// check external encryption mode
|
|
static constexpr auto flags_ceem_pos {6u};
|
|
static constexpr std::byte flags_ceem_mask {3u << flags_ceem_pos};
|
|
// raw decryption mode control
|
|
static constexpr auto flags_rdmc_pos {4u};
|
|
static constexpr std::byte flags_rdmc_mask {3u << flags_rdmc_pos};
|
|
// supplemental decryption key
|
|
static constexpr auto flags_sdk_pos {3u};
|
|
static constexpr std::byte flags_sdk_mask {1u << flags_sdk_pos};
|
|
// clear key on demount
|
|
static constexpr auto flags_ckod_pos {2u};
|
|
static constexpr std::byte flags_ckod_mask {1u << flags_ckod_pos};
|
|
// clear key on reservation preempt
|
|
static constexpr auto flags_ckorp_pos {1u};
|
|
static constexpr std::byte flags_ckorp_mask {1u << flags_ckorp_pos};
|
|
// clear key on reservation loss
|
|
static constexpr auto flags_ckorl_pos {0u};
|
|
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;
|
|
kadf 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, // corresponds to --allow-raw-read
|
|
// command line option
|
|
disabled = 3u << page_sde::flags_rdmc_pos, // corresponds to --no-allow-raw-read command
|
|
// line option
|
|
};
|
|
|
|
// 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;
|
|
// encryption mode external status
|
|
static constexpr auto flags_emes_pos {1u};
|
|
static constexpr std::byte flags_emes_mask {1u << flags_emes_pos};
|
|
// raw decryption mode disabled status
|
|
static constexpr auto flags_rdmds_pos {0u};
|
|
static constexpr std::byte flags_rdmds_mask {1u << flags_rdmds_pos};
|
|
kadf 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;
|
|
// algorithm valid for mounted volume
|
|
static constexpr auto flags1_avfmv_pos {7u};
|
|
static constexpr std::byte flags1_avfmv_mask {1u << flags1_avfmv_pos};
|
|
// supplemental decryption key capable
|
|
static constexpr auto flags1_sdk_c_pos {6u};
|
|
static constexpr std::byte flags1_sdk_c_mask {1u << flags1_sdk_c_pos};
|
|
// message authentication code capable
|
|
static constexpr auto flags1_mac_c_pos {5u};
|
|
static constexpr std::byte flags1_mac_c_mask {1u << flags1_mac_c_pos};
|
|
// distinguish encrypted logical block capable
|
|
static constexpr auto flags1_delb_c_pos {4u};
|
|
static constexpr std::byte flags1_delb_c_mask {1u << flags1_delb_c_pos};
|
|
// decryption capabilities
|
|
static constexpr auto flags1_decrypt_c_pos {2u};
|
|
static constexpr std::byte flags1_decrypt_c_mask {3u << flags1_decrypt_c_pos};
|
|
// encryption capabilities
|
|
static constexpr auto flags1_encrypt_c_pos {0u};
|
|
static constexpr std::byte flags1_encrypt_c_mask {3u << flags1_encrypt_c_pos};
|
|
std::byte flags2;
|
|
// algorithm valid for current logical position
|
|
static constexpr auto flags2_avfcp_pos {6u};
|
|
static constexpr std::byte flags2_avfcp_mask {3u << flags2_avfcp_pos};
|
|
// nonce capabilities
|
|
static constexpr auto flags2_nonce_pos {4u};
|
|
static constexpr std::byte flags2_nonce_mask {3u << flags2_nonce_pos};
|
|
// KAD format capable
|
|
static constexpr auto flags2_kadf_c_pos {3u};
|
|
static constexpr std::byte flags2_kadf_c_mask {1u << flags2_kadf_c_pos};
|
|
// volume contains encrypted logical blocks capable
|
|
static constexpr auto flags2_vcelb_c_pos {2u};
|
|
static constexpr std::byte flags2_vcelb_c_mask {1u << flags2_vcelb_c_pos};
|
|
// U-KAD fixed
|
|
static constexpr auto flags2_ukadf_pos {1u};
|
|
static constexpr std::byte flags2_ukadf_mask {1u << flags2_ukadf_pos};
|
|
// A-KAD fixed
|
|
static constexpr auto flags2_akadf_pos {0u};
|
|
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;
|
|
// decryption capabilities
|
|
static constexpr auto flags3_dkad_c_pos {6u};
|
|
static constexpr std::byte flags3_dkad_c_mask {3u << flags3_dkad_c_pos};
|
|
// external encryption mode control capabilities
|
|
static constexpr auto flags3_eemc_c_pos {4u};
|
|
static constexpr std::byte flags3_eemc_c_mask {3u << flags3_eemc_c_pos};
|
|
// raw decryption mode control capabilities
|
|
static constexpr auto flags3_rdmc_c_pos {1u};
|
|
static constexpr std::byte flags3_rdmc_c_mask {7u << flags3_rdmc_c_pos};
|
|
// encryption algorithm records encryption mode
|
|
static constexpr auto flags3_earem_pos {0u};
|
|
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;
|
|
// external data encryption control capable
|
|
static constexpr auto flags_extdecc_pos {2u};
|
|
static constexpr std::byte flags_extdecc_mask {3u << flags_extdecc_pos};
|
|
// configuration prevented
|
|
static constexpr auto flags_cfg_p_pos {0u};
|
|
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 std::byte no_sense {0u};
|
|
static constexpr std::byte recovered_error {1u};
|
|
static constexpr std::byte not_ready {2u};
|
|
static constexpr std::byte medium_error {3u};
|
|
static constexpr std::byte hardware_error {4u};
|
|
static constexpr std::byte illegal_request {5u};
|
|
static constexpr std::byte unit_attention {6u};
|
|
static constexpr std::byte data_protect {7u};
|
|
static constexpr std::byte blank_check {8u};
|
|
|
|
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 {"SCSI I/O 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<std::reference_wrapper<const kad>> read_page_kads(const Page& page)
|
|
{
|
|
const auto start {reinterpret_cast<const std::uint8_t *>(&page)};
|
|
auto it {start + sizeof(Page)};
|
|
const auto end {start + ntohs(page.length) + sizeof(page_header)};
|
|
std::vector<std::reference_wrapper<const kad>> v {};
|
|
|
|
while (it < end) {
|
|
auto elem {reinterpret_cast<const kad *>(it)};
|
|
v.push_back(std::cref(*elem));
|
|
it += ntohs(elem->length) + sizeof(kad);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
// Check if a tape is loaded
|
|
bool is_device_ready(const std::string& device);
|
|
// Get SCSI inquiry data from device
|
|
inquiry_data get_inquiry(const std::string& device);
|
|
// Get data encryption status page
|
|
void get_des(const std::string& device, std::uint8_t *buffer,
|
|
std::size_t length);
|
|
// Get next block encryption status page
|
|
void get_nbes(const std::string& device, std::uint8_t *buffer,
|
|
std::size_t length);
|
|
// Get device encryption capabilities
|
|
void get_dec(const std::string& device, 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, kadf key_format, 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);
|
|
std::vector<std::reference_wrapper<const algorithm_descriptor>>
|
|
read_algorithms(const page_dec& page);
|
|
|
|
} // namespace scsi
|
|
|
|
#endif
|