From 13f09ea5aaa91a90e40265808582e54b9ba13390 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Sun, 8 May 2022 12:07:58 -0700 Subject: [PATCH] Remove KeyInfo and use standard library to parse hex strings (#46) * Remove KeyInfo and use standard library to parse hex strings * One declaration per line, more useful name of key_from_hex_chars --- src/Makefile.am | 4 +- src/keyinfo.cpp | 122 -------------------------------------------- src/keyinfo.h | 20 -------- src/main.cpp | 60 +++++++++++++++++----- src/scsiencrypt.cpp | 15 +++--- src/scsiencrypt.h | 4 +- tests/Makefile.am | 2 +- tests/scsi.cpp | 24 ++++++--- 8 files changed, 77 insertions(+), 174 deletions(-) delete mode 100644 src/keyinfo.cpp delete mode 100644 src/keyinfo.h diff --git a/src/Makefile.am b/src/Makefile.am index c94aa27..f882d35 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ bin_PROGRAMS = stenc -AM_CXXFLAGS = $(INTI_CFLAGS) $(DEPS_CFLAGS) -stenc_SOURCES = main.cpp scsiencrypt.cpp scsiencrypt.h keyinfo.h keyinfo.cpp +AM_CXXFLAGS = -std=c++17 $(INTI_CFLAGS) $(DEPS_CFLAGS) +stenc_SOURCES = main.cpp scsiencrypt.cpp scsiencrypt.h #stenc_LDADD = $(INTI_LIBS) diff --git a/src/keyinfo.cpp b/src/keyinfo.cpp deleted file mode 100644 index 5be2fab..0000000 --- a/src/keyinfo.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include "keyinfo.h" -#include "scsiencrypt.h" -#include -#include -#include -#include -#include - -Keyinfo::Keyinfo() { - valid = false; - check = ""; - key = NULL; - keySize = 0; -} -void Keyinfo::load(std::string hexinput) { - valid = true; - if (hexinput.size() < 2) { - valid = false; - std::cout << "Key input too short!\n"; - return; - } - // parse for invalid characters - for (unsigned int i = 0; i < hexinput.size(); i++) { - switch ((unsigned char)hexinput.at(i)) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - break; - default: - std::cout << "Invalid character '" << hexinput.at(i) - << "' found in key!\n"; - valid = false; - return; - } - } - // delete the key if its already allocated - if (key != NULL) - delete[] key; - // check that the input size is divisible by 2 - if (hexinput.size() % 2 != 0) { - valid = false; - std::cout << "Each hexadecimal byte must consist of 2 digits!\n"; - return; - } - // convert the hex input to a char* - loadKey(hexinput); - // load the check value - loadCheck(); - // check for oversized key - if (keySize == 0 || keySize > SSP_KEY_LENGTH) { - std::cout << "Key size cannot exceed " << (SSP_KEY_LENGTH * 8) - << " bits!\n"; - std::cout << "Provided key is " << (keySize * 8) << " bits in length.\n"; - valid = false; - return; - } - std::cout << "Provided key length is " << (keySize * 8) << " bits.\n"; - std::cout << "Key checksum is " << check << ".\n"; -} -void Keyinfo::loadCheck() { - int i; - int chk = 0; - for (i = 0; i < keySize; i++) { - chk += ((int)key[i]) * (i + 1); - } - std::stringstream retval; - retval << std::hex << chk; - check = retval.str(); -} -Keyinfo::~Keyinfo() { delete[] key; } -void Keyinfo::loadKey(std::string str) { - int length = str.size(); - // make sure the input string has an even digit numbers - if (length % 2 == 1) { - str = "0" + str; - length++; - } - - // allocate memory for the output array - key = new char[length / 2]; - memset(key, 0, (length / 2) + 1); - keySize = length / 2; - - std::stringstream sstr(str); - for (int i = 0; i < keySize; i++) { - char ch1, ch2; - sstr >> ch1 >> ch2; - int dig1 = 0, dig2 = 0; - if (isdigit(ch1)) - dig1 = ch1 - '0'; - else if (ch1 >= 'A' && ch1 <= 'F') - dig1 = ch1 - 'A' + 10; - else if (ch1 >= 'a' && ch1 <= 'f') - dig1 = ch1 - 'a' + 10; - if (isdigit(ch2)) - dig2 = ch2 - '0'; - else if (ch2 >= 'A' && ch2 <= 'F') - dig2 = ch2 - 'A' + 10; - else if (ch2 >= 'a' && ch2 <= 'f') - dig2 = ch2 - 'a' + 10; - key[i] = dig1 * 16 + dig2; - } -} diff --git a/src/keyinfo.h b/src/keyinfo.h deleted file mode 100644 index 2099380..0000000 --- a/src/keyinfo.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef KEYINFO_H -#define KEYINFO_H -#include - -class Keyinfo { -public: - char *key; - int keySize; - bool valid; - std::string check; - void load(std::string hexinput); - Keyinfo(); - ~Keyinfo(); - -private: - void loadKey(std::string str); - void loadCheck(); -}; - -#endif diff --git a/src/main.cpp b/src/main.cpp index 633f167..8121439 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,11 +13,10 @@ 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. */ -#include "keyinfo.h" #include "scsiencrypt.h" +#include #include -#include #ifdef HAVE_UNISTD_H #include #endif @@ -25,10 +24,14 @@ GNU General Public License for more details. #include #include #include +#include +#include #include #include #include +#include #include +#include #ifdef HAVE_STDLIB_H #include #endif @@ -73,6 +76,33 @@ std::string timestamp(); void echo(bool); std::ofstream logFile; +static std::optional> key_from_hex_chars(const std::string& s) +{ + auto it = s.data(); + std::vector bytes; + + if (s.size() % 2) { // treated as if there is an implicit leading 0 + uint8_t result; + auto [ptr, ec] { std::from_chars(it, it + 1, result, 16) }; + if (ec != errc {}) { + return {}; + } + bytes.push_back(result); + it = ptr; + } + + while (*it) { + uint8_t result; + auto [ptr, ec] { std::from_chars(it, it + 2, result, 16) }; + if (ec != errc {}) { + return {}; + } + bytes.push_back(result); + it = ptr; + } + return bytes; +} + int main(int argc, char **argv) { bitcheck bc; memset(&bc, 0, 1); @@ -262,11 +292,10 @@ int main(int argc, char **argv) { exit(EXIT_SUCCESS); } - Keyinfo ki{}; if (drvOptions.cryptMode != CRYPTMODE_OFF) { if (keyFile == "") { - std::string p1 = "01"; - std::string p2 = "02"; + std::string p1; + std::string p2; bool done = false; while (!done) { std::cout << "Enter key in hex format: "; @@ -280,17 +309,20 @@ int main(int argc, char **argv) { std::cout << "\n"; if (p1 != p2) { std::cout << "Keys do not match!\n"; + } else if (p1.empty()) { + std::cout << "Key cannot be empty!\n"; } else { - ki.load(p1); - if (ki.valid) { + if (auto key_bytes = key_from_hex_chars(p1)) { std::cout << "Set encryption using this key? [y/n]: "; std::string ans = ""; getline(std::cin, ans); if (ans == "y") { + drvOptions.cryptoKey = *key_bytes; done = true; } - } else + } else { std::cout << "Invalid key!\n"; + } } } drvOptions.keyName = keyDesc; @@ -303,14 +335,15 @@ int main(int argc, char **argv) { getline(myfile, keyInput); getline(myfile, keyDesc); myfile.close(); - ki.load(keyInput); - if (!ki.valid) + if (auto key_bytes = key_from_hex_chars(keyInput)) { + drvOptions.cryptoKey = *key_bytes; + } else { errorOut("Invalid key found in '" + keyFile + "'"); + } drvOptions.keyName = keyDesc; } else errorOut("Could not open '" + keyFile + "' for reading"); } - drvOptions.cryptoKey.assign(ki.key, ki.keySize); } // Write the options to the tape device @@ -332,10 +365,9 @@ int main(int argc, char **argv) { if (drvOptions.cryptMode != CRYPTMODE_OFF) { std::stringstream msg; msg << "Encryption turned on for device '" << tapeDrive << "'. "; - if (drvOptions.keyName.size() == 0) - msg << "Key Checksum: " << ki.check; - else + if (!drvOptions.keyName.empty()) { msg << "Key Descriptor: '" << drvOptions.keyName << "'"; + } msg << " Key Instance: " << std::dec << BSLONG(opt->des.keyInstance) << std::endl; diff --git a/src/scsiencrypt.cpp b/src/scsiencrypt.cpp index 82a2fb6..7aa571e 100644 --- a/src/scsiencrypt.cpp +++ b/src/scsiencrypt.cpp @@ -222,21 +222,20 @@ int SCSIInitSDEPage(SCSIEncryptOptions *eOptions, break; default: byteswap((unsigned char *)options.keyLength, 2, DEFAULT_KEYSIZE); - eOptions->cryptoKey = ""; // blank the key - eOptions->keyName = ""; // blank the key name, not supported when turned off + eOptions->cryptoKey.clear(); // blank the key + eOptions->keyName.clear(); // blank the key name, not supported when turned off break; } - if (eOptions->cryptoKey != "") { + if (!eOptions->cryptoKey.empty()) { // byte swap the keylength byteswap((unsigned char *)&options.keyLength, 2, eOptions->cryptoKey.size()); // copy the crypto key into the options - eOptions->cryptoKey.copy((char *)&options.keyData, - eOptions->cryptoKey.size(), 0); + std::copy(eOptions->cryptoKey.begin(), eOptions->cryptoKey.end(), options.keyData); } // create the key descriptor - if (eOptions->keyName != "") { + if (!eOptions->keyName.empty()) { SSP_KAD kad; memset(&kad, 0, sizeof(kad)); // set the descriptor length to the length of the keyName @@ -381,7 +380,7 @@ bool SCSIExecute(std::string tapedrive, unsigned char *cmd_p, int cmd_len, std::cout << "SCSI Data: "; for (int i = 0; i < dxfer_len; i++) { - std::cout << HEX(dxfer_p[i])); + std::cout << HEX(dxfer_p[i]); } std::cout << std::endl; #endif @@ -425,7 +424,7 @@ void byteswap(unsigned char *array, int size, int value) { SCSIEncryptOptions::SCSIEncryptOptions() { cryptMode = CRYPTMODE_OFF; algorithmIndex = DEFAULT_ALGORITHM; - cryptoKey = ""; + cryptoKey = {}; CKOD = false; keyName = ""; rdmc = RDMC_DEFAULT; diff --git a/src/scsiencrypt.h b/src/scsiencrypt.h index 04cc488..c4f6e1c 100644 --- a/src/scsiencrypt.h +++ b/src/scsiencrypt.h @@ -53,6 +53,8 @@ GNU General Public License for more details. #include #endif +#include + #ifdef BYTE_ORDER #define STENC_BYTE_ORDER BYTE_ORDER #endif @@ -343,7 +345,7 @@ public: bool CKOD; int cryptMode; unsigned int algorithmIndex; - std::string cryptoKey; + std::vector cryptoKey; std::string keyName; SCSIEncryptOptions(); }; diff --git a/tests/Makefile.am b/tests/Makefile.am index 665706c..0883c01 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS=-I${top_srcdir}/src +AM_CPPFLAGS=-std=c++17 -I${top_srcdir}/src TESTS=scsi check_PROGRAMS=scsi scsi_SOURCES=catch.hpp scsi.cpp ${top_srcdir}/src/scsiencrypt.cpp diff --git a/tests/scsi.cpp b/tests/scsi.cpp index 5bdb5f6..520daf5 100644 --- a/tests/scsi.cpp +++ b/tests/scsi.cpp @@ -62,8 +62,12 @@ TEST_CASE("Enable encryption command", "[scsi]") { opt.cryptMode = CRYPTMODE_ON; opt.algorithmIndex = 1; - opt.cryptoKey = "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"s - "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"s; + opt.cryptoKey = { + 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; int pagelen = SCSIInitSDEPage(&opt, buffer); @@ -95,8 +99,12 @@ TEST_CASE("Enable encryption command with options", "[scsi]") { opt.CKOD = true; opt.cryptMode = CRYPTMODE_ON; opt.algorithmIndex = 1; - opt.cryptoKey = "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"s - "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"s; + opt.cryptoKey = { + 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; int pagelen = SCSIInitSDEPage(&opt, buffer); @@ -131,8 +139,12 @@ TEST_CASE("Enable encryption command with key name", "[scsi]") { opt.cryptMode = CRYPTMODE_ON; opt.algorithmIndex = 1; - opt.cryptoKey = "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"s - "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"s; + opt.cryptoKey = { + 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; int pagelen = SCSIInitSDEPage(&opt, buffer);