Add unit test coverage of stenc output (#67)
using stream in functions instead of directly writing to cout
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -31,6 +31,10 @@
|
||||
*.app
|
||||
src/stenc
|
||||
tests/scsi
|
||||
tests/output
|
||||
|
||||
# Other outputs
|
||||
man/stenc.1
|
||||
|
||||
# dist outputs
|
||||
*.tar.gz
|
||||
|
||||
162
src/main.cpp
162
src/main.cpp
@@ -99,6 +99,7 @@ static std::optional<std::vector<uint8_t>> key_from_hex_chars(const std::string&
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#if !defined(CATCH_CONFIG_MAIN)
|
||||
int main(int argc, const char **argv) {
|
||||
bitcheck bc;
|
||||
memset(&bc, 0, 1);
|
||||
@@ -341,6 +342,7 @@ int main(int argc, const char **argv) {
|
||||
errorOut("Turning encryption off for '" + tapeDrive + "' failed!");
|
||||
}
|
||||
}
|
||||
#endif // defined(CATCH_CONFIG_MAIN)
|
||||
|
||||
// exits to shell with an error message
|
||||
void errorOut(const std::string& message) {
|
||||
@@ -358,28 +360,30 @@ void showUsage() {
|
||||
"Type 'man stenc' for more information.\n";
|
||||
}
|
||||
|
||||
static void print_device_inquiry(std::ostream& os, const SCSI_PAGE_INQ *iresult)
|
||||
{
|
||||
os << std::left << std::setw(25) << "Vendor:";
|
||||
os.write((const char *)iresult->vender, 8);
|
||||
os.put('\n');
|
||||
os << std::left << std::setw(25) << "Product ID:";
|
||||
os.write((const char *)iresult->productID, 16);
|
||||
os.put('\n');
|
||||
os << std::left << std::setw(25) << "Product Revision:";
|
||||
os.write((const char *)iresult->productRev, 4);
|
||||
os.put('\n');
|
||||
}
|
||||
|
||||
void inquiryDrive(const 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;
|
||||
|
||||
print_device_inquiry(std::cout, iresult);
|
||||
delete iresult;
|
||||
}
|
||||
|
||||
void showDriveStatus(const std::string& tapeDrive, bool detail) {
|
||||
SSP_DES *opt = SSPGetDES(tapeDrive);
|
||||
if (opt == NULL)
|
||||
return;
|
||||
static void print_device_status(std::ostream& os, const SSP_DES *opt, bool detail)
|
||||
{
|
||||
std::string emode = "unknown";
|
||||
std::cout << std::left << std::setw(25) << "Drive Encryption:";
|
||||
os << std::left << std::setw(25) << "Drive Encryption:";
|
||||
if ((int)opt->des.encryptionMode == 0x2 && // encrypt
|
||||
(int)opt->des.decryptionMode == 0x2 // read only encrypted data
|
||||
)
|
||||
@@ -399,58 +403,58 @@ void showDriveStatus(const std::string& tapeDrive, bool detail) {
|
||||
)
|
||||
emode = "off";
|
||||
|
||||
std::cout << emode << "\n";
|
||||
os << emode << "\n";
|
||||
if (detail) {
|
||||
std::cout << std::left << std::setw(25) << "Drive Output:";
|
||||
os << 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";
|
||||
os << "Not decrypting\n";
|
||||
os << 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";
|
||||
os << "Not decrypting\n";
|
||||
os << 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";
|
||||
os << "Decrypting\n";
|
||||
os << std::setw(25) << " "
|
||||
<< "Unencrypted data not outputted\n";
|
||||
break;
|
||||
case 0x3:
|
||||
std::cout << "Decrypting\n";
|
||||
std::cout << std::setw(25) << " "
|
||||
<< "Unencrypted data outputted\n";
|
||||
os << "Decrypting\n";
|
||||
os << std::setw(25) << " "
|
||||
<< "Unencrypted data outputted\n";
|
||||
break;
|
||||
default:
|
||||
std::cout << "Unknown '0x" << std::hex << (int)opt->des.decryptionMode
|
||||
<< "' \n";
|
||||
os << "Unknown '0x" << std::hex << (int)opt->des.decryptionMode
|
||||
<< "' \n";
|
||||
break;
|
||||
}
|
||||
std::cout << std::setw(25) << "Drive Input:";
|
||||
os << std::setw(25) << "Drive Input:";
|
||||
switch ((int)opt->des.encryptionMode) {
|
||||
case 0x0:
|
||||
std::cout << "Not encrypting\n";
|
||||
os << "Not encrypting\n";
|
||||
break;
|
||||
case 0x2:
|
||||
std::cout << "Encrypting\n";
|
||||
os << "Encrypting\n";
|
||||
break;
|
||||
default:
|
||||
std::cout << "Unknown result '0x" << std::hex
|
||||
os << "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";
|
||||
os << std::setw(25) << " "
|
||||
<< "Protecting from raw read\n";
|
||||
}
|
||||
|
||||
std::cout << std::setw(25) << "Key Instance Counter:" << std::dec
|
||||
os << 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";
|
||||
os << std::setw(25) << "Encryption Algorithm:" << std::hex
|
||||
<< (int)opt->des.algorithmIndex << "\n";
|
||||
}
|
||||
}
|
||||
if (opt->kads.size() > 0) {
|
||||
@@ -460,60 +464,65 @@ void showDriveStatus(const std::string& tapeDrive, bool detail) {
|
||||
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,
|
||||
os << std::setw(25) << lbl.str();
|
||||
os.write((const char *)&opt->kads[i].descriptor,
|
||||
BSSHORT(opt->kads[i].descriptorLength));
|
||||
std::cout << std::endl;
|
||||
os.put('\n');
|
||||
break;
|
||||
case KAD_TYPE_AKAD:
|
||||
lbl << "aKAD): ";
|
||||
std::cout << std::setw(25) << lbl.str();
|
||||
std::cout.write((const char *)&opt->kads[i].descriptor,
|
||||
os << std::setw(25) << lbl.str();
|
||||
os.write((const char *)&opt->kads[i].descriptor,
|
||||
BSSHORT(opt->kads[i].descriptorLength));
|
||||
std::cout << std::endl;
|
||||
os.put('\n');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void showDriveStatus(const std::string& tapeDrive, bool detail) {
|
||||
SSP_DES *opt = SSPGetDES(tapeDrive);
|
||||
if (opt == NULL)
|
||||
return;
|
||||
|
||||
print_device_status(std::cout, opt, detail);
|
||||
delete opt;
|
||||
}
|
||||
|
||||
void showVolumeStatus(const std::string& tapeDrive) {
|
||||
SSP_NBES *opt = SSPGetNBES(tapeDrive, true);
|
||||
if (opt == NULL)
|
||||
return;
|
||||
static void print_volume_status(std::ostream& os, const SSP_NBES *opt)
|
||||
{
|
||||
if (opt->nbes.compressionStatus != 0) {
|
||||
std::cout << std::left << std::setw(25) << "Volume Compressed:";
|
||||
os << std::left << std::setw(25) << "Volume Compressed:";
|
||||
switch (opt->nbes.compressionStatus) {
|
||||
case 0x00:
|
||||
std::cout << "Drive cannot determine\n";
|
||||
os << "Drive cannot determine\n";
|
||||
break;
|
||||
default:
|
||||
std::cout << "Unknown result '" << std::hex
|
||||
<< (int)opt->nbes.compressionStatus << "'\n";
|
||||
os << "Unknown result '" << std::hex
|
||||
<< (int)opt->nbes.compressionStatus << "'\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::cout << std::left << std::setw(25) << "Volume Encryption:";
|
||||
os << std::left << std::setw(25) << "Volume Encryption:";
|
||||
switch ((int)opt->nbes.encryptionStatus) {
|
||||
case 0x01:
|
||||
std::cout << "Unable to determine\n";
|
||||
os << "Unable to determine\n";
|
||||
break;
|
||||
case 0x02:
|
||||
std::cout << "Logical block is not a logical block\n";
|
||||
os << "Logical block is not a logical block\n";
|
||||
break;
|
||||
case 0x03:
|
||||
std::cout << "Not encrypted\n";
|
||||
os << "Not encrypted\n";
|
||||
break;
|
||||
case 0x05:
|
||||
std::cout << "Encrypted and able to decrypt\n";
|
||||
os << "Encrypted and able to decrypt\n";
|
||||
if (opt->nbes.RDMDS == 1)
|
||||
std::cout << std::left << std::setw(25)
|
||||
<< " Protected from raw read\n";
|
||||
os << 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";
|
||||
os << "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;
|
||||
@@ -521,35 +530,42 @@ void showVolumeStatus(const std::string& tapeDrive) {
|
||||
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,
|
||||
os << std::setw(25) << lbl.str();
|
||||
os.write((const char *)&opt->kads[i].descriptor,
|
||||
BSSHORT(opt->kads[i].descriptorLength));
|
||||
std::cout << std::endl;
|
||||
os.put('\n');
|
||||
break;
|
||||
case KAD_TYPE_AKAD:
|
||||
lbl << "aKAD): ";
|
||||
std::cout << std::setw(25) << lbl.str();
|
||||
std::cout.write((const char *)&opt->kads[i].descriptor,
|
||||
os << std::setw(25) << lbl.str();
|
||||
os.write((const char *)&opt->kads[i].descriptor,
|
||||
BSSHORT(opt->kads[i].descriptorLength));
|
||||
std::cout << std::endl;
|
||||
os.put('\n');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opt->nbes.RDMDS == 1)
|
||||
std::cout << std::left << std::setw(25) << " Protected from raw read\n";
|
||||
os << std::left << std::setw(25) << " Protected from raw read\n";
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cout << "Unknown result '" << std::hex
|
||||
<< (int)opt->nbes.encryptionStatus << "'\n";
|
||||
os << "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";
|
||||
os << std::left << std::setw(25)
|
||||
<< "Volume Algorithm:" << (int)opt->nbes.algorithmIndex << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void showVolumeStatus(const std::string& tapeDrive) {
|
||||
SSP_NBES *opt = SSPGetNBES(tapeDrive, true);
|
||||
if (opt == NULL)
|
||||
return;
|
||||
|
||||
print_volume_status(std::cout, opt);
|
||||
delete opt;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
AM_CPPFLAGS=-std=c++17 -I${top_srcdir}/src
|
||||
TESTS=scsi
|
||||
check_PROGRAMS=scsi
|
||||
TESTS=scsi output
|
||||
check_PROGRAMS=scsi output
|
||||
scsi_SOURCES=catch.hpp scsi.cpp ${top_srcdir}/src/scsiencrypt.cpp
|
||||
output_SOURCES=catch.hpp output.cpp ${top_srcdir}/src/scsiencrypt.cpp
|
||||
|
||||
112
tests/output.cpp
Normal file
112
tests/output.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "config.h"
|
||||
#include "main.cpp"
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
/**
|
||||
* Compare the output of stenc given device responses
|
||||
*
|
||||
* These tests check the representation and interpretation of raw device data
|
||||
* and that the program output accurately reports the meaning of the data.
|
||||
*/
|
||||
TEST_CASE("Test SCSI inquiry output", "[output]")
|
||||
{
|
||||
const uint8_t response[] {
|
||||
0x01, 0x80, 0x00, 0x02, 0x5b, 0x00, 0x00, 0x02,
|
||||
0x41, 0x43, 0x4d, 0x45, 0x20, 0x20, 0x20, 0x20,
|
||||
0x55, 0x6c, 0x74, 0x72, 0x69, 0x75, 0x6d, 0x2d,
|
||||
0x31, 0x30, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20,
|
||||
0x31, 0x32, 0x33, 0x34, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
// note: fixed width strings in output
|
||||
const std::string expected_output {"\
|
||||
Vendor: ACME \n\
|
||||
Product ID: Ultrium-1000 \n\
|
||||
Product Revision: 1234\n"s};
|
||||
std::ostringstream oss;
|
||||
print_device_inquiry(oss, reinterpret_cast<const SCSI_PAGE_INQ*>(response));
|
||||
REQUIRE(oss.str() == expected_output);
|
||||
}
|
||||
|
||||
TEST_CASE("SCSI get device encryption status output 1", "[output]")
|
||||
{
|
||||
const uint8_t page[] {
|
||||
0x00, 0x20, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
const std::string expected_output {"\
|
||||
Drive Encryption: off\n\
|
||||
Drive Output: Not decrypting\n\
|
||||
Raw encrypted data not outputted\n\
|
||||
Drive Input: Not encrypting\n\
|
||||
Key Instance Counter: 0\n"s};
|
||||
std::ostringstream oss;
|
||||
print_device_status(oss, std::make_unique<SSP_DES>(reinterpret_cast<const SSP_PAGE_BUFFER*>(page)).get(), true);
|
||||
REQUIRE(oss.str() == expected_output);
|
||||
}
|
||||
|
||||
TEST_CASE("SCSI get device encryption status output 2", "[output]")
|
||||
{
|
||||
const uint8_t page[] {
|
||||
0x00, 0x20, 0x00, 0x24, 0x42, 0x02, 0x02, 0x01,
|
||||
0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0c, 0x48, 0x65, 0x6c, 0x6c,
|
||||
0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
|
||||
};
|
||||
const std::string expected_output {"\
|
||||
Drive Encryption: on\n\
|
||||
Drive Output: Decrypting\n\
|
||||
Unencrypted data not outputted\n\
|
||||
Drive Input: Encrypting\n\
|
||||
Key Instance Counter: 1\n\
|
||||
Encryption Algorithm: 1\n\
|
||||
Drive Key Desc.(uKAD): Hello world!\n"s};
|
||||
std::ostringstream oss;
|
||||
print_device_status(oss, std::make_unique<SSP_DES>(reinterpret_cast<const SSP_PAGE_BUFFER*>(page)).get(), true);
|
||||
REQUIRE(oss.str() == expected_output);
|
||||
}
|
||||
|
||||
TEST_CASE("Test SCSI get next block encryption status output 1", "[output]")
|
||||
{
|
||||
const uint8_t page[] {
|
||||
0x00, 0x21, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
};
|
||||
const std::string expected_output {"\
|
||||
Volume Encryption: Not encrypted\n"s};
|
||||
std::ostringstream oss;
|
||||
print_volume_status(oss, std::make_unique<SSP_NBES>(reinterpret_cast<const SSP_PAGE_BUFFER*>(page)).get());
|
||||
REQUIRE(oss.str() == expected_output);
|
||||
}
|
||||
|
||||
TEST_CASE("Test SCSI get next block encryption status output 2", "[output]")
|
||||
{
|
||||
const uint8_t page[] {
|
||||
0x00, 0x21, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00,
|
||||
0x00, 0x01, 0x00, 0x0c, 0x48, 0x65, 0x6c, 0x6c,
|
||||
0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
|
||||
};
|
||||
const std::string expected_output {"\
|
||||
Volume Encryption: Encrypted and able to decrypt\n\
|
||||
Volume Algorithm: 1\n"s};
|
||||
std::ostringstream oss;
|
||||
print_volume_status(oss, std::make_unique<SSP_NBES>(reinterpret_cast<const SSP_PAGE_BUFFER*>(page)).get());
|
||||
REQUIRE(oss.str() == expected_output);
|
||||
}
|
||||
Reference in New Issue
Block a user