mirror of
https://github.com/scsitape/stenc.git
synced 2026-04-24 10:30:39 +00:00
Closes: https://github.com/scsitape/stenc/issues/31 Co-authored-by: Jonas Stein <news@jonasstein.de>
630 lines
20 KiB
C++
630 lines
20 KiB
C++
/*
|
|
stenc - program to set and retrieve hardware based encryption
|
|
options from certain SCSI devices (i.e. LTO4 Tape drives)
|
|
|
|
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.
|
|
*/
|
|
#include "scsiencrypt.h"
|
|
|
|
#include <charconv>
|
|
#include <config.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <ios>
|
|
#include <iostream>
|
|
#include <optional>
|
|
#include <stdint.h>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <syslog.h>
|
|
#include <sys/mtio.h>
|
|
#include <sys/stat.h>
|
|
#include <termios.h>
|
|
#include <vector>
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
|
|
|
|
typedef struct {
|
|
#if STENC_BIG_ENDIAN == 0
|
|
unsigned char bit1 : 1;
|
|
unsigned char bit2 : 1;
|
|
unsigned char bit3 : 1;
|
|
unsigned char bit4 : 1;
|
|
unsigned char bit5 : 1;
|
|
unsigned char bit6 : 1;
|
|
unsigned char bit7 : 1;
|
|
unsigned char bit8 : 1;
|
|
#else
|
|
unsigned char bit8 : 1;
|
|
unsigned char bit7 : 1;
|
|
unsigned char bit6 : 1;
|
|
unsigned char bit5 : 1;
|
|
unsigned char bit4 : 1;
|
|
unsigned char bit3 : 1;
|
|
unsigned char bit2 : 1;
|
|
unsigned char bit1 : 1;
|
|
#endif
|
|
} bitcheck;
|
|
|
|
using namespace std;
|
|
void showUsage();
|
|
void errorOut(std::string const message);
|
|
void inquiryDrive(std::string tapeDevice);
|
|
void showDriveStatus(std::string tapeDevice, bool detail);
|
|
void showVolumeStatus(std::string tapeDevice);
|
|
std::string randomKey(int length);
|
|
void echo(bool);
|
|
|
|
static std::optional<std::vector<uint8_t>> key_from_hex_chars(const std::string& s)
|
|
{
|
|
auto it = s.data();
|
|
std::vector<uint8_t> 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);
|
|
bc.bit2 = 1;
|
|
bc.bit5 = 1;
|
|
unsigned char check;
|
|
memcpy(&check, &bc, 1);
|
|
|
|
switch ((int)check) {
|
|
case 0x12:
|
|
// this is good
|
|
break;
|
|
case 0x48:
|
|
#if STENC_BIG_ENDIAN == 1
|
|
errorOut("Swapped bit ordering detected(BI). Program needs to be "
|
|
"configured without the --enable-swapendian option in order to "
|
|
"function properly on your system");
|
|
#else
|
|
errorOut("Swapped bit ordering detected(LI). Program needs to be "
|
|
"configured with the --enable-swapendian option in order to "
|
|
"function properly on your system");
|
|
#endif
|
|
break;
|
|
default:
|
|
std::cerr << "Unknown bit check result " << std::hex << check << "\n";
|
|
errorOut("Exiting program because it will not run properly");
|
|
break;
|
|
}
|
|
|
|
std::string tapeDrive;
|
|
int action = 0; // 0 = status, 1 =setting param, 2 = generating key
|
|
std::string keyFile, keyDesc;
|
|
int keyLength = 0;
|
|
bool detail = false;
|
|
SCSIEncryptOptions drvOptions;
|
|
|
|
// First load all of the options
|
|
for (int i = 1; i < argc; i++) {
|
|
std::string thisCmd = argv[i];
|
|
std::string nextCmd = "";
|
|
if (i + 1 < argc) {
|
|
if (strncmp(argv[i + 1], "-", 1) != 0)
|
|
nextCmd = argv[i + 1];
|
|
}
|
|
if (thisCmd == "--version") {
|
|
std::cout << "stenc v" << VERSION << " - SCSI Tape Encryption Manager\n";
|
|
std::cout << "https://github.com/scsitape/stenc \n";
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
if (thisCmd == "-g") { // Check if the help flag was passed. If it was,
|
|
// show usage and exit
|
|
if (nextCmd == "")
|
|
errorOut("Key size must be specified when using -g");
|
|
i++; // skip the next argument
|
|
keyLength = std::atoi(nextCmd.c_str());
|
|
if (keyLength % 8 != 0)
|
|
errorOut("Key size must be divisible by 8");
|
|
keyLength = keyLength / 8;
|
|
if (keyLength > SSP_KEY_LENGTH) {
|
|
std::cout << "Warning: Keys over " << (SSP_KEY_LENGTH * 8)
|
|
<< " bits cannot be used by this program! \n";
|
|
}
|
|
action = 2; // generating key
|
|
} else if (thisCmd == "-e") {
|
|
if (nextCmd == "")
|
|
errorOut("Key file not specified after -k option");
|
|
if (nextCmd == "on")
|
|
drvOptions.cryptMode = CRYPTMODE_ON; // encrypt, read only encrypted
|
|
// data
|
|
else if (nextCmd == "mixed")
|
|
drvOptions.cryptMode =
|
|
CRYPTMODE_MIXED; // encrypt, read encrypted and unencrypted data
|
|
else if (nextCmd == "rawread")
|
|
drvOptions.cryptMode =
|
|
CRYPTMODE_RAWREAD; // encrypt, read encrypted and unencrypted data
|
|
else if (nextCmd == "off")
|
|
drvOptions.cryptMode =
|
|
CRYPTMODE_OFF; // encrypt, read encrypted and unencrypted data
|
|
else
|
|
errorOut("Unknown encryption mode '" + nextCmd +
|
|
"'"); // encrypt, read encrypted and unencrypted data
|
|
i++; // skip the next argument
|
|
action = 1;
|
|
} else if (thisCmd == "-f") {
|
|
if (nextCmd == "")
|
|
errorOut("Device not specified after -f option.");
|
|
tapeDrive = nextCmd; // set the tape drive
|
|
i++; // skip the next argument
|
|
} else if (thisCmd == "-k") {
|
|
if (nextCmd == "")
|
|
errorOut("Key file not specified after -k option");
|
|
keyFile = nextCmd; // set the key file
|
|
i++; // skip the next argument
|
|
} else if (thisCmd == "-kd") {
|
|
if (nextCmd == "")
|
|
errorOut("Key description not specified after the -kd option");
|
|
keyDesc = nextCmd; // set the key file
|
|
if (keyDesc.size() > SSP_UKAD_LENGTH) {
|
|
errorOut("Key description too long!");
|
|
}
|
|
i++; // skip the next argument
|
|
} else if (thisCmd == "--protect") {
|
|
if (drvOptions.rdmc == RDMC_UNPROTECT)
|
|
errorOut("'--protect' cannot be specified at the same time as "
|
|
"'--unprotect'");
|
|
drvOptions.rdmc = RDMC_PROTECT;
|
|
} else if (thisCmd == "--unprotect") {
|
|
if (drvOptions.rdmc == RDMC_PROTECT)
|
|
errorOut("'--unprotect' cannot be specified at the same time as "
|
|
"'--protect'");
|
|
drvOptions.rdmc = RDMC_UNPROTECT;
|
|
} else if (thisCmd == "--ckod") {
|
|
drvOptions.CKOD = true;
|
|
} else if (thisCmd == "--detail") {
|
|
detail = true;
|
|
} else if (thisCmd == "-a") {
|
|
if (nextCmd == "")
|
|
errorOut("You must specify a numeric algorithm index when using the -a "
|
|
"flag");
|
|
drvOptions.algorithmIndex = std::atoi(nextCmd.c_str());
|
|
i++; // skip the next argument
|
|
} else {
|
|
errorOut("Unknown command '" + thisCmd + "'");
|
|
}
|
|
}
|
|
|
|
if (action == 2) { // generate key
|
|
if (keyFile == "") {
|
|
errorOut("Specify file to save into with the -k argument.");
|
|
}
|
|
|
|
std::string const newkey = randomKey(keyLength);
|
|
std::ofstream kf{};
|
|
umask(077); // make sure that no one else can read the new key file
|
|
kf.open(keyFile.c_str(), std::ios::trunc);
|
|
if (!kf.is_open()) {
|
|
errorOut("Could not open '" + keyFile + "' for writing.");
|
|
}
|
|
kf << newkey << keyDesc;
|
|
kf.close();
|
|
std::cout << "Random key saved into '" << keyFile << "'\n";
|
|
chmod(keyFile.c_str(), 0600);
|
|
std::cout << "Permissions of keyfile set to 600\n";
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
// select device from env variable or system default if not given with -f
|
|
if (tapeDrive.empty()) {
|
|
const char *env_tape = getenv("TAPE");
|
|
if (env_tape != nullptr) {
|
|
tapeDrive = env_tape;
|
|
} else {
|
|
tapeDrive = DEFTAPE;
|
|
}
|
|
}
|
|
if (drvOptions.cryptMode == CRYPTMODE_RAWREAD &&
|
|
drvOptions.rdmc == RDMC_PROTECT) {
|
|
errorOut(
|
|
"'--protect' is not valid when setting encryption mode to 'rawread'");
|
|
}
|
|
|
|
if (getuid() != 0) {
|
|
errorOut("You must be root to read or set encryption options on a drive!");
|
|
}
|
|
openlog("stenc", LOG_CONS, LOG_USER);
|
|
|
|
if (action == 0) {
|
|
std::cout << "Status for " << tapeDrive << "\n"
|
|
<< "--------------------------------------------------\n";
|
|
|
|
if (detail)
|
|
inquiryDrive(tapeDrive);
|
|
showDriveStatus(tapeDrive, detail);
|
|
if (detail)
|
|
showVolumeStatus(tapeDrive);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
if (drvOptions.cryptMode != CRYPTMODE_OFF) {
|
|
if (keyFile == "") {
|
|
std::string p1;
|
|
std::string p2;
|
|
bool done = false;
|
|
while (!done) {
|
|
std::cout << "Enter key in hex format: ";
|
|
echo(false);
|
|
getline(std::cin, p1);
|
|
echo(true);
|
|
std::cout << "\nRe-enter key in hex format: ";
|
|
echo(false);
|
|
getline(std::cin, p2);
|
|
echo(true);
|
|
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 {
|
|
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 {
|
|
std::cout << "Invalid key!\n";
|
|
}
|
|
}
|
|
}
|
|
drvOptions.keyName = keyDesc;
|
|
|
|
} else {
|
|
// set keyInput here
|
|
std::string keyInput;
|
|
std::ifstream myfile(keyFile.c_str());
|
|
if (myfile.is_open()) {
|
|
getline(myfile, keyInput);
|
|
getline(myfile, keyDesc);
|
|
myfile.close();
|
|
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");
|
|
}
|
|
}
|
|
|
|
// Write the options to the tape device
|
|
std::cout << "Turning "
|
|
<< ((drvOptions.cryptMode != CRYPTMODE_OFF) ? "on" : "off")
|
|
<< " encryption on device '" << tapeDrive << "'..." << std::endl;
|
|
bool res = SCSIWriteEncryptOptions(tapeDrive, &drvOptions);
|
|
if (res) {
|
|
|
|
SSP_DES *opt = SSPGetDES(tapeDrive);
|
|
if (drvOptions.cryptMode != CRYPTMODE_OFF && opt->des.encryptionMode != 2) {
|
|
errorOut("Turning encryption on for '" + tapeDrive + "' failed!");
|
|
}
|
|
if (drvOptions.cryptMode == CRYPTMODE_OFF && opt->des.encryptionMode != 0) {
|
|
errorOut("Turning encryption off for '" + tapeDrive + "' failed!");
|
|
}
|
|
delete opt;
|
|
|
|
if (drvOptions.cryptMode != CRYPTMODE_OFF) {
|
|
std::stringstream msg;
|
|
msg << "Encryption turned on for device '" << tapeDrive << "'. ";
|
|
if (!drvOptions.keyName.empty()) {
|
|
msg << "Key Descriptor: '" << drvOptions.keyName << "'";
|
|
}
|
|
msg << " Key Instance: " << std::dec << BSLONG(opt->des.keyInstance)
|
|
<< std::endl;
|
|
|
|
syslog(LOG_NOTICE, "%s", msg.str().c_str());
|
|
} else {
|
|
std::stringstream msg{};
|
|
|
|
msg << "Encryption turned off for device '" << tapeDrive << "'.";
|
|
msg << " Key Instance: " << std::dec << BSLONG(opt->des.keyInstance)
|
|
<< std::endl;
|
|
|
|
syslog(LOG_NOTICE, "%s", msg.str().c_str());
|
|
}
|
|
std::cout << "Success! See system logs for a key change audit log.\n";
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
if (drvOptions.cryptMode != CRYPTMODE_OFF) {
|
|
errorOut("Turning encryption on for '" + tapeDrive + "' failed!");
|
|
} else {
|
|
errorOut("Turning encryption off for '" + tapeDrive + "' failed!");
|
|
}
|
|
}
|
|
// exits to shell with an error message
|
|
|
|
void errorOut(std::string const message) {
|
|
std::cerr << "Error: " << message << "\n";
|
|
showUsage();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// shows the command usage
|
|
void showUsage() {
|
|
std::cout
|
|
<< "Usage: stenc --version | -g <length> -k <file> [-kd <description>] | "
|
|
"-f <device> [--detail] [-e <on/mixed/rawread/off> [-k <file>] "
|
|
"[-kd <description>] [-a <index>] [--protect | --unprotect] [--ckod] ]\n\n"
|
|
"Type 'man stenc' for more information.\n";
|
|
}
|
|
void inquiryDrive(std::string tapeDevice) {
|
|
// todo: std::cout should not be used outside main()
|
|
SCSI_PAGE_INQ *const iresult = SCSIGetInquiry(tapeDevice);
|
|
std::cout << std::left << std::setw(25) << "Device Mfg:";
|
|
std::cout.write((const char *)iresult->vender, 8);
|
|
std::cout << std::endl;
|
|
std::cout << std::left << std::setw(25) << "Product ID:";
|
|
std::cout.write((const char *)iresult->productID, 16);
|
|
std::cout << std::endl;
|
|
std::cout << std::left << std::setw(25) << "Product Revision:";
|
|
std::cout.write((const char *)iresult->productRev, 4);
|
|
std::cout << std::endl;
|
|
|
|
delete iresult;
|
|
}
|
|
|
|
void showDriveStatus(std::string tapeDrive, bool detail) {
|
|
SSP_DES *opt = SSPGetDES(tapeDrive);
|
|
if (opt == NULL)
|
|
return;
|
|
std::string emode = "unknown";
|
|
std::cout << std::left << std::setw(25) << "Drive Encryption:";
|
|
if ((int)opt->des.encryptionMode == 0x2 && // encrypt
|
|
(int)opt->des.decryptionMode == 0x2 // read only encrypted data
|
|
)
|
|
emode = "on";
|
|
if ((int)opt->des.encryptionMode == 0x2 && // encrypt
|
|
(int)opt->des.decryptionMode == 0x3 // read encrypted and unencrypted
|
|
)
|
|
emode = "mixed";
|
|
|
|
if ((int)opt->des.encryptionMode == 0x2 && // encrypt
|
|
(int)opt->des.decryptionMode == 0x1 // read encrypted and unencrypted
|
|
)
|
|
emode = "rawread";
|
|
|
|
if ((int)opt->des.encryptionMode == 0x0 && // encrypt
|
|
(int)opt->des.decryptionMode == 0x0 // read encrypted and unencrypted
|
|
)
|
|
emode = "off";
|
|
|
|
std::cout << emode << "\n";
|
|
if (detail) {
|
|
std::cout << std::left << std::setw(25) << "Drive Output:";
|
|
switch ((int)opt->des.decryptionMode) {
|
|
case 0x0:
|
|
std::cout << "Not decrypting\n";
|
|
std::cout << std::setw(25) << " "
|
|
<< "Raw encrypted data not outputted\n";
|
|
break;
|
|
case 0x1:
|
|
std::cout << "Not decrypting\n";
|
|
std::cout << std::setw(25) << " "
|
|
<< "Raw encrypted data outputted\n";
|
|
break;
|
|
case 0x2:
|
|
std::cout << "Decrypting\n";
|
|
std::cout << std::setw(25) << " "
|
|
<< "Unencrypted data not outputted\n";
|
|
break;
|
|
case 0x3:
|
|
std::cout << "Decrypting\n";
|
|
std::cout << std::setw(25) << " "
|
|
<< "Unencrypted data outputted\n";
|
|
break;
|
|
default:
|
|
std::cout << "Unknown '0x" << std::hex << (int)opt->des.decryptionMode
|
|
<< "' \n";
|
|
break;
|
|
}
|
|
std::cout << std::setw(25) << "Drive Input:";
|
|
switch ((int)opt->des.encryptionMode) {
|
|
case 0x0:
|
|
std::cout << "Not encrypting\n";
|
|
break;
|
|
case 0x2:
|
|
std::cout << "Encrypting\n";
|
|
break;
|
|
default:
|
|
std::cout << "Unknown result '0x" << std::hex
|
|
<< (int)opt->des.encryptionMode << "'\n";
|
|
break;
|
|
}
|
|
if (opt->des.RDMD == 1) {
|
|
std::cout << std::setw(25) << " "
|
|
<< "Protecting from raw read\n";
|
|
}
|
|
|
|
std::cout << std::setw(25) << "Key Instance Counter:" << std::dec
|
|
<< BSLONG(opt->des.keyInstance) << "\n";
|
|
if (opt->des.algorithmIndex != 0) {
|
|
std::cout << std::setw(25) << "Encryption Algorithm:" << std::hex
|
|
<< (int)opt->des.algorithmIndex << "\n";
|
|
}
|
|
}
|
|
if (opt->kads.size() > 0) {
|
|
for (unsigned int i = 0; i < opt->kads.size(); i++) {
|
|
std::stringstream lbl{};
|
|
lbl << "Drive Key Desc.(";
|
|
switch (opt->kads[i].type) {
|
|
case KAD_TYPE_UKAD:
|
|
lbl << "uKAD): ";
|
|
std::cout << std::setw(25) << lbl.str();
|
|
std::cout.write((const char *)&opt->kads[i].descriptor,
|
|
BSSHORT(opt->kads[i].descriptorLength));
|
|
std::cout << std::endl;
|
|
break;
|
|
case KAD_TYPE_AKAD:
|
|
lbl << "aKAD): ";
|
|
std::cout << std::setw(25) << lbl.str();
|
|
std::cout.write((const char *)&opt->kads[i].descriptor,
|
|
BSSHORT(opt->kads[i].descriptorLength));
|
|
std::cout << std::endl;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
delete opt;
|
|
}
|
|
|
|
void showVolumeStatus(std::string tapeDrive) {
|
|
SSP_NBES *opt = SSPGetNBES(tapeDrive, true);
|
|
if (opt == NULL)
|
|
return;
|
|
if (opt->nbes.compressionStatus != 0) {
|
|
std::cout << std::left << std::setw(25) << "Volume Compressed:";
|
|
switch (opt->nbes.compressionStatus) {
|
|
case 0x00:
|
|
std::cout << "Drive cannot determine\n";
|
|
break;
|
|
default:
|
|
std::cout << "Unknown result '" << std::hex
|
|
<< (int)opt->nbes.compressionStatus << "'\n";
|
|
break;
|
|
}
|
|
}
|
|
std::cout << std::left << std::setw(25) << "Volume Encryption:";
|
|
switch ((int)opt->nbes.encryptionStatus) {
|
|
case 0x01:
|
|
std::cout << "Unable to determine\n";
|
|
break;
|
|
case 0x02:
|
|
std::cout << "Logical block is not a logical block\n";
|
|
break;
|
|
case 0x03:
|
|
std::cout << "Not encrypted\n";
|
|
break;
|
|
case 0x05:
|
|
std::cout << "Encrypted and able to decrypt\n";
|
|
if (opt->nbes.RDMDS == 1)
|
|
std::cout << std::left << std::setw(25)
|
|
<< " Protected from raw read\n";
|
|
break;
|
|
case 0x06:
|
|
std::cout << "Encrypted, but unable to decrypt due to invalid key.\n";
|
|
if (opt->kads.size() > 0) {
|
|
for (unsigned int i = 0; i < opt->kads.size(); i++) {
|
|
std::stringstream lbl;
|
|
lbl << "Volume Key Desc.(";
|
|
switch (opt->kads[i].type) {
|
|
case KAD_TYPE_UKAD:
|
|
lbl << "uKAD): ";
|
|
std::cout << std::setw(25) << lbl.str();
|
|
std::cout.write((const char *)&opt->kads[i].descriptor,
|
|
BSSHORT(opt->kads[i].descriptorLength));
|
|
std::cout << std::endl;
|
|
break;
|
|
case KAD_TYPE_AKAD:
|
|
lbl << "aKAD): ";
|
|
std::cout << std::setw(25) << lbl.str();
|
|
std::cout.write((const char *)&opt->kads[i].descriptor,
|
|
BSSHORT(opt->kads[i].descriptorLength));
|
|
std::cout << std::endl;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (opt->nbes.RDMDS == 1)
|
|
std::cout << std::left << std::setw(25) << " Protected from raw read\n";
|
|
break;
|
|
|
|
default:
|
|
std::cout << "Unknown result '" << std::hex
|
|
<< (int)opt->nbes.encryptionStatus << "'\n";
|
|
break;
|
|
}
|
|
if (opt->nbes.algorithmIndex != 0) {
|
|
std::cout << std::left << std::setw(25)
|
|
<< "Volume Algorithm:" << (int)opt->nbes.algorithmIndex << "\n";
|
|
}
|
|
|
|
delete opt;
|
|
}
|
|
|
|
void echo(bool on = true) {
|
|
struct termios settings {};
|
|
tcgetattr(STDIN_FILENO, &settings);
|
|
settings.c_lflag =
|
|
on ? (settings.c_lflag | ECHO) : (settings.c_lflag & ~(ECHO));
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &settings);
|
|
}
|
|
|
|
std::string randomKey(int length) {
|
|
unsigned char rnd;
|
|
std::stringstream retval{};
|
|
std::ifstream random{};
|
|
|
|
// Under Linux and AIX /dev/random provides much more cryptographically secure
|
|
// random output than rand()
|
|
random.open("/dev/random", std::ios::in | std::ios::binary);
|
|
if (random.is_open()) {
|
|
for (int i = 0; i < length; i++) {
|
|
random.read(reinterpret_cast<char *>(&rnd), 1);
|
|
retval << std::hex << std::setfill('0') << setw(2) << static_cast<int>(rnd);
|
|
}
|
|
random.close();
|
|
} else {
|
|
std::cout << "Enter random keys on the keyboard to seed the generator.\n"
|
|
"End by pressing enter...\n";
|
|
|
|
double check = 0;
|
|
char c = 0;
|
|
echo(false);
|
|
while (c != 10) {
|
|
check += (int)c;
|
|
c = getchar();
|
|
}
|
|
echo(true);
|
|
srand(time(NULL) + (int)check);
|
|
for (int i = 0; i < length; i++) {
|
|
retval << std::hex << (std::rand() % 256);
|
|
}
|
|
}
|
|
retval << std::endl;
|
|
return (retval.str());
|
|
}
|