/* $Id: TestPKI.cpp $
* Last updated:
* $Date: 2023-01-01 03:23:00 $
* $Version: 21.0.0 $
*/
// Some tests using the CryptoSys PKI C++ (STL) interface.
// Requires certain files to exist in the current working directory
/******************************* LICENSE ***********************************
* Copyright (C) 2022-23 David Ireland, DI Management Services Pty Limited.
* All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
* The code in this module is licensed under the terms of the MIT license.
* For a copy, see <http://opensource.org/licenses/MIT>
****************************************************************************
*/
#include <string>
#include <cwchar>
#include <iostream>
#include <clocale>
#include <assert.h>
#include <fstream>
#include <sstream>
#include <sys/stat.h> // For the stat() function used in file_length()
#include "dipki.hpp"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h> // For ``SetConsoleOutputCP(CP_UTF8);``
#endif
using std::cout;
using std::endl;
// Global vars set by command-line args
static bool doPrompt = false;
// UTILITY FUNCTIONS
/** Read in a file to a string in one go. */
static std::string read_text_file(const std::string& path) {
std::ifstream in(path);
auto ss = std::ostringstream{};
ss << in.rdbuf();
return ss.str();
}
/** Read in a binary file. */
static dipki::bvec_t read_binary_file(const std::string& path) {
std::ifstream stream(path, std::ios::in | std::ios::binary);
dipki::bvec_t bv((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
return bv;
}
/** Write string to a text file */
static void write_text_file(const std::string& path, const std::string& s) {
std::ofstream f(path);
f << s;
f.close();
}
/** Write to a binary file file */
static void write_binary_file(const std::string& path, const dipki::bvec_t& bv) {
std::ofstream f(path, std::ios::binary);
f.write(reinterpret_cast<const char*>(bv.data()), bv.size());
f.close();
}
/** Read just the first line from a text file */
static std::string read_first_line(const std::string& path) {
std::ifstream in(path);
std::string line;
std::getline(in, line);
return line;
}
/** Get size of file */
static long file_length(const std::string& path) {
struct stat stat_buf;
int rc = stat(path.c_str(), &stat_buf);
return rc == 0 ? stat_buf.st_size : -1;
}
/** Format a boolean value */
inline const std::string bool2str(bool b)
{
return b ? "true" : "false";
}
// THE TESTS...
static void test_Cnv() {
dipki::bvec_t bv;
cout << "\nTESTING Cnv..." << endl;
cout << dipki::Cnv::ToHex("abc") << endl;
cout << dipki::Cnv::ToHex(dipki::Cnv::FromHex("616263")) << endl;
cout << dipki::Cnv::ToHex(dipki::Cnv::FromBase64("/ty6mHZUMhA=")) << endl;
cout << dipki::Cnv::ToBase64(dipki::Cnv::FromBase64("/ty6mHZUMhA=")) << endl;
cout << dipki::Cnv::ToBase64("abc") << endl;
// Punct and spaces are ignored
bv = dipki::Cnv::FromHex(" 61 : 62 :63 ");
cout << "bv.size=" << bv.size() << endl;
cout << "BEGIN EXPECTING ERRORS..." << endl;
try {
bv = dipki::Cnv::FromHex("BADHEXXX");
cout << "bv.size=" << bv.size() << endl;
}
catch (std::exception& e) {
cout << e.what() << endl;
}
try {
bv = dipki::Cnv::FromBase64("BADBASE64%#@");
cout << "bv.size=" << bv.size() << endl;
}
catch (std::exception& e) {
cout << e.what() << endl;
}
cout << "...END ERRORS" << endl;
}
void test_Cnv_byteutils()
{
dipki::bvec_t bv;
cout << "\nTESTING CNV BYTE UTILS..." << endl;
cout << "Testing Cnv::ReverseBytes()..." << endl;
dipki::bvec_t b{ 0x01, 0x02, 0x03, 0x04, 0x05 };
cout << "(before)=" << dipki::Cnv::ToHex(b) << endl;
// (before)=0102030405
b = dipki::Cnv::ReverseBytes(b);
cout << "(after) =" << dipki::Cnv::ToHex(b) << endl;
// (after) =0504030201
assert(b[0] == 0x05 && b[4] == 0x01);
cout << "Testing Cnv::NumToBytes()..." << endl;
bv = dipki::Cnv::NumToBytes(0xdeadbeef);
cout << "Cnv::NumToBytes(0xdeadbeef,BE)->" + dipki::Cnv::ToHex(bv) << endl;
// Cnv::NumToBytes(0xdeadbeef,BE)->DEADBEEF
bv = dipki::Cnv::NumToBytes(0xdeadbeef, dipki::Cnv::EndianNess::LittleEndian);
cout << "Cnv::NumToBytes(0xdeadbeef,LE)->" + dipki::Cnv::ToHex(bv) << endl;
// Cnv::NumToBytes(0xdeadbeef,LE)->EFBEADDE
cout << "Testing Cnv::NumFromBytes()..." << endl;
bv = dipki::Cnv::FromHex("DEADBEEF");
uint32_t ui = dipki::Cnv::NumFromBytes(bv);
cout << "Cnv::NumFromBytes(" << dipki::Cnv::ToHex(bv) << ")->" << ui << "=0x" << std::hex << ui << std::dec << endl;
// Cnv::NumFromBytes(DEADBEEF)->3735928559=0xdeadbeef
bv = dipki::Cnv::FromHex("DEAD"); // Short array
ui = dipki::Cnv::NumFromBytes(bv);
cout << "Cnv::NumFromBytes(" << dipki::Cnv::ToHex(bv) << ")->" << ui << "=0x" << std::hex << ui << std::dec << endl;
// Cnv::NumFromBytes(DEAD)->3735879680=0xdead0000
}
void test_Cnv_base58()
{
std::string s, b58str;
dipki::bvec_t b;
cout << "\nBASE58 CONVERSION TESTS:" << endl;
b58str = "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM";
b = dipki::Cnv::FromBase58(b58str);
cout << "'" + b58str + "'->\n0x" + dipki::Cnv::ToHex(b) << endl;
// Back to base58
s = dipki::Cnv::ToBase58(b);
cout << "In base58='" + s + "'" << endl;
assert(s == b58str);
// Convert directly from base58 to hex
s = dipki::Cnv::ToHex(dipki::Cnv::FromBase58(b58str));
cout << "In hex='" + s + "'" << endl;
assert(s == "00010966776006953D5567439E5E39F86A0D273BEED61967F6");
// One-liner
cout << "Cnv::FromBase58(\"16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM\")=0x" << dipki::Cnv::ToHex(dipki::Cnv::FromBase58("16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM")) << endl;
// Cnv::FromBase58("16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM")=0x00010966776006953D5567439E5E39F86A0D273BEED61967F6
}
void test_Cnv_utf8()
{
dipki::bvec_t bv;
int n;
std::string s;
std::wstring wstr;
std::string fname;
cout << "\nUTF-8 CONVERSION TESTS:" << endl;
// Create a string that includes some Latin-1 accented characters:
s = "abcóéíáñ";
// This should contain the following 8 characters:
// U+0061 LATIN SMALL LETTER A
// U+0062 LATIN SMALL LETTER B
// U+0063 LATIN SMALL LETTER C
// U+00F3 LATIN SMALL LETTER O WITH ACUTE
// U+00E9 LATIN SMALL LETTER E WITH ACUTE
// U+00ED LATIN SMALL LETTER I WITH ACUTE
// U+00E1 LATIN SMALL LETTER A WITH ACUTE
// U+00F1 LATIN SMALL LETTER N WITH TILDE
cout << "[" << s << "]" << "(missing!)" << endl; // Aha! Does not show all the characters because they are not UTF-8, which we set the console for in main().
cout << dipki::Cnv::ToHex(dipki::str2bvec(s)) << endl; // Shows all 8 characters are there, but in Latin-1 encoding.
n = dipki::Cnv::CheckUTF8(dipki::str2bvec(s));
cout << "Cnv::CheckUTF8=" << n << " (expecting 0)==> " << dipki::Cnv::CheckUTF8CodeAsString(n) << endl;
bv = dipki::Cnv::UTF8BytesFromLatin1(s);
cout << dipki::Cnv::ToHex(bv) << endl;
cout << dipki::bvec2str(bv) << endl;
n = dipki::Cnv::CheckUTF8(bv);
cout << "Cnv::CheckUTF8=" << n << " (expecting 2)==> " << dipki::Cnv::CheckUTF8CodeAsString(n) << endl;
// Convert back to Latin-1
std::string latin1str = dipki::Cnv::Latin1FromUTF8Bytes(bv);
cout << dipki::Cnv::ToHex(dipki::str2bvec(latin1str)) << endl;
// Plain ASCII
n = dipki::Cnv::CheckUTF8(dipki::str2bvec("abc"));
cout << "Cnv::CheckUTF8=" << n << " (expecting 1)==> " << dipki::Cnv::CheckUTF8CodeAsString(n) << endl;
// Chinese - hardcoded UTF-8 bytes
// U+4E2D U+56FD => E4 B8 AD E5 9B BD
dipki::bvec_t b{ 0xe4, 0xb8, 0xad, 0xE5, 0x9B, 0xBD };
n = dipki::Cnv::CheckUTF8(b);
cout << "Cnv::CheckUTF8=" << n << " (expecting 3)==> " << dipki::Cnv::CheckUTF8CodeAsString(n) << endl;
// Try (wrongly) to convert bytes to Latin-1
s = dipki::Cnv::Latin1FromUTF8Bytes(b);
cout << s << endl;
// Save to file then check the file
fname = "zhongguo.txt";
write_binary_file(fname, b);
cout << "FILE: " << fname << endl;
n = dipki::Cnv::CheckUTF8File(fname);
cout << "Cnv::CheckUTF8File=" << n << " (expecting 3)==> " << dipki::Cnv::CheckUTF8CodeAsString(n) << endl;
// Mapping wide chars to a UTF-8-encoded string
cout << "Map UTF-16 (wide) characters to UTF-8..." << endl;
// EXAMPLE using Polish characters
// ę 'LATIN SMALL LETTER E WITH OGONEK' (U+0119) => 0xC4 0x99
// ł 'LATIN SMALL LETTER L WITH STROKE' (U+0142) => 0xC5 0x82
cout << "Polish:" << endl;
wstr = L"węzeł";
s = dipki::Cnv::Utf8FromWide(wstr);
cout << "Cnv::Utf8FromWide=[" << s << "]" << endl;
cout << "s-->utf-8 bytes: " << dipki::Cnv::ToHex(dipki::str2bvec(s)) << endl;
// Spanish
cout << "Spanish:" << endl;
wstr = L"áéíñóü";
s = dipki::Cnv::Utf8FromWide(wstr);
cout << "Cnv::Utf8FromWide=[" << s << "]" << endl;
cout << "s-->utf-8 bytes: " << dipki::Cnv::ToHex(dipki::str2bvec(s)) << endl;
// This won't display properly on a Western Windows platform, but the UTF-8 bytes are correct
cout << "Chinese:" << endl;
wstr = L"中国"; // U+4E2D U+56FD => E4 B8 AD E5 9B BD
s = dipki::Cnv::Utf8FromWide(wstr);
cout << "Cnv::Utf8FromWide=[" << s << "] <= NOTE Chinese chars may not display properly" << endl;
cout << "but the UTF-8 bytes are correct..." << endl;
cout << "s-->utf-8 bytes: " << dipki::Cnv::ToHex(dipki::str2bvec(s)) << endl;
cout << "U+4E2D U+56FD => E4 B8 AD E5 9B BD" << endl;
// One-liner
cout << dipki::Cnv::ToHex(dipki::Cnv::Utf8FromWide(L"中国")) << endl;
// E4B8ADE59BBD U+4E2D U+56FD => E4 B8 AD E5 9B BD
cout << dipki::Cnv::ToHex(dipki::Cnv::Utf8FromWide(L"áéíñóü")) << endl;
// C3A1C3A9C3ADC3B1C3B3C3BC
}
void test_Cipher() {
//std::string s;
dipki::bvec_t bv;
dipki::bvec_t pt, ct, dt, key, iv, data;
std::string algstr;
cout << "\nTESTING Cipher..." << endl;
algstr = "Aes128/CBC/pkcs5";
cout << algstr << endl;
key = dipki::Cnv::FromHex("0123456789ABCDEFF0E1D2C3B4A59687");
iv = dipki::Cnv::FromHex("FEDCBA9876543210FEDCBA9876543210");
pt = dipki::str2bvec("Now is the time for all good men to");
ct = dipki::Cipher::Encrypt(pt, key, iv, algstr);
cout << "ct=" << dipki::Cnv::ToHex(ct) << endl;
assert(0 == dipki::Cnv::ToHex(ct).compare("C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E17753C7E8DF5975A36677355F5C6584228B"));
dt = dipki::Cipher::Decrypt(ct, key, iv, algstr);
cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
assert(dt.size() > 0);
ct = dipki::Cipher::Encrypt(pt, key, iv, algstr, dipki::Cipher::Opts::PrefixIV);
cout << "ct(PrefixIV)=" << dipki::Cnv::ToHex(ct) << endl;
//assert(0 == dipki::Cnv::ToHex(ct).compare("C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E17753C7E8DF5975A36677355F5C6584228B"));
dt = dipki::Cipher::Decrypt(ct, key, iv, algstr, dipki::Cipher::Opts::PrefixIV);
cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
assert(dt.size() > 0);
cout << "bvec2str(empty)='" << dipki::bvec2str(dipki::bvec_t()) << "'" << endl;
// Same input, different mode and padding, ECB mode so pass an empty IV
algstr = "Aes128/ECB/OneAndZeroes";
cout << algstr << endl;
ct = dipki::Cipher::Encrypt(pt, key, dipki::bvec_t(), algstr);
cout << "ct=" << dipki::Cnv::ToHex(ct) << endl;
assert(0 == dipki::Cnv::ToHex(ct).compare("F0D1AD6F901FFFAE5572A6928DAB52B064B25C79F876730321E36DC01011ACCED7E53F5E5CB18233FE486CD4FFA79FE9"));
dt = dipki::Cipher::Decrypt(ct, key, iv, algstr);
cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
assert(dt.size() > 0);
// Again, using Alg/Mode/Pad params instead of alg string
ct = dipki::Cipher::Encrypt(pt, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CBC, dipki::Cipher::Padding::Default);
cout << "ct=" << dipki::Cnv::ToHex(ct) << endl;
assert(0 == dipki::Cnv::ToHex(ct).compare("C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E17753C7E8DF5975A36677355F5C6584228B"));
dt = dipki::Cipher::Decrypt(ct, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CBC, dipki::Cipher::Padding::Default);
cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
assert(dt.size() > 0);
ct = dipki::Cipher::Encrypt(pt, key, dipki::bvec_t(), dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::ECB, dipki::Cipher::Padding::OneAndZeroes);
cout << "ct=" << dipki::Cnv::ToHex(ct) << endl;
assert(0 == dipki::Cnv::ToHex(ct).compare("F0D1AD6F901FFFAE5572A6928DAB52B064B25C79F876730321E36DC01011ACCED7E53F5E5CB18233FE486CD4FFA79FE9"));
dt = dipki::Cipher::Decrypt(ct, key, dipki::bvec_t(), dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::ECB, dipki::Cipher::Padding::OneAndZeroes);
cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
assert(dt.size() > 0);
cout << "TEST CIPHER FUNCTIONS WITH EXACT BLOCK LENGTHS..." << endl;
key = dipki::Cnv::FromHex("0123456789ABCDEFF0E1D2C3B4A59687");
iv = dipki::Cnv::FromHex("FEDCBA9876543210FEDCBA9876543210");
pt = dipki::str2bvec("Now is the time for all good men"); // Exactly 32 bytes, a multiple of 16
ct = dipki::Cipher::EncryptBlock(pt, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CBC);
cout << "ct=" << dipki::Cnv::ToHex(ct) << endl;
assert(0 == dipki::Cnv::ToHex(ct).compare("C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E177"));
dt = dipki::Cipher::DecryptBlock(ct, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CBC);
cout << "dt=" << dipki::Cnv::ToHex(dt) << endl;
cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
assert(dt.size() > 0);
// Use defaults and a random key
dipki::Cipher::Alg myalg = dipki::Cipher::Alg::Aes256; // Cipher::Alg _must_ be specified
cout << "Cipher::Alg = " << dipki::Cipher::AlgName(myalg) << endl;
key = dipki::Rng::Bytes(dipki::Cipher::KeyBytes(myalg));
cout << "Random key: " << dipki::Cnv::ToHex(key) << endl;
ct = dipki::Cipher::EncryptBlock(pt, key, iv, myalg);
cout << "ct=" << dipki::Cnv::ToHex(ct) << endl;
dt = dipki::Cipher::DecryptBlock(ct, key, iv, myalg);
cout << "dt=" << dipki::Cnv::ToHex(dt) << endl;
cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
assert(dt.size() > 0);
cout << "BEGIN EXPECTING ERRORS..." << endl;
// Invalid decryption
cout << "Expecting an error (passing the wrong key to decrypt)..." << endl;
key = dipki::Cnv::FromHex("FF0123456789ABCDEFF0E1D2C3B4A596"); // Wrong key
iv = dipki::Cnv::FromHex("FEDCBA9876543210FEDCBA9876543210");
ct = dipki::Cnv::FromHex("C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E17753C7E8DF5975A36677355F5C6584228B");
try {
dt = dipki::Cipher::Decrypt(ct, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CBC);
cout << "dt=" << dipki::Cnv::ToHex(dt) << endl;
}
catch (std::exception& e) {
cout << e.what() << endl;
}
cout << "Passing an invalid IV..." << endl;
try {
dt = dipki::Cipher::EncryptBlock(ct, key, dipki::bvec_t(), dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CBC);
cout << "dt=" << dipki::Cnv::ToHex(dt) << endl;
}
catch (std::exception& e) {
cout << e.what() << endl;
}
cout << "... END ERRORS" << endl;
// One-liners
cout << "Cipher::BlockBytes(dipki::Cipher::Alg::Aes256)=" << dipki::Cipher::BlockBytes(dipki::Cipher::Alg::Aes256) << endl;
// Cipher::BlockBytes(dipki::Cipher::Alg::Aes256)=16
cout << "Cipher::KeyBytes(dipki::Cipher::Alg::Aes256)=" << dipki::Cipher::KeyBytes(dipki::Cipher::Alg::Aes256) << endl;
// Cipher::KeyBytes(dipki::Cipher::Alg::Aes256)=32
cout << "Cipher::AlgName(dipki::Cipher::Alg::Aes256)=" << dipki::Cipher::AlgName(dipki::Cipher::Alg::Aes256) << endl;
// Cipher::AlgName(dipki::Cipher::Alg::Aes256)=AES-256
}
void test_Cipher_File() {
dipki::bvec_t key, iv;
std::string fnameData, fnameEnc, fnameCheck;
dipki::bvec_t bv;
int r;
cout << "\nTESTING Cipher_File..." << endl;
key = dipki::Cnv::FromHex("fedcba98765432100123456789abcdeffedcba9876543210");
fnameData = "excontent.txt";
fnameEnc = "excontent.tdea.enc.dat";
r = dipki::Cipher::FileEncrypt(fnameEnc, fnameData, key, dipki::bvec_t(), dipki::Cipher::Alg::Tdea);
cout << "Created file " << fnameEnc << endl;
bv = read_binary_file(fnameEnc);
cout << "CT=" << dipki::Cnv::ToHex(bv) << endl;
assert(dipki::Cnv::ToHex(bv) == "DD1E1FA430AE6BE1D3B83245F7A5B17C4BF03688238778E95F2CCD05AF1A8F44");
// Decrypt the file
fnameCheck = "excontent.tdea.chk.txt";
r = dipki::Cipher::FileDecrypt(fnameCheck, fnameEnc, key, dipki::bvec_t(), dipki::Cipher::Alg::Tdea);
cout << "Created file " << fnameCheck << endl;
bv = read_binary_file(fnameCheck);
cout << "DT=" << dipki::Cnv::ToHex(bv) << endl;
cout << "DT='" << dipki::bvec2str(bv) << "'" << endl;
key = dipki::Cnv::FromHex("fedcba98765432100123456789abcdef");
iv = dipki::Cnv::FromHex ("100000000000000000000000000000FF");
fnameData = "excontent.txt";
fnameEnc = "excontent.aes128.enc.dat";
r = dipki::Cipher::FileEncrypt(fnameEnc, fnameData, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CTR, dipki::Cipher::Padding::Default, dipki::Cipher::Opts::PrefixIV);
cout << "Created file " << fnameEnc << endl;
bv = read_binary_file(fnameEnc);
cout << "CT=" << dipki::Cnv::ToHex(bv) << endl;
assert(dipki::Cnv::ToHex(bv) == "100000000000000000000000000000FF64DFD2C966A837024098F2CE6E3F0EAEA7FE35FD21FCAFA94EEFDB22");
// Decrypt the file
fnameCheck = "excontent.aes128.chk.txt";
r = dipki::Cipher::FileDecrypt(fnameCheck, fnameEnc, key, dipki::bvec_t(), dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CTR, dipki::Cipher::Padding::Default, dipki::Cipher::Opts::PrefixIV);
cout << "Created file " << fnameCheck << endl;
bv = read_binary_file(fnameCheck);
cout << "DT=" << dipki::Cnv::ToHex(bv) << endl;
cout << "DT='" << dipki::bvec2str(bv) << "'" << endl;
}
void test_Cipher_GCM() {
// New in [v20.7]
dipki::bvec_t key, iv, pt, ct, dt;
std::string fnameData, fnameEnc, fnameCheck;
dipki::bvec_t bv;
int r;
cout << "\nTESTING Cipher GCM..." << endl;
cout << "Encrypt a file using AES-GCM..." << endl;
key = dipki::Cnv::FromHex("2B7E151628AED2A6ABF7158809CF4F3C");
iv = dipki::Cnv::FromHex("000102030405060708090A0B");
fnameData = "excontent.txt";
fnameEnc = "excontent.aes128gcm.enc.dat";
r = dipki::Cipher::FileEncrypt(fnameEnc, fnameData, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::GCM, dipki::Cipher::Padding::Default, dipki::Cipher::Opts::PrefixIV);
cout << "Created file " << fnameEnc << endl;
bv = read_binary_file(fnameEnc);
cout << "CT=" << dipki::Cnv::ToHex(bv) << endl;
// Decrypt the file NB IV is included in CT
fnameCheck = "excontent.aes128gcm.chk.txt";
r = dipki::Cipher::FileDecrypt(fnameCheck, fnameEnc, key, dipki::bvec_t(), dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::GCM, dipki::Cipher::Padding::Default, dipki::Cipher::Opts::PrefixIV);
cout << "Created file " << fnameCheck << endl;
bv = read_binary_file(fnameCheck);
cout << "DT=" << dipki::Cnv::ToHex(bv) << endl;
cout << "DT='" << dipki::bvec2str(bv) << "'" << endl;
cout << "Encrypt bytes using AES-GCM..." << endl;
// Same as EncryptAEAD except without AAD
pt = read_binary_file(fnameData);
ct = dipki::Cipher::Encrypt(pt, key, iv, "aes128-gcm");
cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
dt = dipki::Cipher::Decrypt(ct, key, iv, "aes128-gcm");
cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
cout << "DT='" << dipki::bvec2str(dt) << "'" << endl;
}
void test_Cipher_Aead() {
dipki::bvec_t key, iv, pt, ct, dt, aad;
cout << "\nTESTING Cipher AEAD..." << endl;
cout << "GCM Test Case #03 (AES-128)" << endl;
key = dipki::Cnv::FromHex("feffe9928665731c6d6a8f9467308308");
iv = dipki::Cnv::FromHex("cafebabefacedbaddecaf888");
pt = dipki::Cnv::FromHex("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255");
cout << "KY=" << dipki::Cnv::ToHex(key) << endl;
cout << "IV=" << dipki::Cnv::ToHex(iv) << endl;
cout << "PT=" << dipki::Cnv::ToHex(pt) << endl;
ct = dipki::Cipher::EncryptAEAD(pt, key, iv, dipki::Cipher::AeadAlg::Aes_128_Gcm);
cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
assert(dipki::Cnv::ToHex(ct) ==
"42831EC2217774244B7221B784D0D49CE3AA212F2C02A4E035C17E2329ACA12E21D514B25466931C7D8F6A5AAC84AA051BA30B396A0AAC973D58E091473F59854D5C2AF327CD64A62CF35ABD2BA6FAB4");
// Decrypt - must pass IV here
dt = dipki::Cipher::DecryptAEAD(ct, key, iv, dipki::Cipher::AeadAlg::Aes_128_Gcm);
cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
assert(dipki::Cnv::ToHex(dt) == dipki::Cnv::ToHex(pt));
// Prepend IV to ciphertext...
ct = dipki::Cipher::EncryptAEAD(pt, key, iv, dipki::Cipher::AeadAlg::Aes_128_Gcm, dipki::Cipher::Opts::PrefixIV);
cout << "IV|CT=" << dipki::Cnv::ToHex(ct) << endl;
assert(dipki::Cnv::ToHex(ct) == "CAFEBABEFACEDBADDECAF888"
"42831EC2217774244B7221B784D0D49CE3AA212F2C02A4E035C17E2329ACA12E21D514B25466931C7D8F6A5AAC84AA051BA30B396A0AAC973D58E091473F59854D5C2AF327CD64A62CF35ABD2BA6FAB4");
// Decrypt - IV is prefixed to CT, so pass an empty IV
dt = dipki::Cipher::DecryptAEAD(ct, key, dipki::bvec_t(), dipki::Cipher::AeadAlg::Aes_128_Gcm, dipki::Cipher::Opts::PrefixIV);
cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
assert(dipki::Cnv::ToHex(dt) == dipki::Cnv::ToHex(pt));
cout << "GCM Test Case #08 (AES-192)" << endl;
key = dipki::Cnv::FromHex("000000000000000000000000000000000000000000000000");
iv = dipki::Cnv::FromHex("000000000000000000000000");
pt = dipki::Cnv::FromHex("00000000000000000000000000000000");
cout << "KY=" << dipki::Cnv::ToHex(key) << endl;
cout << "IV=" << dipki::Cnv::ToHex(iv) << endl;
cout << "PT=" << dipki::Cnv::ToHex(pt) << endl;
ct = dipki::Cipher::EncryptAEAD(pt, key, iv, dipki::Cipher::AeadAlg::Aes_192_Gcm);
cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
assert(dipki::Cnv::ToHex(ct) ==
"98E7247C07F0FE411C267E4384B0F6002FF58D80033927AB8EF4D4587514F0FB");
// Decrypt - must pass IV here
dt = dipki::Cipher::DecryptAEAD(ct, key, iv, dipki::Cipher::AeadAlg::Aes_192_Gcm);
cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
assert(dipki::Cnv::ToHex(dt) == dipki::Cnv::ToHex(pt));
cout << "GCM Test Case #16 (AES-256)" << endl;
key = dipki::Cnv::FromHex("feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308");
iv = dipki::Cnv::FromHex("cafebabefacedbaddecaf888");
pt = dipki::Cnv::FromHex("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39");
aad = dipki::Cnv::FromHex("feedfacedeadbeeffeedfacedeadbeefabaddad2");
cout << "KY=" << dipki::Cnv::ToHex(key) << endl;
cout << "IV=" << dipki::Cnv::ToHex(iv) << endl;
cout << "PT=" << dipki::Cnv::ToHex(pt) << endl;
cout << "AD=" << dipki::Cnv::ToHex(aad) << endl;
ct = dipki::Cipher::EncryptAEAD(pt, key, iv, dipki::Cipher::AeadAlg::Aes_256_Gcm, dipki::Cipher::Opts::None, aad);
cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
assert(dipki::Cnv::ToHex(ct) ==
"522DC1F099567D07F47F37A32A84427D643A8CDCBFE5C0C97598A2BD2555D1AA8CB08E48590DBB3DA7B08B1056828838C5F61E6393BA7A0ABCC9F66276FC6ECE0F4E1768CDDF8853BB2D551B");
// Decrypt - must pass IV here, and the original AAD
dt = dipki::Cipher::DecryptAEAD(ct, key, iv, dipki::Cipher::AeadAlg::Aes_256_Gcm, dipki::Cipher::Opts::None, aad);
cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
assert(dipki::Cnv::ToHex(dt) == dipki::Cnv::ToHex(pt));
}
void test_Cipher_KeyWrap() {
dipki::bvec_t wk;
dipki::bvec_t keydata, kek, unwk;
cout << "\nTESTING Cipher KeyWrap..." << endl;
cout << "Using AES-128 Key wrap" << endl;
keydata = dipki::Cnv::FromHex("00112233 44556677 8899aabb ccddeeff");
kek = dipki::Cnv::FromHex("c17a44e8 e28d7d64 81d1ddd5 0a3b8914");
// Wrap key using AES-128 Key Wrap
wk = dipki::Cipher::KeyWrap(keydata, kek, dipki::Cipher::Alg::Aes128);
cout << "WK=" << dipki::Cnv::ToHex(wk) << endl;
cout << "OK=503D75C73630A7B02ECF51B9B29B907749310B77B0B2E054" << endl;
assert(dipki::Cnv::ToHex(wk) == "503D75C73630A7B02ECF51B9B29B907749310B77B0B2E054");
// Now unwrap it
unwk = dipki::Cipher::KeyUnwrap(wk, kek, dipki::Cipher::Alg::Aes128);
cout << "KY=" << dipki::Cnv::ToHex(unwk) << endl;
cout << "OK=00112233445566778899AABBCCDDEEFF" << endl;
assert(dipki::Cnv::ToHex(unwk) == "00112233445566778899AABBCCDDEEFF");
cout << "Using Triple DES Key Wrap (wrapped key always different value but always 40 bytes)" << endl;
keydata = dipki::Cnv::FromHex("84e7f2d8 78f89fcc cd2d5eba fc56daf7 3300f27e f771cd68");
kek = dipki::Cnv::FromHex("8ad8274e 56f46773 8edd83d4 394e5e29 af7c4089 e4f8d9f4");
wk = dipki::Cipher::KeyWrap(keydata, kek, dipki::Cipher::Alg::Tdea);
cout << "WK=" << dipki::Cnv::ToHex(wk) << endl;
assert(wk.size() == 40); // Triple DES wrapped key is _always_ 40 bytes long.
// Now unwrap it
unwk = dipki::Cipher::KeyUnwrap(wk, kek, dipki::Cipher::Alg::Tdea);
cout << "KY=" << dipki::Cnv::ToHex(unwk) << endl;
cout << "OK=84E7F2D878F89FCCCD2D5EBAFC56DAF73300F27EF771CD68" << endl;
assert(dipki::Cnv::ToHex(unwk) == "84E7F2D878F89FCCCD2D5EBAFC56DAF73300F27EF771CD68");
}
void test_Cipher_Pad() {
dipki::bvec_t block, input, unpad;
cout << "\nTESTING Cipher Padding..." << endl;
input = dipki::Cnv::FromHex("FDFDFDFDFD");
cout << "Input data = 0x" << dipki::Cnv::ToHex(input) << endl;
block = dipki::Cipher::Pad(input, dipki::Cipher::Alg::Tdea);
cout << "Padded data = 0x" << dipki::Cnv::ToHex(block) << endl;
unpad = dipki::Cipher::Unpad(block, dipki::Cipher::Alg::Tdea);
cout << "Unpadded = 0x" << dipki::Cnv::ToHex(unpad) << endl;
input = dipki::Cnv::FromHex("FDFD");
cout << "Input data = 0x" << dipki::Cnv::ToHex(input) << endl;
block = dipki::Cipher::Pad(input, dipki::Cipher::Alg::Aes128, dipki::Cipher::Padding::OneAndZeroes);
cout << "Padded data = 0x" << dipki::Cnv::ToHex(block) << endl;
unpad = dipki::Cipher::Unpad(block, dipki::Cipher::Alg::Aes128, dipki::Cipher::Padding::OneAndZeroes);
cout << "Unpadded = 0x" << dipki::Cnv::ToHex(unpad) << endl;
cout << "Invalid padding..." << endl;
try {
unpad = dipki::Cipher::Unpad(block, dipki::Cipher::Alg::Tdea);
}
catch (std::exception& e) {
cout << e.what() << endl;
}
}
void test_Rng() {
dipki::bvec_t bv;
cout << "\nTESTING Rng..." << endl;
cout << "GUID=" << dipki::Rng::Guid() << endl;
cout << "GUID=" << dipki::Rng::Guid() << endl;
cout << "GUID=" << dipki::Rng::Guid() << endl;
cout << "Rng::Number(-5,+5):" << endl;
for (int i = 0; i < 20; i++) {
cout << dipki::Rng::Number(-5, 5) << " ";
}
cout << endl;
cout << "Rng::Octet:" << endl;
for (int i = 0; i < 20; i++) {
cout << dipki::Rng::Octet() << " ";
}
cout << endl;
cout << "Rng::Bytes:" << endl;
for (int i = 0; i < 4; i++) {
bv = dipki::Rng::Bytes(24);
cout << dipki::Cnv::ToHex(bv) << endl;
}
std::string seedfile = "mycppseedfile.dat";
dipki::Rng::Initialize(seedfile);
// Show contents of seed file
bv = read_binary_file(seedfile);
cout << "Seed file:\n" << dipki::Cnv::ToHex(bv) << endl;
dipki::Rng::UpdateSeedFile(seedfile);
bv = read_binary_file(seedfile);
cout << "New Seed file:\n" << dipki::Cnv::ToHex(bv) << endl;
}
void test_Gen_Errors() {
cout << "\nTESTING error lookups..." << endl;
for (int i = 0; i < 5; i++) {
cout << "Err::ErrorLookup(" << i << ")=" << dipki::Err::ErrorLookup(i) << endl;
}
cout << "Err::ErrorLookup(" << -9999 << ")=" << dipki::Err::ErrorLookup(-9999) << endl;
}
void test_Asn1() {
cout << "\nTESTING Asn1..." << endl;
cout << dipki::Asn1::TextDumpToString("MAQwAgUA") << endl;
cout << dipki::Asn1::TextDumpToString("MAQwAgUA", dipki::Asn1::Opts::NoComments) << endl;
cout << dipki::Asn1::TextDumpToString("MAQwAgUA", dipki::Asn1::Opts::AddLevels) << endl;
cout << "Asn1::Type(\"AliceRSASignByCarl.cer\")=" << dipki::Asn1::Type("AliceRSASignByCarl.cer") << endl;
int n = dipki::Asn1::TextDump("AliceRSASignByCarl-asn1-dump.txt", "AliceRSASignByCarl.cer");
cout << "Asn1::TextDump returns " << n << endl;
assert(0 == n);
//cout << read_text_file("AliceRSASignByCarl-asn1-dump.txt") << endl;
}
void test_Hash() {
cout << "\nTESTING Hash..." << endl;
std::string hashstr;
dipki::bvec_t hashbv;
hashstr = dipki::Hash::HexFromHex("616263");
cout << "'" << hashstr << "'" << endl;
hashstr = dipki::Hash::HexFromHex("616263", dipki::Hash::Alg::Sha224);
cout << "'" << hashstr << "'" << endl;
hashbv = dipki::Hash::Bytes(dipki::Cnv::FromHex("616263"));
cout << dipki::Cnv::ToHex(hashbv) << endl;
hashbv = dipki::Hash::Bytes(dipki::Cnv::FromHex("616263"), dipki::Hash::Alg::Sha224);
cout << dipki::Cnv::ToHex(hashbv) << endl;
hashbv = dipki::Hash::Bytes(dipki::Cnv::FromHex("616263"), dipki::Hash::Alg::Sha256);
cout << dipki::Cnv::ToHex(hashbv) << endl;
dipki::Hash::Alg myhashalg;
myhashalg = dipki::Hash::Alg::Sha256;
hashbv = dipki::Hash::Bytes(dipki::Cnv::FromHex("616263"), myhashalg);
cout << dipki::Cnv::ToHex(hashbv) << endl;
hashstr = dipki::Hash::HexFromString("abc");
cout << "'" << hashstr << "'" << endl;
hashstr = dipki::Hash::HexFromBytes(dipki::Cnv::FromHex("616263"));
cout << "'" << hashstr << "'" << endl;
hashstr = dipki::Hash::HexFromString("abc", myhashalg);
cout << "'" << hashstr << "'" << endl;
hashstr = dipki::Hash::HexFromBytes(dipki::Cnv::FromHex("616263"), myhashalg);
cout << "'" << hashstr << "'" << endl;
// One-liners
cout << dipki::Hash::HexFromHex("616263") << endl;
// a9993e364706816aba3e25717850c26c9cd0d89d
cout << dipki::Hash::HexFromString("abc", dipki::Hash::Alg::Sha256) << endl;
// ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
cout << dipki::Hash::AlgName(dipki::Hash::Alg::Sha512) << endl;
// sha512
cout << "AlgName=" << dipki::Hash::AlgName(dipki::Hash::HashAlgFromString("! SHa-256;")) << endl;
// AlgName=sha256
hashbv = dipki::Hash::Double(dipki::Cnv::FromHex("616263"), myhashalg);
cout << "SHA1(SHA1('abc'))=" << dipki::Cnv::ToHex(hashbv) << endl;
hashbv = dipki::Hash::File("abc.txt", myhashalg);
cout << dipki::Cnv::ToHex(hashbv) << endl;
hashstr = dipki::Hash::HexFromFile("abc.txt", myhashalg);
cout << "'" << hashstr << "'" << endl;
cout << "File is missing..." << endl;
try {
hashbv = dipki::Hash::File("missing.file", myhashalg);
cout << dipki::Cnv::ToHex(hashbv) << endl;
}
catch (std::exception& e) {
cout << e.what() << endl;
}
// Hash the empty string
cout << "SHA-1 hash the empty string..." << endl;
hashstr = dipki::Hash::HexFromString("");
cout << "'" << hashstr << "'" << endl;
hashbv = dipki::Hash::Bytes(dipki::bvec_t());
cout << dipki::Cnv::ToHex(hashbv) << endl;
hashstr = dipki::Hash::HexFromHex("");
cout << "'" << hashstr << "'" << endl;
hashbv = dipki::Hash::File("empty.txt");
cout << dipki::Cnv::ToHex(hashbv) << endl;
hashstr = dipki::Hash::HexFromFile("empty.txt");
cout << "'" << hashstr << "'" << endl;
}
void test_Hmac() {
cout << "\nTESTING Hmac..." << endl;
std::string hmacstr;
dipki::bvec_t hmacbv, bv, data, key;
cout << "Test case 1 from RFC 2202 and RFC 4231" << endl;
// HEX('Hi There') = 4869205468657265
hmacstr = dipki::Hmac::HexFromHex("4869205468657265", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
cout << "HMAC-SHA-1='" << hmacstr << "'" << endl;
assert(hmacstr == "b617318655057264e28bc0b6fb378c8ef146be00");
hmacstr = dipki::Hmac::HexFromHex("4869205468657265", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", dipki::Hmac::Alg::Sha256);
cout << "HMAC-SHA-256='" << hmacstr << "'" << endl;
assert(hmacstr == "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7");
cout << "Test case 4 from RFC 2202 and RFC 4231" << endl;
key = dipki::Cnv::FromHex("0102030405060708090a0b0c0d0e0f10111213141516171819");
cout << "key=" << dipki::Cnv::ToHex(key) << endl;
// data = 0xcd repeated 50 times
data.clear();
data.insert(data.begin(), 50, (unsigned char)0xcd);
cout << "data=" << dipki::Cnv::ToHex(data) << endl;
bv = dipki::Hmac::Bytes(data, key);
cout << "HMAC-SHA-1=" << dipki::Cnv::ToHex(bv) << endl;
assert(bv == dipki::Cnv::FromHex("4c9007f4026250c6bc8414f9bf50c86c2d7235da"));
bv = dipki::Hmac::Bytes(data, key, dipki::Hmac::Alg::Md5);
cout << "HMAC-MD5=" << dipki::Cnv::ToHex(bv) << endl;
assert(bv == dipki::Cnv::FromHex("697eaf0aca3a3aea3a75164746ffaa79"));
bv = dipki::Hmac::Bytes(data, key, dipki::Hmac::Alg::Sha256);
cout << "HMAC-SHA-256=" << dipki::Cnv::ToHex(bv) << endl;
assert(bv == dipki::Cnv::FromHex("82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"));
hmacstr = dipki::Hmac::HexFromBytes(data, key, dipki::Hmac::Alg::Sha256);
cout << "HMAC-SHA-256='" << hmacstr << "'" << endl;
assert(hmacstr == "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b");
cout << "Test case 7 from RFC 4231" << endl;
data = dipki::str2bvec("This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.");
// key 0xaa repeated 131 times
key.clear();
key.insert(key.begin(), 131, (unsigned char)0xAA);
bv = dipki::Hmac::Bytes(data, key, dipki::Hmac::Alg::Sha224);
cout << "HMAC-SHA-224=" << dipki::Cnv::ToHex(bv) << endl;
assert(bv == dipki::Cnv::FromHex("3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1"));
// One-liners
cout << dipki::Hmac::HexFromString("Hi There", dipki::Cnv::FromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")) << endl;
// b617318655057264e28bc0b6fb378c8ef146be00
cout << dipki::Hmac::HexFromBytes(dipki::Cnv::FromHex("7768617420646f2079612077616e7420666f72206e6f7468696e673f"), dipki::Cnv::FromHex("4a656665"), dipki::Hmac::Alg::Sha256) << endl;
// 5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
cout << dipki::Hmac::HexFromHex("7768617420646f2079612077616e7420666f72206e6f7468696e673f", "4a656665", dipki::Hmac::Alg::Sha384) << endl;
// af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649
}
void test_hmac_SHA3() {
cout << "\nTESTING Hmac(SHA3)..." << endl;
std::string hmacstr;
dipki::bvec_t hmacbv, bv, data, key;
cout << "NIST HMAC_SHA3-256.pdf Sample #1" << endl;
hmacstr = dipki::Hmac::HexFromBytes(dipki::str2bvec("Sample message for keylen<blocklen"),
dipki::Cnv::FromHex("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"), dipki::Hmac::Alg::Sha3_256);
cout << "HMAC-SHA3-256='" << hmacstr << "'" << endl;
assert(hmacstr == "4fe8e202c4f058e8dddc23d8c34e467343e23555e24fc2f025d598f558f67205");
cout << "NIST HMAC_SHA3-384.pdf Sample #2" << endl;
key = dipki::Cnv::FromHex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
"404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"
"6061626364656667");
hmacstr = dipki::Hmac::HexFromBytes(dipki::str2bvec("Sample message for keylen=blocklen"),
key, dipki::Hmac::Alg::Sha3_384);
cout << "HMAC-SHA3-384='" << hmacstr << "'" << endl;
assert(hmacstr == "a27d24b592e8c8cbf6d4ce6fc5bf62d8fc98bf2d486640d9eb8099e24047837f5f3bffbe92dcce90b4ed5b1e7e44fa90");
}
void test_Wipe() {
dipki::bvec_t bv;
std::string s;
cout << "\nTESTING Wipe..." << endl;
bv = dipki::str2bvec("Some secret data");
dipki::Wipe::Data(bv);
cout << "After Wipe::Data='" << dipki::Cnv::ToHex(bv) << "'" << endl;
s = "More secret data";
dipki::Wipe::String(s);
cout << "After Wipe::String='" << s << "'" << endl;
std::string fname = "todelete.txt";
write_text_file(fname, "Even more secret stuff\n");
cout << "FILE: " << fname << endl;
cout << "[" << read_text_file(fname) << "]" << endl;
dipki::Wipe::File(fname);
cout << "After Wipe::File=" << "[" << read_text_file(fname) << "]" << endl;
}
void test_Pbe() {
cout << "\nTESTING PASSWORD-BASED ENCRYPTION (PBE)..." << endl;
std::string password = "password";
dipki::bvec_t salt = dipki::Cnv::FromHex("78 57 8E 5A 5D 63 CB 06");
int count = 2048;
dipki::bvec_t dk;
dk = dipki::Pbe::Kdf2(24, password, salt, count);
cout << "dk=" << dipki::Cnv::ToHex(dk) << endl;
assert(dk == dipki::Cnv::FromHex("BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643"));
// One-liner
dipki::bvec_t derived_key = dipki::Pbe::Kdf2(24, "password", dipki::Cnv::FromHex("78578E5A5D63CB06"), 2048);
cout << "derived_key=" << dipki::Cnv::ToHex(derived_key) << endl;
// derived_key=BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643
// Same params but derive longer key (CAUTION: never use same salt in practice)
dk = dipki::Pbe::Kdf2(64, password, salt, count);
cout << "dk=" << dipki::Cnv::ToHex(dk) << endl;
assert(dk == dipki::Cnv::FromHex("BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643C4B150DEF77511224479994567F2E9B4E3BD0DF7AEDA3022B1F26051D81505C794F8940C04DF1144"));
// Use different HMAC algorithm
dk = dipki::Pbe::Kdf2(24, password, salt, count, dipki::Pbe::PrfAlg::Hmac_Sha256);
cout << "dk=" << dipki::Cnv::ToHex(dk) << endl;
assert(dk == dipki::Cnv::FromHex("97B5A91D35AF542324881315C4F849E327C4707D1BC9D322"));
}
void test_Pem() {
cout << "\nTESTING PEM file encoding..." << endl;
cout << "Create a PEM-format CERTIFICATE file from binary file..." << endl;
int r;
std::string h1, h2;
std::string binfile = "smallca.cer";
std::string pemfile = "smallca.pem";
r = dipki::Pem::FileFromBinFile(pemfile, binfile, "CERTIFICATE");
assert(0 == r);
cout << "Pem::FileFromBinFile created file '" << pemfile << "'" << endl;
cout << read_text_file(pemfile) << endl;
r = dipki::Pem::FileFromBinFile(pemfile, binfile, "CERTIFICATE", 72, dipki::Pem::Eol::Unix);
assert(0 == r);
cout << "Pem::FileFromBinFile created file '" << pemfile << "'" << endl;
cout << read_text_file(pemfile) << endl;
cout << "Convert PEM to binary..." << endl;
std::string newbin = "smallca-copy.bin";
r = dipki::Pem::FileToBinFile(newbin, pemfile);
assert(0 == r);
// Compute SHA-1 hash of both binary files to show they are identical
h1 = dipki::Hash::HexFromFile(binfile);
cout << "Hash binfile = " << h1 << endl;
h2 = dipki::Hash::HexFromFile(newbin);
cout << "Hash newbin = " << h2 << endl;
}
void test_Smime() {
cout << "\nTESTING S/MIME..." << endl;
int r;
std::string p7mfile, wrappedfile, extractedp7m, query;
// Wrap a CMS object
p7mfile = "cmsalice2bob.p7m";
wrappedfile = "cmsalice2bob-smime-env.txt";
r = dipki::Smime::Wrap(wrappedfile, p7mfile);
assert(r >= 0);
cout << "Smime::Wrap created file '" << wrappedfile << "'" << endl;
cout << read_text_file(wrappedfile) << endl;
// Query the S/MIME wrapped file
query = "smime-type";
cout << "Smime::Query('" << query << "')=" << dipki::Smime::Query(wrappedfile, query) << endl;
query = "content-type";
cout << "Smime::Query('" << query << "')=" << dipki::Smime::Query(wrappedfile, query) << endl;
query = "encoding";
cout << "Smime::Query('" << query << "')=" << dipki::Smime::Query(wrappedfile, query) << endl;
// Now extract original CMS object from it
extractedp7m = "cmsalice2bob-extracted.p7m";
r = dipki::Smime::Extract(extractedp7m, wrappedfile);
assert(r >= 0);
cout << "Smime::Extract created file '" << extractedp7m << "'" << endl;
//cout << dipki::Asn1::TextDumpToString(extractedp7m) << endl;
cout << "Extracted file has type: " << dipki::Asn1::Type(extractedp7m) << ". SHA-1=" << dipki::Hash::HexFromFile(extractedp7m) << endl;
// Compare to original file that was wrapped...
cout << "Original file has type: " << dipki::Asn1::Type(p7mfile) << ". SHA-1=" << dipki::Hash::HexFromFile(p7mfile) << endl;
// Test advanced wrap options - binary content plus add "x-" in subtype
p7mfile = "cmsalice2bob.p7m";
wrappedfile = "cmsalice2bob-smime-env-opts.dat";
r = dipki::Smime::Wrap(wrappedfile, p7mfile, dipki::Smime::Encoding::Binary, dipki::Smime::AdvOpts::AddX);
assert(r >= 0);
cout << "Smime::Wrap created file '" << wrappedfile << "'" << endl;
query = "content-type";
cout << "Smime::Query('" << query << "')=" << dipki::Smime::Query(wrappedfile, query) << endl;
query = "encoding";
cout << "Smime::Query('" << query << "')=" << dipki::Smime::Query(wrappedfile, query) << endl;
}
void test_Pwd(bool doPrompt) {
if (!doPrompt) return;
cout << "\nTESTING PWD PROMPT..." << endl;
std::string pwd = dipki::Pwd::Prompt("Caption here", "Enterada da password");
cout << "Password='" << pwd << "'" << endl;
}
void test_Pfx() {
cout << "\nTESTING PFX..." << endl;
int r, n, ncerts;
bool isok;
std::string pfxfile, certfile, keyfile, password, p7file;
std::string certstr, hash1, hash2;
pfxfile = "bob_bycarl.pfx";
r = dipki::Pfx::MakeFile(pfxfile, "BobRSASignByCarl.cer", "BobPrivRSAEncrypt.p8e", "password");
cout << "Pfx::MakeFile returns " << r << endl;
assert(r == 0);
cout << "Created file " << pfxfile << endl;
cout << "Asn1 type=" << dipki::Asn1::Type(pfxfile) << endl;
isok = dipki::Pfx::SignatureIsValid(pfxfile, "password");
cout << "Pfx::SignatureIsValid returns " << bool2str(isok) << endl;
assert(isok);
// Wrong password
isok = dipki::Pfx::SignatureIsValid(pfxfile, "wrong-password");
cout << "Pfx::SignatureIsValid(/wrong-password/) returns " << bool2str(isok) << endl;
assert(!isok);
// Extract the certificate from a PKCS-12 PFX file
certfile = "alice_fromPFX.cer";
n = dipki::X509::GetCertFromPFX(certfile, pfxfile, "password");
cout << "X509::GetCertFromPFX returns " << n << " (expected +ve)." << endl;
cout << "Created file " << certfile << endl;
cout << "Asn1 type=" << dipki::Asn1::Type(certfile) << endl;
// We can read the certificate directly into a string from the PFX file
certstr = dipki::X509::ReadCertStringFromPFX(pfxfile, "password");
cout << "Cert as a string:" << endl;
cout << certstr << endl;
// Show the file and certstr are equal by comparing the hash of their SerialNumber and
hash1 = dipki::X509::HashIssuerAndSN(certfile);
cout << "X509::HashIssuerAndSN(file)=" << hash1 << endl;
hash2 = dipki::X509::HashIssuerAndSN(certstr);
cout << "X509::HashIssuerAndSN(string)=" << hash2 << endl;
assert(hash1 == hash2);
// Use Carl's X.509 certificate to create a "certificate-only" pkcs-12 file. This is useful for importing the certificate into Mozilla/Firefox.
pfxfile = "carl_nokey.p12";
r = dipki::Pfx::MakeFile(pfxfile, "CarlRSASelf.cer");
cout << "Pfx::MakeFile(cert-only) returns " << r << endl;
assert(r == 0);
cout << "Created file " << pfxfile << endl;
cout << "Asn1 type=" << dipki::Asn1::Type(pfxfile) << endl;
// Make a PFX file with three certificates
pfxfile = "bob-3certsEnc.pfx";
password = "password";
n = dipki::Pfx::MakeFile(pfxfile, "BobRSASignByCarl.cer;CarlRSASelf.cer;AliceRSASignByCarl.cer",
"BobPrivRSAEncrypt.p8e", password, "Bob's friendly 3-cert ID");
cout << "Pfx.MakeFile(3-certs) returns " << n << " (expected 0)" << endl;
cout << "Created file '" << pfxfile << "'" << endl;
// Extract all the certificate from a PKCS-12 PFX file as a PKCS-7 certificate chain file
p7file = "p7ChainfromPFX.p7c";
n = dipki::X509::GetP7ChainFromPFX(p7file, pfxfile, "password");
cout << "X509.GetP7ChainFromPFX returns " << n << " (expected +ve)." << endl;
cout << "Created file " << p7file << endl;
cout << "Asn1 type=" << dipki::Asn1::Type(p7file) << endl;
// How many certs in it?
ncerts = dipki::X509::GetCertCountInP7Chain(p7file);
cout << p7file << " contains " << ncerts << " certificates (expected 3)" << endl;
assert(3 == ncerts);
// Extract the second cert in the chain
certfile = "cert2.cer";
isok = dipki::X509::GetCertFromP7Chain(certfile, p7file, 2);
cout << "X509::GetCertFromP7Chain(2) returns " << bool2str(isok) << endl;
assert(isok);
cout << "Created file '" << certfile << "'" << endl;
cout << "subjectName: " << dipki::X509::QueryCert(certfile, "subjectName") << endl;
cout << "Read all certs in order:" << endl;
for (int i = 1; i <= ncerts; i++) {
certstr = dipki::X509::ReadCertStringFromP7Chain(p7file, i);
cout << "cert" << i << ": subjectName: " << dipki::X509::QueryCert(certstr, "subjectName") << endl;
}
}
void test_Ocsp()
{
std::string s;
std::string fname, issuerCert;
std::string certFile, snStr;
cout << "\nOCSP (ONLINE CERTIFICATE STATUS PROTOCOL) TESTS:" << endl;
// Creates an OCSP request to check our own current code signing certificate file dims.cer.
// This was issued by the holder of certificate in the file UTNUSERFirst-Object.cer.
issuerCert = "UTNUSERFirst-Object.cer";
certFile = "dims.cer";
cout << "IssuerFile=" << issuerCert << endl;
cout << "CertFile=" << certFile << endl;
s = dipki::Ocsp::MakeRequest(issuerCert, certFile);
cout << "OCSPRequest=" << s << endl;
assert(s.length() > 0);
// Pass a hex serial number instead of filename
snStr = "#x 00 FB C7 23 22 8C 8C 80 22 D8 85 92 23 DE E7 06 60";
cout << "Cert SerialNumber=" << snStr << endl;
s = dipki::Ocsp::MakeRequest(issuerCert, snStr);
cout << "OCSPRequest=" << s << endl;
assert(s.length() > 0);
// Use a different hash algorithm
snStr = "#x 00 FB C7 23 22 8C 8C 80 22 D8 85 92 23 DE E7 06 60";
cout << "Cert SerialNumber=" << snStr << endl;
s = dipki::Ocsp::MakeRequest(issuerCert, snStr, dipki::Ocsp::HashAlg::Sha256);
cout << "OCSPRequest=" << s << endl;
assert(s.length() > 0);
cout << dipki::Asn1::TextDumpToString(s) << endl;
// We use a response received from ocsp.usertrust.com for our own code signing certificate
// (this HAS been signed by our cert's issuer)
fname = "ocsp_response_ok_dims.dat";
issuerCert = "UTNUSERFirst-Object.cer";
cout << "ResponseFile=" << fname << endl;
cout << "IssuerFile=" << issuerCert << endl;
s = dipki::Ocsp::ReadResponse(fname, issuerCert);
cout << "OCSPResponse=" << s << endl;
assert(s.length() > 0);
}
void test_Compr()
{
cout << "\nCOMPRESS DATA USING ZLIB COMPRESSION:" << endl;
dipki::bvec_t data, cdata, udata;
// Put data into a byte array
data = dipki::str2bvec("hello, hello, hello. This is a 'hello world' message for the world, repeat, for the world.");
// Compress it
cdata = dipki::Compr::Compress(data);
cout << "Compressed " << data.size() << " bytes to " << cdata.size() << endl;
// Now decompress it
udata = dipki::Compr::Uncompress(cdata);
cout << "Uncompressed length is " << udata.size() << endl;
// Show data as an ascii string
cout << "[" << dipki::bvec2str(udata) << "]" << endl;
}
void test_Rsa()
{
cout << "\nRSA TESTS:" << endl;
cout << "Creating an RSA key pair..." << endl;
// Default options
int r = dipki::Rsa::MakeKeys("rsa1024.pub", "rsa1024.p8e", "password", 1024);
cout << "Rsa::MakeKeys returns " << r << " (expecting 0)" << endl;
std::string pubkeyfile, prikeyfile;
pubkeyfile = "rsa1024.pub";
prikeyfile = "rsa1024.p8e";
cout << pubkeyfile << "-->" << dipki::Asn1::Type(pubkeyfile) << endl;
cout << prikeyfile << "-->" << dipki::Asn1::Type(prikeyfile) << endl;
std::string prikeystr, pubkeystr;
prikeystr = dipki::Rsa::ReadPrivateKey(prikeyfile, "password");
cout << "p8e: " << dipki::Rsa::KeyBits(prikeystr) << " bits, " << dipki::Rsa::KeyBytes(prikeystr) << " bytes" << endl;
cout << "KeyIsPrivate=" << (dipki::Rsa::KeyIsPrivate(prikeystr) ? "true" : "false") << endl;
pubkeystr = dipki::Rsa::ReadPublicKey(pubkeyfile);
cout << "pub: " << dipki::Rsa::KeyBits(pubkeystr) << " bits, " << dipki::Rsa::KeyBytes(pubkeystr) << " bytes" << endl;
cout << "KeyIsPrivate=" << (dipki::Rsa::KeyIsPrivate(pubkeystr) ? "true" : "false") << endl;
cout << "KeyMatch=" << (dipki::Rsa::KeyMatch(prikeystr, pubkeystr) ? "true" : "false") << endl;
}
void test_Rsa_Xml()
{
cout << "\nRSA XML TESTS:" << endl;
std::string keystr, xmlstr;
xmlstr = "<RSAKeyValue>"
"<Modulus>CmZ5HcaYgWjeerd0Gbt/sMABxicQJwB1FClC4ZqNjFH"
"QU7PjeCod5dxa9OvplGgXARSh3+Z83Jqa9V1lViC7qw==</Modulus>"
"<Exponent>AQAB</Exponent>"
"</RSAKeyValue>";
keystr = dipki::Rsa::FromXMLString(xmlstr);
cout << keystr << endl;
cout << "KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(keystr) << std::dec << endl;
xmlstr = "<ds:RSAKeyValue>"
"<ds:Modulus>CmZ5HcaYgWjeerd0Gbt/sMABxicQJwB1FClC4ZqNjFH"
"QU7PjeCod5dxa9OvplGgXARSh3+Z83Jqa9V1lViC7qw==</ds:Modulus>"
"<ds:Exponent>AQAB</ds:Exponent>"
"</ds:RSAKeyValue>";
keystr = dipki::Rsa::FromXMLString(xmlstr);
cout << keystr << endl;
cout << "KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(keystr) << std::dec << endl;
cout << "FILE: " << "AlicePrivRSASign.p8e" << endl;
keystr = dipki::Rsa::ReadPrivateKey("AlicePrivRSASign.p8e", "password");
cout << "KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(keystr) << std::dec << endl;
cout << dipki::Rsa::ToXMLString(keystr) << endl;
cout << dipki::Rsa::ToXMLString(keystr, dipki::Rsa::XmlOptions::ExcludePrivateParams | dipki::Rsa::XmlOptions::HexBinaryFormat) << endl;
cout << dipki::Rsa::ToXMLString(keystr, dipki::Rsa::XmlOptions::ExcludePrivateParams, "ds") << endl;
// Now derive internal private key string from XML
xmlstr = dipki::Rsa::ToXMLString(keystr);
keystr = dipki::Rsa::FromXMLString(xmlstr);
cout << "KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(keystr) << std::dec << endl;
// Extract fields in base64 encoding from key string
cout << "Modulus=" << dipki::Rsa::KeyValue(keystr, "Modulus") << endl;
cout << "Exponent=" << dipki::Rsa::KeyValue(keystr, "Exponent") << endl;
}
void test_Rsa_SaveKey()
{
cout << "\nRSA SAVE KEY TESTS:" << endl;
std::string keystr, fname;
int r;
size_t pos;
std::string s;
cout << "FILE: " << "AlicePrivRSASign.p8e" << endl;
keystr = dipki::Rsa::ReadPrivateKey("AlicePrivRSASign.p8e", "password");
cout << "Private KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(keystr) << std::dec << endl;
cout << "Save as an unencrypted private key..." << endl;
fname = "alice_pri.p8";
r = dipki::Rsa::SaveKey(fname, keystr);
cout << "Created file '" << fname << "'" << endl;
cout << dipki::Asn1::Type(fname) << endl;
cout << "Save encrypted with stronger encryption and in PEM form..." << endl;
fname = "alice_pri_ex.p8e";
r = dipki::Rsa::SaveEncKey(fname, keystr, "password1", dipki::Rsa::PbeScheme::Pbkdf2_Aes256, "prf=hmacWithSHA512;count=6000", dipki::Rsa::Format::PEM);
cout << "Created file '" << fname << "'" << endl;
cout << dipki::Asn1::Type(fname) << endl;
// Get first line of file:
cout << read_first_line(fname) << endl;
// Examine the ASN.1 text dump
std::string dump = dipki::Asn1::TextDumpToString(fname);
cout << dump << endl;
pos = dump.find("hmacWithSHA512");
assert(pos != std::string::npos);
cout << "Check: found 'hmacWithSHA512' as expected" << endl;
pos = dump.find("aes256-CBC");
assert(pos != std::string::npos);
cout << "Check: found 'aes256-CBC' as expected" << endl;
cout << "Derive public key from private" << endl;
std::string pubkeystr = dipki::Rsa::PublicKeyFromPrivate(keystr);
cout << "Public KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(pubkeystr) << std::dec << endl;
cout << "Save this as public key in SSL PEM format..." << endl;
fname = "alice_ssl.pem";
r = dipki::Rsa::SaveKey(fname, pubkeystr, dipki::Rsa::Format::SSL);
cout << "Created file '" << fname << "'" << endl;
cout << dipki::Asn1::Type(fname) << endl;
// Get first line of file:
cout << read_first_line(fname) << endl;
// Extract a private key file from PFX
std::string pfxFile = "alice.p12";
cout << "Processing file: " << pfxFile << endl;
cout << dipki::Asn1::Type(pfxFile) << endl;
fname = "alice_from_pfx.p8e";
r = dipki::Rsa::GetPrivateKeyFromPFX(fname, pfxFile);
cout << "Created file '" << fname << "'" << endl;
cout << dipki::Asn1::Type(fname) << endl;
}
void test_Rsa_Encode()
{
cout << "\nRSA ENCODING TESTS:" << endl;
dipki::bvec_t message, block, decoded, digest;
int blocklen = 512 / 8; // We must specify the number of bytes in the key block
cout << "Encode message for encryption (different each time)..." << endl;
message = dipki::Cnv::FromHex("DEADBEEF");
cout << "message=" << dipki::Cnv::ToHex(message) << endl;
cout << "default EME-PKCS1-v1_5:" << endl;
block = dipki::Rsa::EncodeMsgForEncryption(blocklen, message);
cout << "block=" << dipki::Cnv::ToHex(block) << endl;
// Decode
decoded = dipki::Rsa::DecodeMsgForEncryption(block);
cout << "decoded=" << dipki::Cnv::ToHex(decoded) << endl;
assert(dipki::Cnv::ToHex(message) == dipki::Cnv::ToHex(decoded));
cout << "EME-OAEP:" << endl;
block = dipki::Rsa::EncodeMsgForEncryption(blocklen, message, dipki::Rsa::EME::OAEP);
cout << "block=" << dipki::Cnv::ToHex(block) << endl;
// Decode
decoded = dipki::Rsa::DecodeMsgForEncryption(block, dipki::Rsa::EME::OAEP);
cout << "decoded=" << dipki::Cnv::ToHex(decoded) << endl;
assert(dipki::Cnv::ToHex(message) == dipki::Cnv::ToHex(decoded));
cout << "Encode message for signature..." << endl;
cout << "EMSA-PKCS1-v1_5 (default SHA-1):" << endl;
block = dipki::Rsa::EncodeMsgForSignature(blocklen, message);
cout << "block=" << dipki::Cnv::ToHex(block) << endl;
decoded = dipki::Rsa::DecodeMsgForSignature(block);
cout << "decoded=" << dipki::Cnv::ToHex(decoded) << endl;
cout << "Check =" << dipki::Hash::HexFromBytes(message) << endl;
cout << "EMSA-PKCS1-v1_5 (using SHA-256):" << endl;
block = dipki::Rsa::EncodeMsgForSignature(blocklen, message, dipki::Rsa::HashAlg::Sha256);
cout << "block=" << dipki::Cnv::ToHex(block) << endl;
decoded = dipki::Rsa::DecodeMsgForSignature(block);
cout << "decoded=" << dipki::Cnv::ToHex(decoded) << endl;
cout << "Check =" << dipki::Hash::HexFromBytes(message, dipki::Hash::Alg::Sha256) << endl;
// Encode message digest directly for a Signature
// SHA-256(0xDEADBEEF)=5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953
digest = dipki::Cnv::FromHex("5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953");
block = dipki::Rsa::EncodeMsgForSignature(blocklen, digest, dipki::Rsa::HashAlg::Sha256, true);
cout << "block=" << dipki::Cnv::ToHex(block) << endl;
decoded = dipki::Rsa::DecodeMsgForSignature(block);
cout << "decoded=" << dipki::Cnv::ToHex(decoded) << endl;
cout << "Check =" << dipki::Hash::HexFromBytes(message, dipki::Hash::Alg::Sha256) << endl;
// Extract the full digestInfo
decoded = dipki::Rsa::DecodeMsgForSignature(block, true);
cout << "digestInfo=" << dipki::Cnv::ToHex(decoded) << endl;
}
void test_Rsa_Encrypt()
{
cout << "\nTEST RSA ENCRYPT:" << endl;
dipki::bvec_t ct, pt, dt, blk;
std::string pubkeyfile, prikeyfile;
std::string pubkeystr, prikeystr;
int keybytes;
cout << "Encrypt in two parts: encode then do raw encrypt with public key..." << endl;
pt = dipki::str2bvec("Hi Bob"); // Note usually we use RSA to encrypt a session key
pubkeystr = dipki::Rsa::ReadPublicKey("BobRSASignByCarl.cer");
// We need the length of the RSA key modulus in bytes
keybytes = dipki::Rsa::KeyBytes(pubkeystr);
cout << "KEYBYTES=" << keybytes << endl;
blk = dipki::Rsa::EncodeMsgForEncryption(keybytes, pt);
cout << "BK=" << dipki::Cnv::ToHex(blk) << endl;
ct = dipki::Rsa::RawPublic(blk, pubkeystr);
// NB ciphertext is different each time
cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
cout << "Decrypt in two parts: do raw RSA with private key then decode..." << endl;
prikeystr = dipki::Rsa::ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password");
blk = dipki::Rsa::RawPrivate(ct, prikeystr);
cout << "BK=" << dipki::Cnv::ToHex(blk) << endl;
dt = dipki::Rsa::DecodeMsgForEncryption(blk);
cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
// In this case we expect plain ASCII text
cout << "DT='" << dipki::bvec2str(dt) << "'" << endl;
cout << "Same again using simpler Rsa::Encrypt and Rsa::Decrypt..." << endl;
cout << "PT=" << dipki::Cnv::ToHex(pt) << endl;
ct = dipki::Rsa::Encrypt(pt, pubkeystr);
cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
dt = dipki::Rsa::Decrypt(ct, prikeystr);
cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
// In this case we expect plain ASCII text
cout << "DT='" << dipki::bvec2str(dt) << "'" << endl;
cout << "RSAES-OAEP Encryption Example 1.1 from `oaep-vect.txt` in `pkcs-1v2-1-vec.zip`" << endl;
cout << "Rsa::Encrypt" << endl;
pubkeyfile = "rsa-oaep-1.pub";
pt = dipki::Cnv::FromHex("6628194e12073db03ba94cda9ef9532397d50dba79b987004afefe34");
cout << "PT=" << dipki::Cnv::ToHex(pt) << endl;
// Set seed to fixed value for debugging (don't do this in practice)....
ct = dipki::Rsa::Encrypt(pt, pubkeyfile, dipki::Rsa::EME::OAEP, dipki::Rsa::HashAlg::Sha1, dipki::Rsa::AdvOpts::Default, "seed=18b776ea21069d69776a33e96bad48e1dda0a5ef");
cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
assert(dipki::Cnv::ToHex(ct) ==
"354FE67B4A126D5D35FE36C777791A3F7BA13DEF484E2D3908AFF722FAD468FB21696DE95D0BE911C2D3174F8AFCC201035F7B6D8E69402DE5451618C21A535FA9D7BFC5B8DD9FC243F8CF927DB31322D6E881EAA91A996170E657A05A266426D98C88003F8477C1227094A0D9FA1E8C4024309CE1ECCCB5210035D47AC72E8A");
cout << "Rsa::Decrypt" << endl;
// Decrypt using private key
prikeyfile = "rsa-oaep-1.p8"; // unencrypted, no password
dt = dipki::Rsa::Decrypt(ct, prikeyfile, "", dipki::Rsa::EME::OAEP, dipki::Rsa::HashAlg::Sha1);
cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
assert(dipki::Cnv::ToHex(dt) == dipki::Cnv::ToHex(pt));
}
void test_Rsa_Sign()
{
cout << "\nTEST RSA SIGN:" << endl;
dipki::bvec_t message, blk, sig, dig, digvalue;
std::string pubkeystr, prikeystr;
int keybytes;
cout << "Sign in two parts: encode then do raw encrypt with private key..." << endl;
// See also Sig::Sign for a cleaner way.
// Read in private key
prikeystr = dipki::Rsa::ReadPrivateKey("AlicePrivRSASign.p8e", "password");
message = dipki::str2bvec("abc");
// We need the length of RSA key modulus in bytes
keybytes = dipki::Rsa::KeyBytes(prikeystr);
cout << "KEYBYTES=" << keybytes << endl;
// 1. Encode the message in a block of the correct size
// -- this computes the message digest value automatically
blk = dipki::Rsa::EncodeMsgForSignature(keybytes, message);
cout << "BLK=[" << dipki::Cnv::ToHex(blk) << "]" << endl;
// 2. Encrypt the block using "raw" RSA transform
sig = dipki::Rsa::RawPrivate(blk, prikeystr);
cout << "SIG=[" << dipki::Cnv::ToHex(sig) << "]" << endl;
// Read in public key to "decrypt" the signature
pubkeystr = dipki::Rsa::ReadPublicKey("AliceRSASignByCarl.cer");
// 1. Decrypt the signature to a block using "raw" RSA transform
blk = dipki::Rsa::RawPublic(sig, pubkeystr);
cout << "BLK=[" << dipki::Cnv::ToHex(blk) << "]" << endl;
// 2. Decode to extract the digest
dig = dipki::Rsa::DecodeMsgForSignature(blk);
cout << "DIG=[" << dipki::Cnv::ToHex(dig) << "]" << endl;
// Check we got a match: SHA-1('abc') == recovered digest value
assert(dipki::Hash::Bytes(message) == dig);
cout << "Do again but start with digest value, and use SHA-256..." << endl;
digvalue = dipki::Hash::Bytes(dipki::str2bvec("abc"), dipki::Hash::Alg::Sha256);
cout << "SHA256('abc')=" << dipki::Cnv::ToHex(digvalue) << endl;
blk = dipki::Rsa::EncodeMsgForSignature(keybytes, digvalue, dipki::Rsa::HashAlg::Sha256, true);
cout << "BLK=[" << dipki::Cnv::ToHex(blk) << "]" << endl;
sig = dipki::Rsa::RawPrivate(blk, prikeystr);
cout << "SIG=[" << dipki::Cnv::ToHex(sig) << "]" << endl;
// Decrypt and decode
blk = dipki::Rsa::RawPublic(sig, pubkeystr);
cout << "BLK=[" << dipki::Cnv::ToHex(blk) << "]" << endl;
dig = dipki::Rsa::DecodeMsgForSignature(blk);
cout << "DIG=[" << dipki::Cnv::ToHex(dig) << "]" << endl;
// Check we got a match: SHA-256('abc') == recovered digest value
assert(dipki::Hash::Bytes(message, dipki::Hash::Alg::Sha256) == dig);
}
void test_Ecc()
{
cout << "\nECC TESTS:" << endl;
cout << "Creating an ECC key pair..." << endl;
int r;
std::string pubkeyfile, prikeyfile;
std::string prikeystr, pubkeystr;
std::string s, query;
// All default settings...
pubkeyfile = "ecc_test.pub";
prikeyfile = "ecc_test.p8e";
r = dipki::Ecc::MakeKeys(pubkeyfile, prikeyfile, dipki::Ecc::Curve::Ed25519, "password");
cout << "Ecc::MakeKeys returns " << r << " (expecting 0)" << endl;
assert(0 == r);
cout << pubkeyfile << ":" << endl;
cout << dipki::Asn1::Type(pubkeyfile) << endl;
cout << dipki::Asn1::TextDumpToString(pubkeyfile) << endl;
cout << prikeyfile << ":" << endl;
cout << dipki::Asn1::Type(prikeyfile) << endl;
cout << dipki::Asn1::TextDumpToString(prikeyfile) << endl;
// With specialist options...
pubkeyfile = "ecc_test-ex-pub.pem";
prikeyfile = "ecc_test-ex-pri.pem";
// New in [v20.4] use Brainpool curve
r = dipki::Ecc::MakeKeys(pubkeyfile, prikeyfile, dipki::Ecc::Curve::BrainpoolP512r1, "password",
dipki::Ecc::PbeScheme::Pbkdf2_Aes128, "count=3999;prf=hmacWithSha256", dipki::Ecc::Format::PEM);
cout << "Ecc::MakeKeys returns " << r << " (expecting 0)" << endl;
assert(0 == r);
cout << pubkeyfile << ":" << endl;
cout << dipki::Asn1::Type(pubkeyfile) << endl;
cout << dipki::Asn1::TextDumpToString(pubkeyfile) << endl;
cout << prikeyfile << ":" << endl;
cout << dipki::Asn1::Type(prikeyfile) << endl;
cout << dipki::Asn1::TextDumpToString(prikeyfile) << endl;
// Read keys into ephemeral internal key strings (encrypted and different each time)
prikeystr = dipki::Ecc::ReadPrivateKey(prikeyfile, "password");
cout << "INT PRI KEYSTR=" << prikeystr << endl;
pubkeystr = dipki::Ecc::ReadPublicKey(pubkeyfile);
cout << "INT PUB KEYSTR=" << pubkeystr << endl;
// Query the key strings
cout << "Querying the private key '" << prikeyfile << "'" << endl;
query = "curveName";
s = dipki::Ecc::QueryKey(prikeystr, query);
cout << " " << query << "=" << s << endl;
query = "keyBits";
s = dipki::Ecc::QueryKey(prikeystr, query);
cout << " " << query << "=" << s << endl;
query = "isPrivate";
s = dipki::Ecc::QueryKey(prikeystr, query);
cout << " " << query << "=" << s << endl;
cout << "Querying the public key '" << pubkeyfile << "'" << endl;
query = "curveName";
s = dipki::Ecc::QueryKey(pubkeystr, query);
cout << " " << query << "=" << s << endl;
query = "keyBits";
s = dipki::Ecc::QueryKey(pubkeystr, query);
cout << " " << query << "=" << s << endl;
query = "isPrivate";
s = dipki::Ecc::QueryKey(pubkeystr, query);
cout << " " << query << "=" << s << endl;
cout << "EXPECTING ERRORS..." << endl;
try {
s = s = dipki::Ecc::QueryKey(pubkeystr, "bad-query");
cout << " " << query << "=" << s << endl;
}
catch (std::exception& e) {
cout << e.what() << endl;
}
try {
s = s = dipki::Ecc::QueryKey("bad-key-string", query);
cout << " " << query << "=" << s << endl;
}
catch (std::exception& e) {
cout << e.what() << endl;
}
cout << "...END ERRORS." << endl;
// Clear private key string
dipki::Wipe::String(prikeystr);
}
void test_Ecc_ByCurve()
{
cout << "\nECC CREATE CURVE TESTS:" << endl;
// Create ECC keys using dta in hex strings - no files required.
std::string keystr;
std::string hexKey, b58key;
dipki::Ecc::Curve cvname;
int32_t hashcode1, hashcode2;
cout << "1. A NIST P-192 public key in X9.63 uncompressed format..." << endl;
hexKey =
"0496C248BE456192FA1380CCF615D171452F41FF31B92BA733524FD77168DEA4425A3EA8FD79B98DC7AFE83C86DCC39A96";
cvname = dipki::Ecc::Curve::Prime192v1;
cout << "KEYHEX: " << hexKey << endl;
cout << "CURVE: " << dipki::Ecc::CurveNameAsString(cvname) << endl;
keystr = dipki::Ecc::ReadKeyByCurve(hexKey, cvname);
cout << "keyBits=" << dipki::Ecc::QueryKey(keystr, "keyBits") << endl;
cout << "curveName=" << dipki::Ecc::QueryKey(keystr, "curveName") << endl;
cout << "isPrivate=" << dipki::Ecc::QueryKey(keystr, "isPrivate") << endl;
cout << "2. A Bitcoin private key in base58 form..." << endl;
b58key = "6ACCbmy9qwiFcuVgvxNNwMPfoghobzznWrLs3v7t3RmN";
cout << "KEYB58: " << b58key << endl;
cvname = dipki::Ecc::Curve::Secp256k1;
// Convert key to hex
hexKey = dipki::Cnv::ToHex(dipki::Cnv::FromBase58(b58key));
cout << "KEYHEX: " << hexKey << endl;
cout << "CURVE: " << dipki::Ecc::CurveNameAsString(cvname) << endl;
keystr = dipki::Ecc::ReadKeyByCurve(hexKey, cvname);
cout << "keyBits=" << dipki::Ecc::QueryKey(keystr, "keyBits") << endl;
cout << "curveName=" << dipki::Ecc::QueryKey(keystr, "curveName") << endl;
cout << "isPrivate=" << dipki::Ecc::QueryKey(keystr, "isPrivate") << endl;
// We can compute the public key given the private key
cout << "publicKey=" << dipki::Ecc::QueryKey(keystr, "publicKey") << endl;
cout << "3. A X25519 private key..." << endl;
hexKey =
"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a";
cvname = dipki::Ecc::Curve::X25519;
cout << "KEYHEX: " << hexKey << endl;
cout << "CURVE: " << dipki::Ecc::CurveNameAsString(cvname) << endl;
keystr = dipki::Ecc::ReadKeyByCurve(hexKey, cvname);
cout << "keyBits=" << dipki::Ecc::QueryKey(keystr, "keyBits") << endl;
cout << "curveName=" << dipki::Ecc::QueryKey(keystr, "curveName") << endl;
cout << "isPrivate=" << dipki::Ecc::QueryKey(keystr, "isPrivate") << endl;
// We can compute the public key given the private key
cout << "publicKey=" << dipki::Ecc::QueryKey(keystr, "publicKey") << endl;
hashcode1 = dipki::Ecc::KeyHashCode(keystr);
cout << "KeyHashCode=0x" << std::hex << hashcode1 << std::dec << endl;
cout << "4. X25519 public key (matching previous private key)..." << endl;
hexKey =
"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a";
cvname = dipki::Ecc::Curve::X25519;
cout << "KEYHEX: " << hexKey << endl;
cout << "CURVE: " << dipki::Ecc::CurveNameAsString(cvname) << endl;
keystr = dipki::Ecc::ReadKeyByCurve(hexKey, cvname, dipki::Ecc::Publicity::PublicKey); // NB _must_ specify if public for safe curves
cout << "keyBits=" << dipki::Ecc::QueryKey(keystr, "keyBits") << endl;
cout << "curveName=" << dipki::Ecc::QueryKey(keystr, "curveName") << endl;
cout << "isPrivate=" << dipki::Ecc::QueryKey(keystr, "isPrivate") << endl;
hashcode2 = dipki::Ecc::KeyHashCode(keystr);
cout << "KeyHashCode=0x" << std::hex << hashcode2 << std::dec << endl;
// Hash codes of private and public key should match
assert(hashcode1 == hashcode2);
// One-liners used in documentation examples
cout << dipki::Ecc::CurveNameAsString(dipki::Ecc::Curve::Secp256r1) << endl;
// Secp256r1
}
void test_Ecc_SaveKey()
{
cout << "\nECC SAVE KEY TESTS:" << endl;
// Read and save EC keys.
std::string keyfile;
std::string prikeystr, pubkeystr;
int r;
// An encrypted private key file we made earlier, saved with default options
keyfile = "ecc_test.p8e";
cout << "FILE: " << keyfile << endl;
cout << dipki::Asn1::Type(keyfile) << endl;
// Read in key to internal key string
prikeystr = dipki::Ecc::ReadPrivateKey(keyfile, "password");
cout << "keyBits=" << dipki::Ecc::QueryKey(prikeystr, "keyBits") << endl;
cout << "isPrivate=" << dipki::Ecc::QueryKey(prikeystr, "isPrivate") << endl;
cout << "KeyHashCode=0x" << std::hex << dipki::Ecc::KeyHashCode(prikeystr) << std::dec << endl;
// Save as an unencrypted file in PKCS#8 format
keyfile = "ecc_test_unenc.p8";
r = dipki::Ecc::SaveKey(keyfile, prikeystr, dipki::Ecc::KeyType::Pkcs8PrivateKeyInfo);
cout << "Created new file '" << keyfile << "'" << endl;
cout << dipki::Asn1::Type(keyfile) << endl;
// Save with stronger encryption and in textual PEM form (and using a "stronger" password :-)
keyfile = "ecc_test_stronger.p8e";
r = dipki::Ecc::SaveEncKey(keyfile, prikeystr, "password1", dipki::Ecc::PbeScheme::Pbkdf2_Aes128, "count=6000;prf=hmacwithSHA256", dipki::Ecc::Format::PEM);
cout << "Created new file '" << keyfile << "'" << endl;
cout << dipki::Asn1::Type(keyfile) << endl;
// Derive the equivalent public key from the private key
pubkeystr = dipki::Ecc::PublicKeyFromPrivate(prikeystr);
cout << "isPrivate=" << dipki::Ecc::QueryKey(pubkeystr, "isPrivate") << endl;
cout << "KeyHashCode=0x" << std::hex << dipki::Ecc::KeyHashCode(pubkeystr) << std::dec << endl;
// Save public key to a new file in PEM format
keyfile = "ecc_test_pub_new.pem";
r = dipki::Ecc::SaveKey(keyfile, pubkeystr);
cout << "Created new file '" << keyfile << "'" << endl;
cout << dipki::Asn1::Type(keyfile) << endl;
}
void test_Ecc_ECDH()
{
cout << "\nECC DH SHARED SECRET TESTS:" << endl;
std::string ourkey, theirkey, correcthex;
dipki::Ecc::Curve curve;
dipki::bvec_t zz;
// Ref: CAVS 14.1 ECC CDH Primitive (SP800 - 56A Section 5.7.1.2)
// Test Information for "testecccdh" ecccdhtestvectors.zip
curve = dipki::Ecc::Curve::P_256;
cout << "Test for curve " << dipki::Ecc::CurveNameAsString(curve) << " ..." << endl;
// Our private key is dUIT:
ourkey = dipki::Ecc::ReadKeyByCurve("7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534", curve);
cout << "Our private key has " << dipki::Ecc::QueryKey(ourkey, "keyBits") << " bits" << endl;
// Their public key as hex is "04" || QCAVSx || QCAVSy
theirkey = dipki::Ecc::ReadKeyByCurve("04" "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
"db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac", curve);
cout << "Their public key has " << dipki::Ecc::QueryKey(theirkey, "keyBits") << " bits" << endl;
// Compute shared secret
zz = dipki::Ecc::DHSharedSecret(ourkey, theirkey);
cout << "ZZ=" << dipki::Cnv::ToHex(zz) << endl;
correcthex = "46FC62106420FF012E54A434FBDD2D25CCC5852060561E68040DD7778997BD7B";
cout << "OK=" << correcthex << endl;
assert(correcthex == dipki::Cnv::ToHex(zz));
// Ref: RFC7748 Section 6.1. X25519 function in an Elliptic Curve Diffie-Hellman (ECDH) protocol.
std::string alice_prikey = "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a";
std::string alice_pubkey = "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a";
std::string bob_prikey = "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb";
std::string bob_pubkey = "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f";
correcthex = "4A5D9D5BA4CE2DE1728E3BF480350F25E07E21C947D19E3376F09B3C1E161742";
// NB Using ReadKeyByCurve with X25519 we _must_ specify if key value represents a public or private key
curve = dipki::Ecc::Curve::X25519;
cout << "Test for curve " << dipki::Ecc::CurveNameAsString(curve) << " ..." << endl;
cout << "1. Alice's private + Bob's public" << endl;
ourkey = dipki::Ecc::ReadKeyByCurve(alice_prikey, curve, dipki::Ecc::Publicity::PrivateKey);
theirkey = dipki::Ecc::ReadKeyByCurve(bob_pubkey, curve, dipki::Ecc::Publicity::PublicKey);
// Compute shared secret
zz = dipki::Ecc::DHSharedSecret(ourkey, theirkey);
cout << "ZZ=" << dipki::Cnv::ToHex(zz) << endl;
assert(correcthex == dipki::Cnv::ToHex(zz));
cout << "2. Bob's private + Alice's public" << endl;
ourkey = dipki::Ecc::ReadKeyByCurve(bob_prikey, curve, dipki::Ecc::Publicity::PrivateKey);
theirkey = dipki::Ecc::ReadKeyByCurve(alice_pubkey, curve, dipki::Ecc::Publicity::PublicKey);
// Compute shared secret
zz = dipki::Ecc::DHSharedSecret(ourkey, theirkey);
cout << "ZZ=" << dipki::Cnv::ToHex(zz) << endl;
assert(correcthex == dipki::Cnv::ToHex(zz));
}
void test_Sig()
{
cout << "\nTESTING SIG:" << endl;
std::string sig, keyFile, password, certFile, dataFile;
std::string correct;
dipki::bvec_t data;
bool isok;
keyFile = "AlicePrivRSASign.p8e"; // Used to sign data
certFile = "AliceRSASignByCarl.cer"; // Used to verify signature
password = "password"; // CAUTION: DO NOT HARD-CODE REAL PASSWORDS
// Create RSA signatures over the string "abc"
// -- these are all known values we can check for a given private key
data = dipki::str2bvec("abc");
// Default rsa-sha1
cout << "Alice signs 'abc' using default SHA-1 with RSA" << endl;
correct = // Alice/'abc'/SHA-1
"YK1aePtKQDDsVCyJdM0V9VOE6DZVTO3ZoyLV9BNcYmep0glwxU5mUQcLAUTUOETImTIN2Pp4Gffr"
"xqdxUoczLshnXBNhg7P4ofge+WlBgmcTCnVv27LHHZpmdEbjTg6tnPMb+2b4FvMZ0LfkMKXyiRVTmG4A"
"NyAmHH6QIsDZ8R8=";
sig = dipki::Sig::SignData(data, keyFile, password);
cout << sig << endl;
assert(sig == correct);
// Verify the signature we just made
isok = dipki::Sig::VerifyData(sig, data, certFile);
cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
assert(isok);
cout << "Alice signs 'abc' using SHA-256 with RSA" << endl;
correct = // Alice/'abc'/SHA-256
"tLy6hJadL4w9JI/A/qLCG0Vz1fWPIrPMWD8NGmA5wP7HHlUID54elztUYrpdm9RFeh0RCMJ618dw"
"BpgIutuZg2qQ2i9uMUYB0DvZvkyeD6MqmtVa4ihgc9SLqhigKeP+KB7voEi7PH3hpEr9Wa3kn4mbPpeD"
"1VHSzgu/qirjOaA=";
sig = dipki::Sig::SignData(data, keyFile, password, dipki::Sig::Alg::Rsa_Sha256);
cout << sig << endl;
assert(sig == correct);
// Verify the signature we just made
isok = dipki::Sig::VerifyData(sig, data, certFile, dipki::Sig::Alg::Rsa_Sha256);
cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
assert(isok);
}
void test_Sig_Ecc()
{
cout << "\nTESTING SIG ECC:" << endl;
std::string prikeyfile = "p-256.p8e";
std::string pubkeyfile = "p-256.pub";
std::string password = "password";
std::string sig, keystr;
dipki::Sig::Alg sigalg = dipki::Sig::Alg::Ecdsa_Sha256;
int r;
bool isok;
// One-liner
cout << dipki::Sig::AlgName(dipki::Sig::Alg::Rsa_Sha256) << endl;
// rsa-sha256
// 1. Make a new 256-bit keypair using P-256 curve (`Prime256v1` is a synonym)
r = dipki::Ecc::MakeKeys(pubkeyfile, prikeyfile, dipki::Ecc::Curve::Prime256v1, password);
cout << "Ecc::MakeKeys returns " << r << " (expected 0)" << endl;
// 1a. Query the keys we just made, just to check
keystr = dipki::Ecc::ReadPrivateKey(prikeyfile, password);
cout << "curveName=" << dipki::Ecc::QueryKey(keystr, "curveName") << endl;
cout << "keyBits=" << dipki::Ecc::QueryKey(keystr, "keyBits") << endl;
// A. Sign the DATA
cout << "SigAlgorithm: " << dipki::Sig::AlgName(sigalg) << endl;
dipki::bvec_t data = dipki::str2bvec("abc");
sig = dipki::Sig::SignData(data, prikeyfile, password, sigalg);
// Different each time
cout << sig << endl;
// Verify the signature over the DATA
isok = dipki::Sig::VerifyData(sig, data, pubkeyfile, sigalg);
cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
assert(isok);
// B. Sign the DIGEST of the data
// Get the hash digest alg from the signature alg
dipki::Hash::Alg hashalg = dipki::Hash::HashAlgFromString(dipki::Sig::GetHashNameFromSigAlg(sigalg));
cout << "Hash::AlgName=" << dipki::Hash::AlgName(hashalg) << endl;
dipki::bvec_t digest = dipki::Hash::Bytes(dipki::str2bvec("abc"), hashalg);
cout << "digestValue=" << dipki::Cnv::ToHex(digest) << endl;
sig = dipki::Sig::SignDigest(digest, prikeyfile, password, sigalg);
cout << sig << endl;
// Verify the signature over the DIGEST
isok = dipki::Sig::VerifyDigest(sig, digest, pubkeyfile, sigalg);
cout << "Sig::VerifyDigest returns " << (isok ? "true" : "false") << endl;
assert(isok);
// Make sure we can verify over the original data
isok = dipki::Sig::VerifyData(sig, data, pubkeyfile, sigalg);
cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
assert(isok);
// C. Sign a FILE containing the data
std::string fname = "abc.dat";
write_binary_file(fname, dipki::str2bvec("abc"));
cout << "Created file '" << fname << "'" << endl;
dipki::bvec_t bv = read_binary_file(fname);
cout << dipki::Cnv::ToHex(bv) << endl;
sig = dipki::Sig::SignFile(fname, prikeyfile, password, sigalg);
cout << sig << endl;
// Verify the signature over the FILE
isok = dipki::Sig::VerifyFile(sig, fname, pubkeyfile, sigalg);
cout << "Sig::VerifyFile returns " << (isok ? "true" : "false") << endl;
assert(isok);
// Make sure we can verify over the original data
isok = dipki::Sig::VerifyData(sig, data, pubkeyfile, sigalg);
cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
assert(isok);
};
void test_Sig_Ecc_det()
{
cout << "\nTESTING SIG ECC DETERMINISTIC:" << endl;
std::string sig, intKey, intPubKey, hexKey;
std::string sigdetok = "0f2141a0ebbc44d2e1af90a50ebcfce5e197b3b7d4de036deb18bc9e1f3d7387500cb99cf5f7c157070a8961e38700b7";
dipki::Ecc::Curve curve;
dipki::bvec_t data;
bool isok;
// Ref: [RFC6979] "Deterministic Usage of the DSA and ECDSA"
// A.2.3. ECDSA, 192 Bits (Prime Field)
// 1. READ IN PRIVATE KEY IN (HEX,CURVENAME) FORMAT
hexKey = "6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4";
curve = dipki::Ecc::Curve::P_192;
cout << "KEYHEX: " << hexKey << endl;
cout << "CURVE: " << dipki::Ecc::CurveNameAsString(curve) << endl;
intKey = dipki::Ecc::ReadKeyByCurve(hexKey, curve);
// 2. PREPARE INPUT DATA AS AN ARRAY OF BYTES
data = dipki::str2bvec("test");
// 3. SIGN IT USING DETERMINISTIC ALGORITHM FROM [RFC6979]
// with output in hex encoding
// -- deterministic method always gives the same signature value for same input
sig = dipki::Sig::SignData(data, intKey, "", dipki::Sig::Alg::Ecdsa_Sha1, dipki::Sig::Encoding::Base16, dipki::Sig::SigOptions::UseDeterministic);
cout << "SIG(hex): " << sig << endl;
cout << "SIG(ok): " << sigdetok << endl;
assert(sig == sigdetok);
// 4. VERIFY IT WITH THE PUBLIC KEY
intPubKey = dipki::Ecc::PublicKeyFromPrivate(intKey);
isok = dipki::Sig::VerifyData(sig, data, intPubKey, dipki::Sig::Alg::Ecdsa_Sha1);
cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
assert(isok);
// SIGN IT AGAIN WITH SIG IN ASN1DER FORM AND ENCODE IN "URL_SAFE" BASE64URL
sig = dipki::Sig::SignData(data, intKey, "", dipki::Sig::Alg::Ecdsa_Sha1, dipki::Sig::Encoding::Base64url,
dipki::Sig::SigOptions::UseDeterministic | dipki::Sig::SigOptions::Asn1DERStructure);
cout << "SIG(base64url): " << sig << endl;
// VERIFY IT AGAIN (note the encoding is detected automatically by Sig::Verifydata)
isok = dipki::Sig::VerifyData(sig, data, intPubKey, dipki::Sig::Alg::Ecdsa_Sha1);
cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
assert(isok);
};
void test_Sig_PSS()
{
cout << "\nSIGN DATA USING RSA-PSS:" << endl;
std::string sigval, oksig;
dipki::bvec_t msg;
bool isok;
std::string certFile = "AliceRSAPssSignByCarl.cer";
std::string priKeyFile = "AliceRSAPSS.p8e"; // pkcs8-encrypted
std::string mypassword = "password";
// Input data = three-char ASCII string "abc"
msg = dipki::str2bvec("abc");
cout << "MSG: " << dipki::Cnv::ToHex(msg) << endl;
cout << "Sign using RSA-PSS-SHA256 (different each time)" << endl;
sigval = dipki::Sig::SignData(msg, priKeyFile, mypassword, dipki::Sig::Alg::Rsa_Pss_Sha256);
cout << "SIG: " << sigval << endl;
cout << "Verify using X.509 certificate" << endl;
// NOTE We must specify the signature algorithm used to sign it
isok = dipki::Sig::VerifyData(sigval, msg, certFile, dipki::Sig::Alg::Rsa_Pss_Sha256);
cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
assert(isok);
cout << "Sign using RSA-PSS-SHA256 with zero-length salt (so signature is deterministic) and MGF1-with-SHA1 (just to be awkward)" << endl;
// (This should be the same each time)
sigval = dipki::Sig::SignData(msg, priKeyFile, mypassword, dipki::Sig::Alg::Rsa_Pss_Sha256, dipki::Sig::Encoding::Base64,
dipki::Sig::SigOptions::PssSaltLenZero | dipki::Sig::SigOptions::Mgf1Sha1);
cout << "SIG: " << sigval << endl;
assert(sigval.length() > 0);
// Known result
oksig = "e0eb1ocnqg9raQBcjPYhjI5+l23P8aNWUHSPgaW5mUlfh2py1IjD1c/TDGQoIpQisbaBq0yDB4DSSpW0p9RqeQK33bELI3KM6A83q/5+J76WY/ZCPDSGJRTtovV4jiRSvoqPqcL/bJcQv1j9pqH2tTQmdSgArCYAntnLpBLYR2bbm3Q/1hnrs1T8CttYje+qSPvFAmShxSS8ryIm5POj6p2aXXtdDqo47B46nYeHAVArPUT1CKEXMelZWItlTWEjzqnofLO+nquYIpb7gNBExSfkwxTbBHa88UPu35eEe0AfLaxqEudi7YCAZ6/8cBC4MUXlx8Th6PQ5kPKN+i+ibw==";
assert(sigval == oksig);
cout << "Verify using X.509 certificate" << endl;
// We must specify the signature algorithm used to sign it and must specify MGF1-with-SHA1, since we used that option
// (however, salt length can be detected automatically from signature value)
isok = dipki::Sig::VerifyData(sigval, msg, certFile, dipki::Sig::Alg::Rsa_Pss_Sha256, dipki::Sig::VerifyOpts::Mgf1Sha1);
cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
assert(isok);
}
void test_X509_MakeCert()
{
cout << "\nTEST X509 MAKE CERT:" << endl;
int r, n;
bool isok;
std::string s, certFile, query;
std::string newCertFile = "myuser.cer";
std::string issuerCert = "myca.cer";
std::string subjectPubKeyFile = "mykey.pub";
std::string issuerPriKeyFile = "myca.p8e";
std::string password = "password";
int certNum = 0x101;
int yearsValid = 4;
std::string distName = "CN=My User;O=Test Org;OU=Unit;C=AU;L=My Town;S=State;E=myuser@testorg.com";
std::string email = "myuser@testorg.com";
r = dipki::X509::MakeCert(newCertFile, issuerCert, subjectPubKeyFile, issuerPriKeyFile, password, certNum, yearsValid, distName, email);
cout << "X509::MakeCert returns " << r << endl;
cout << "Created file '" << newCertFile << "'" << endl;
cout << dipki::Asn1::TextDumpToString(newCertFile) << endl;
// Make an end-user cert identical to RFC4134 AliceRSASignByCarl.cer
subjectPubKeyFile = "AlicePubRSA.pub";
issuerCert = "CarlRSASelf.cer";
issuerPriKeyFile = "CarlPrivRSASign.p8e";
password = "password";
newCertFile = "AliceRSA-newdup.cer";
std::string dn = "CN=AliceRSA";
std::string extns = "rfc822name=AliceRSA@example.com;"
"serialNumber=46346BC7800056BC11D36E2EC410B3B0;"
"subjectKeyIdentifier=77D2B4D1B74C8A8AA3CE459DCEEC3CA03AE3FF50;"
"notBefore=1999-09-19T01:08:47;"
"notAfter=2039-12-31;";
dipki::X509::KeyUsageOptions kuo = dipki::X509::KeyUsageOptions::DigitalSignature | dipki::X509::KeyUsageOptions::NonRepudiation;
r = dipki::X509::MakeCert(newCertFile, issuerCert, subjectPubKeyFile, issuerPriKeyFile, password, 0, 0, dn, extns, kuo,
dipki::X509::SigAlg::Default, dipki::X509::CertOptions::AuthKeyId);
cout << "X509::MakeCert returns " << r << endl;
cout << "Created file '" << newCertFile << "'" << endl;
cout << dipki::Asn1::TextDumpToString(newCertFile) << endl;
cout << "CERTTHUMB=" << dipki::X509::CertThumb(newCertFile) << endl;
assert("b30c48855055c2e64ce3196492d4b83831a6b3cb" == dipki::X509::CertThumb(newCertFile));
certFile = newCertFile;
cout << "FILE: " << certFile << endl;
query = "sigAlgId";
s = dipki::X509::QueryCert(certFile, query);
cout << "X509::CertQuery(" << query << ")=" << s << endl;
n = static_cast<int>(dipki::X509::SigAlg::Rsa_Sha1);
cout << "static_cast<int>(dipki::X509::SigAlg::Rsa_Sha1)=" << n << endl;
query = "signatureAlgorithm";
s = dipki::X509::QueryCert(certFile, query);
cout << "X509::CertQuery(" << query << ")=" << s << endl;
query = "notAfter";
s = dipki::X509::QueryCert(certFile, query);
cout << "X509::CertQuery(" << query << ")=" << s << endl;
// Check that cert is valid now as per system clock (this will fail in 2039!)
isok = dipki::X509::CertIsValidNow(certFile);
cout << "X509::CertIsValidNow returns " << bool2str(isok) << endl;
assert(isok);
cout << "\nMake a small self-signed X.509 v1 certificate with 386-bit key" << endl;
// Private key is not encrypted...
std::string prikeypem = "-----BEGIN PRIVATE KEY-----"
"MIIBCwIBADANBgkqhkiG9w0BAQEFAASB9jCB8wIBAAIxAvMGwkRKuvMz7WUyRrdc"
"b975lopHv6/xJWbCHJ52y2Sp8JEdsxWAtWbnZMW5XtxJTwIBAwIxAfdZ1tgx0fd3"
"85jMLyToSpSmZFwv1R/2FqSFvNj9UDrBBqGzYLDj//YyqiLR6D6QuwIZAcleIuXA"
"ieV8YHWXdDtfOYtthwwBQfRdOwIZAaabXnM6SScMBij5LdDLe+ot3oV9QIoS/QIZ"
"ATDpbJkrBpj9laO6TXzqJlzzr11WK/g+JwIZARm86aIm229dWXCmHosyUpwelFj+"
"Kwa3UwIZAKZZqflnwaG4QAVMqU3SLN9ZcIOpvEt+7w=="
"-----END PRIVATE KEY-----";
std::string pubkeypem = "-----BEGIN RSA PUBLIC KEY-----"
"MDYCMQLzBsJESrrzM+1lMka3XG/e+ZaKR7+v8SVmwhyedstkqfCRHbMVgLVm52TF"
"uV7cSU8CAQM="
"-----END RSA PUBLIC KEY-----";
// Check keys
cout << "Public key has " << dipki::Rsa::KeyBits(dipki::Rsa::ReadPublicKey(pubkeypem)) << " bits" << endl;
cout << "Private key has " << dipki::Rsa::KeyBits(dipki::Rsa::ReadPrivateKey(prikeypem)) << " bits" << endl;
cout << "Rsa::KeyMatch=" << dipki::Rsa::KeyMatch(dipki::Rsa::ReadPrivateKey(prikeypem), dipki::Rsa::ReadPublicKey(pubkeypem)) << endl;
newCertFile = "tiny.cer";
r = dipki::X509::MakeCertSelf(newCertFile, prikeypem, "", 2, 0, "CN=A", "notbefore=1999-09-10T01:08:47;notafter=2039-12-31T23:59:59",
dipki::X509::KeyUsageOptions::NoKeyUsageOption, dipki::X509::SigAlg::Rsa_Sha1, dipki::X509::CertOptions::VersionOne);
cout << "X509::MakeCert returns " << r << endl;
cout << "Created file '" << newCertFile << "'" << endl;
cout << dipki::Asn1::TextDumpToString(newCertFile) << endl;
cout << "CERTTHUMB=" << dipki::X509::CertThumb(newCertFile) << endl;
certFile = newCertFile;
query = "version";
s = dipki::X509::QueryCert(certFile, query);
cout << "X509::CertQuery(" << query << ")=" << s << endl;
}
void test_X509_Certs()
{
cout << "\nTEST X509 CERTS:" << endl;
std::string s, certFile, query, newFile, thumb;
int r;
// A certificate file with Spanish characters in the distinguished name
certFile = "alicia.cer";
cout << "\nFILE: " << certFile << endl;
// Query the issuerName, which contains non-ASCII characters
query = "issuerName";
s = dipki::X509::QueryCert(certFile, query);
cout << "X509::CertQuery(" << query << ", default):\n" << s << endl;
s = dipki::X509::QueryCert(certFile, query, dipki::X509::OutputOpts::Ldap);
cout << "X509::CertQuery(" << query << ", Ldap):\n" << s << endl;
s = dipki::X509::QueryCert(certFile, query, dipki::X509::OutputOpts::UTF8);
cout << "X509::CertQuery(" << query << ", UTF8):\n" << s << endl;
s = dipki::X509::QueryCert(certFile, query, dipki::X509::OutputOpts::Latin1);
cout << "CAUTION: OutputOpts::Latin1 does not work properly on a UTF-8 console!" << endl;
cout << "X509::CertQuery(" << query << ", Latin1): (NOTE WRONG!)\n" << s << endl;
query = "serialNumber";
s = dipki::X509::QueryCert(certFile, query);
cout << "X509::CertQuery(" << query << "): " << s << endl;
s = dipki::X509::QueryCert(certFile, query, dipki::X509::OutputOpts::Decimal);
cout << "X509::CertQuery(" << query << ", Decimal): " << s << endl;
cout << "TextDump using default, Ldap and UTF8 output options:" << endl;
cout << dipki::X509::TextDumpToString(certFile) << endl;
// Truncate these two examples
cout << dipki::X509::TextDumpToString(certFile, dipki::X509::OutputOpts::Decimal | dipki::X509::OutputOpts::Ldap).substr(0, 300) << "/[cut]...\n" << endl;
cout << dipki::X509::TextDumpToString(certFile, dipki::X509::OutputOpts::Decimal | dipki::X509::OutputOpts::UTF8).substr(0, 300) << "/[cut]...\n" << endl;
cout << "Read certfile into a base64 string..." << endl;
s = dipki::X509::ReadStringFromFile(certFile);
cout << s << endl;
newFile = "newcert.pem";
cout << "Save base64 string to new file '" << newFile << "'..." << endl;
r = dipki::X509::SaveFileFromString(newFile, s, true);
cout << read_first_line(newFile) << endl;
// All three forms should give the same thumbprint
cout << "Check thumbprints of all three certificate forms above..." << endl;
thumb = dipki::X509::CertThumb(certFile);
cout << thumb << endl;
cout << dipki::X509::CertThumb(s) << endl;
assert(thumb == dipki::X509::CertThumb(s));
cout << dipki::X509::CertThumb(newFile) << endl;
assert(thumb == dipki::X509::CertThumb(newFile));
// A certificate file signed using RSA-PSS
certFile = "AliceRSAPssSignByCarl.cer";
cout << "\nFILE: " << certFile << endl;
query = "signatureAlgorithm";
s = dipki::X509::QueryCert(certFile, query);
cout << "X509::CertQuery(" << query << "): " << s << endl;
query = "pssParams";
s = dipki::X509::QueryCert(certFile, query);
cout << "X509::CertQuery(" << query << "): " << s << endl;
cout << dipki::X509::TextDumpToString(certFile) << endl;
cout << "Read certfile into a base64 string..." << endl;
s = dipki::X509::ReadStringFromFile(certFile);
cout << s << endl;
cout << "Check thumbprints of original cert and its base64 string..." << endl;
thumb = dipki::X509::CertThumb(certFile);
cout << thumb << endl;
cout << dipki::X509::CertThumb(s) << endl;
assert(thumb == dipki::X509::CertThumb(s));
}
void test_X509_CertRequest()
{
cout << "\nTEST CERT SIGNING REQUEST:" << endl;
int r;
bool isok;
std::string prikeyFile, csrFile;
// Make a certificate signing request using an RSA private key
prikeyFile = "AlicePrivRSASign.p8e";
csrFile = "alice_csr.p10";
r = dipki::X509::CertRequest(csrFile, prikeyFile, "password", "CN=alice;O=Test Org;C=AU;L=Sydney;S=NSW");
cout << "X509::CertRequest returns " << r << endl;
cout << "Created file '" << csrFile << "'" << endl;
cout << dipki::X509::TextDumpToString(csrFile) << endl;
// Again using extensions and stronger signature algorithm (yes, we know!)
csrFile = "alice_csr_ex.p10";
r = dipki::X509::CertRequest(csrFile, prikeyFile, "password", "CN=alice;O=Test Org;C=AU;L=Sydney;S=NSW",
"rfc822name=fred@example.com;dnsname=fred.example.com;uri=ftp://ftp.example.com/;ipaddress=192.168.15.1",
dipki::X509::SigAlg::Rsa_Sha256, dipki::X509::CsrOptions::FormatBinary_Csr);
cout << "X509::CertRequest returns " << r << endl;
cout << "Created file '" << csrFile << "'" << endl;
cout << dipki::X509::TextDumpToString(csrFile) << endl;
// Use VerifyCert to check the signature in a CSR (we don't need the issuerCert)
isok = dipki::X509::CertIsVerified(csrFile, "");
cout << "X509::CertIsVerified returns " << (isok ? "true" : "false") << endl;
assert(isok);
}
void test_X509_CRL()
{
cout << "\nTEST CRL (CERTIFICATE REVOCATION LIST):" << endl;
int r;
bool isok;
std::string prikeyFile, crlFile, certFile, issuerCert, password, revokedCertList, extns, dateStr;
// Carl creates a Certificate Revocation List (CRL)
// -- A CRL dated with the current system time with default options
crlFile = "CarlsNew.crl";
issuerCert = "CarlRSASelf.cer";
prikeyFile = "CarlPrivRSASign.p8e";
password = "password";
revokedCertList = "1,2007-12-31; 2, 2009-12-31T12:59:59Z; 66000,2066-01-01; #x0102deadbeef,2010-02-28T01:01:59";
r = dipki::X509::MakeCRL(crlFile, issuerCert, prikeyFile, password, revokedCertList);
cout << "X509::MakeCRL returns " << r << endl;
cout << "Created file '" << crlFile << "'" << endl;
cout << dipki::X509::TextDumpToString(crlFile) << endl;
// -- A CRL using specified times (NB these are GMT times, not local)
// -- with an empty revocation list and using sha256WithRSAEncryption to sign
crlFile = "Carl_20211201.crl";
extns = "thisUpdate=2021-12-01T12:00;nextUpdate=2022-01-01";
r = dipki::X509::MakeCRL(crlFile, issuerCert, prikeyFile, password, "", extns, dipki::X509::SigAlg::Rsa_Sha256);
cout << "X509::MakeCRL returns " << r << endl;
cout << "Created file '" << crlFile << "'" << endl;
cout << dipki::X509::TextDumpToString(crlFile) << endl;
// Use VerifyCert to check the signature in a CRL
isok = dipki::X509::CertIsVerified(crlFile, issuerCert);
cout << "X509::CertIsVerified returns " << (isok ? "true" : "false") << endl;
assert(isok);
// Pass the wrong issuer cert when verifying...
isok = dipki::X509::CertIsVerified(crlFile, "AliceRSASignByCarl.cer");
cout << "X509::CertIsVerified(wrong issuer cert) returns " << (isok ? "true" : "false") << " (expecting false)" << endl;
assert(!isok);
// Check if a given cert is in a CRL
// Use test CRL and certs from RFC3280
crlFile = "rfc3280bis_CRL.crl";
cout << "CRL File: " << crlFile << endl;
cout << "This cert has not been revoked..." << endl;
certFile = "rfc3280bis_cert1.cer";
cout << "Cert: " << certFile << endl;
isok = dipki::X509::CertIsRevoked(certFile, crlFile);
cout << "X509::CertIsRevoked returns " << (isok ? "true" : "false") << endl;
assert(!isok);
cout << "This cert HAS been revoked..." << endl;
certFile = "rfc3280bis_cert2.cer";
cout << "Cert: " << certFile << endl;
isok = dipki::X509::CertIsRevoked(certFile, crlFile);
cout << "X509::CertIsRevoked returns " << (isok ? "true" : "false") << endl;
assert(isok);
cout << "But the same cert was not revoked as at 15:00 GMT on 19 November 2004..." << endl;
dateStr = "2004-11-19T15:00Z";
isok = dipki::X509::CertIsRevoked(certFile, crlFile, "", dateStr);
cout << "X509::CertIsRevoked returns " << (isok ? "true" : "false") << " (expecting false)" << endl;
assert(!isok);
}
void test_CMS_SigData()
{
cout << "\nTEST CMS SIG DATA:" << endl;
std::string s, query, fname;
fname = "4.5.bin";
cout << "FILE: " << fname << endl;
query = "version";
s = dipki::Cms::QuerySigData(fname, query);
cout << "Cms::QuerySigData(" << query << "): " << s << endl;
query = "digestAlgorithm";
s = dipki::Cms::QuerySigData(fname, query);
cout << "Cms::QuerySigData(" << query << "): " << s << endl;
query = "signatureValue";
s = dipki::Cms::QuerySigData(fname, query);
cout << "Cms::QuerySigData(" << query << "): " << s << endl;
s = dipki::Cms::GetSigDataDigest(fname);
cout << "Cms::GetSigDataDigest: " << "[" << s << "]" << endl;
s = dipki::Cms::GetSigDataDigest("BasicSignByAlice.bin", "AliceRSASignByCarl.cer");
cout << "Cms::GetSigDataDigest: " << "[" << s << "]" << endl;
}
void test_CMS_EnvData()
{
cout << "\nTEST CMS ENV DATA:" << endl;
std::string msg, s, query, fname, fnameCert;
std::string privateKey;
int n;
dipki::bvec_t b;
// Create an enveloped CMS object Alice to Bob using Bob's X.509 certificate
fname = "cms2bob_aes128_oaep.p7m";
fnameCert = "BobRSASignByCarl.cer";
msg = "This is some sample content.";
cout << "INPUT MSG: " << msg << endl;
// This should return 1 (indicating one successful recipient)
n = dipki::Cms::MakeEnvDataFromString(fname, msg, fnameCert, dipki::Cms::CipherAlg::Aes128, dipki::Cms::KeyEncrAlg::Rsa_Oaep);
cout << "Cms::MakeEnvDataFromString returns " << n << " (expecting 1)" << endl;
cout << "FILE: " << fname << endl;
// Query the enveloped-data file...
query = "version";
s = dipki::Cms::QueryEnvData(fname, query);
cout << "Cms::QueryEnvData(" << query << "): " << s << endl;
query = "countOfRecipientInfos";
s = dipki::Cms::QueryEnvData(fname, query);
cout << "Cms::QueryEnvData(" << query << "): " << s << endl;
query = "contentEncryptionAlgorithm";
s = dipki::Cms::QueryEnvData(fname, query);
cout << "Cms::QueryEnvData(" << query << "): " << s << endl;
query = "keyEncryptionAlgorithm";
s = dipki::Cms::QueryEnvData(fname, query);
cout << "Cms::QueryEnvData(" << query << "): " << s << endl;
query = "encryptedContent";
s = dipki::Cms::QueryEnvData(fname, query);
cout << "Cms::QueryEnvData(" << query << "): " << s << endl;
// Now try and read it using Bob's private key
privateKey = dipki::Rsa::ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password");
cout << "Key length=" << dipki::Rsa::KeyBits(privateKey) << " bits" << endl;
s = dipki::bvec2str(dipki::Cms::ReadEnvDataToBytes(fname, privateKey));
cout << "MSG=" << s << endl;
// As above but passing a byte array instead of a string
fname = "cms2bob_aes128_oaep_bytes.p7m";
b = dipki::str2bvec(msg);
n = dipki::Cms::MakeEnvDataFromBytes(fname, b, fnameCert, dipki::Cms::CipherAlg::Aes128, dipki::Cms::KeyEncrAlg::Rsa_Oaep);
cout << "FILE: " << fname << endl;
cout << "Cms::MakeEnvDataFromBytes returns " << n << " (expecting 1)" << endl;
b = dipki::Cms::ReadEnvDataToBytes(fname, privateKey);
cout << "MSG=" << dipki::bvec2str(b) << endl;
}
void test_CMS()
{
cout << "\nCMS (S/MIME OBJECTS) TESTS:" << endl;
std::string s;
dipki::bvec_t b;
int n;
bool isok;
std::string msg, fnameInput, fnameOutput, fnameCert, fnameOut1, hexDigest;
std::string privateKey, certList;
// Create a test text file
msg = "This is some sample content.";
fnameInput = "excontent.txt";
write_text_file(fnameInput, msg);
cout << "Input data: '" << read_text_file(fnameInput) << "'" << endl;
// Create an enveloped CMS object from Alice to Bob using Bob's X.509 certificate
fnameOutput = "cmsalice2bob.p7m";
fnameCert = "BobRSASignByCarl.cer";
// This should return 1 (indicating one successful recipient)
n = dipki::Cms::MakeEnvData(fnameOutput, fnameInput, fnameCert, dipki::Cms::CipherAlg::Aes128);
cout << "Cms::MakeEnvData returns " << n << " (expected exactly 1)" << endl;
assert(n == 1);
s = dipki::Asn1::Type(fnameOutput);
cout << "FILE " << fnameOutput << " is a " << s << endl;
// Now try and read it using Bob's private key, outputting to a file
fnameOutput = "cmsalice2bob.p7m.txt";
fnameInput = "cmsalice2bob.p7m";
privateKey = dipki::Rsa::ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password");
assert(privateKey.length() > 0);
n = dipki::Cms::ReadEnvDataToFile(fnameOutput, fnameInput, privateKey);
cout << "Cms::ReadEnvDataToFile returns " << n << " (expected 0)" << endl;
assert(n == 0);
// Read in the resulting text file
s = read_text_file(fnameOutput);
cout << "MSG: '" << s << "'" << endl;
assert(msg == s);
// and again but read directly into memory as a byte array
b = dipki::Cms::ReadEnvDataToBytes(fnameInput, privateKey);
cout << "Cms::ReadEnvDataToBytes returns array of size " << b.size() << " (expected +ve)" << endl;
assert(b.size() > 0);
// We know this should be valid text, so convert to a string
s = dipki::bvec2str(b);
cout << "MSG: '" << s << "'" << endl;
assert(msg == s);
// Generate a BER-encoded CMS signedData object as a file
// using Alice's private key
privateKey = dipki::Rsa::ReadPrivateKey("AlicePrivRSASign.p8e", "password");
fnameOutput = "BasicSignByAlice.bin";
fnameInput = "excontent.txt";
fnameCert = "AliceRSASignByCarl.cer";
n = dipki::Cms::MakeSigData(fnameOutput, fnameInput, fnameCert, privateKey, dipki::Cms::SigAlg::Rsa_Sha256);
cout << "Cms::MakeSigData returns " << n << " (expected 0)" << endl;
assert(n == 0);
s = dipki::Asn1::Type(fnameOutput);
cout << "FILE " << fnameOutput << " is a " << s << endl;
// again using a string as input instead of a file
s = read_text_file(fnameInput);
fnameOut1 = "BasicSignByAlice1.bin";
n = dipki::Cms::MakeSigDataFromString(fnameOut1, s, fnameCert, privateKey, dipki::Cms::SigAlg::Rsa_Sha256);
cout << "Cms::MakeSigDataFromString returns " << n << " (expected 0)" << endl;
assert(n == 0);
s = dipki::Asn1::Type(fnameOut1);
cout << "FILE " << fnameOut1 << " is a " << s << endl;
// Check we got the same result by comparing the file digests
assert(dipki::Hash::HexFromFile(fnameOut1) == dipki::Hash::HexFromFile(fnameOutput));
// Verify the signature directly
isok = dipki::Cms::VerifySigData(fnameOutput);
cout << "Cms::VerifySigData returns " << (isok ? "true" : "false") << endl;
assert(isok);
// Get message digest of signed-data object eContent
hexDigest = dipki::Cms::QuerySigData(fnameOutput, "DigestOfeContent");
cout << "Digest=" << hexDigest << endl;
// Make a detached signature using the message digest hash
fnameOutput = "DetSignByAlice.bin";
n = dipki::Cms::MakeDetachedSig(fnameOutput, hexDigest, fnameCert, privateKey, dipki::Cms::SigAlg::Rsa_Sha256);
cout << "Cms::MakeDetachedSig returns " << n << " (expected 0)" << endl;
assert(n == 0);
s = dipki::Asn1::Type(fnameOutput);
cout << "FILE " << fnameOutput << " is a " << s << endl;
// Verify the detached signature using the message digest
isok = dipki::Cms::VerifySigData(fnameOutput, "", hexDigest);
cout << "Cms::VerifySigData(detached) returns " << bool2str(isok) << endl;
assert(isok);
// Extract the contents from the (first) signed object into another file
fnameOutput = "excontent_chk.txt";
fnameInput = "BasicSignByAlice.bin";
n = dipki::Cms::ReadSigDataToFile(fnameOutput, fnameInput);
cout << "Cms::ReadSigDataToFile returns " << n << " (expected 28)" << endl;
assert(n > 0);
s = read_text_file(fnameOutput);
cout << "MSG: '" << s << "'" << endl;
assert(msg == s);
// Extract the contents directly into memory instead
b = dipki::Cms::ReadSigDataToBytes(fnameInput);
cout << "Cms::ReadSigDataToBytes returns array of size " << b.size() << " (expected +ve)" << endl;
assert(b.size() > 0);
s = dipki::bvec2str(b);
cout << "MSG: '" << s << "'" << endl;
assert(msg == s);
// Create a "certs-only" SignedData file == certificate chain file
fnameOutput = "Alice_new.p7c";
certList = "AliceRSASignByCarl.cer" ";" "CarlRSASelf.cer";
n = dipki::Cms::MakeSigData(fnameOutput, "", certList, "", dipki::Cms::SigAlg::Default, dipki::Cms::SigDataOptions::CertsOnly);
cout << "Cms::MakeSigData(CertsOnly) returns " << n << " (expected 0)" << endl;
assert(n == 0);
s = dipki::Asn1::Type(fnameOutput);
cout << "FILE " << fnameOutput << " is a " << s << endl;
// How many certs in the chain
n = dipki::X509::GetCertCountInP7Chain(fnameOutput);
cout << n << " certificates in the p7 chain" << endl;
assert(n == 2);
}
void test_CMS_SigData_PSS()
{
cout << "\nCMS SIG-DATA WITH RSA-PSS:" << endl;
int r;
bool isok;
std::string s;
std::string privateKey, query;
std::string prikeyFile = "999009991b_pri.p8e";
std::string userCert = "999009991b.cer";
std::string recipKeyFile = "999009051b_pri.p8e";
std::string recipCert = "999009051b.cer";
std::string certList = recipCert + ";" + "CA_4096_Cert.cer" + ";" + "Int_4096_Cert.cer";
std::string interFile = "To_999009051b.int";
std::string outFile = "To_999009051b.p7m";
std::string interFile1 = "To_999009051b_1.int";
std::string msg = "Hallo Walt";
// Read in the sender's private key
privateKey = dipki::Rsa::ReadPrivateKey(prikeyFile, "password");
cout << "Key length=" << dipki::Rsa::KeyBits(privateKey) << " bits" << endl;
cout << "MSG: '" << msg << "'" << endl;
// Create intermediate signed-data file
r = dipki::Cms::MakeSigDataFromString(interFile, msg, userCert, privateKey, dipki::Cms::SigAlg::Rsa_Pss_Sha256,
dipki::Cms::SigDataOptions::IncludeAttributes | dipki::Cms::SigDataOptions::AddSignTime);
cout << "Created intermediate signed-data file '" << interFile << "'" << endl;
//cout << dipki::Asn1::TextDumpToString(interFile) << endl;
// While we're here, let's query the signed-data file
query = "signatureAlgorithm";
s = dipki:: Cms::QuerySigData(interFile, query);
cout << "Cms::QuerySigData(" << query << "): " << s << endl;
query = "pssParams";
s = dipki:: Cms::QuerySigData(interFile, query);
cout << "Cms::QuerySigData(" << query << "): " << s << endl;
// Encrypt signed-data directly in enveloped-data file: NB returns number of recipients, not zero on success
r = dipki::Cms::MakeEnvData(outFile, interFile, certList, dipki::Cms::CipherAlg::Aes256, dipki::Cms::KeyEncrAlg::Rsa_Oaep, dipki::Cms::HashAlg::Sha256);
assert(r > 0);
cout << "Created enveloped-data file '" << outFile << "'" << endl;
s = dipki::Asn1::Type(outFile);
cout << "FILE " << outFile << " is a " << s << endl;
// And let's query the enveloped-data file
query = "keyEncryptionAlgorithm";
s = dipki:: Cms::QueryEnvData(outFile, query);
cout << "Cms::QueryEnvData(" << query << "): " << s << endl;
query = "oaepParams";
s = dipki:: Cms::QueryEnvData(outFile, query);
cout << "Cms::QueryEnvData(" << query << "): " << s << endl;
// Clean up
dipki::Wipe::String(privateKey);
cout << "\nPart 2: Reading in the enveloped data..." << endl;
// Read in the recipient's private key (which we shouldn't have, but we do here for testing)
privateKey = dipki::Rsa::ReadPrivateKey(recipKeyFile, "password");
cout << "Key length=" << dipki::Rsa::KeyBits(privateKey) << " bits" << endl;
// Read the enveloped-data file and output the signed-data file
r = dipki::Cms::ReadEnvDataToFile(interFile1, outFile, privateKey, recipCert);
assert(r == 0);
//Console.WriteLine("Read in signed-data file '{0}'", interFile1);
cout << "Read in signed-data file '" << interFile1 << "'" << endl;
// Verify the signed data before reading it
isok = dipki::Cms::VerifySigData(interFile1);
cout << "Cms::VerifySigData returns " << ::bool2str(isok) << endl;
assert(isok);
// Read in the data that was signed (indirectly as bytes then to a string)
s = dipki::bvec2str(dipki::Cms::ReadSigDataToBytes(interFile1));
cout << "msg='" << s << "'" << endl;
assert(s.length() > 0);
// Clean up
dipki::Wipe::String(privateKey);
cout << "Dump certificate data..." << endl;
s = dipki::X509::TextDumpToString(recipCert, dipki::X509::OutputOpts::Decimal | dipki::X509::OutputOpts::Ldap);
cout << s << endl;
isok = dipki::X509::CertPathIsValid(certList);
cout << "X509::CertPathIsValid returns " << ::bool2str(isok) << endl;
assert(isok);
}
void test_CMS_SMIME_Chain()
{
cout << "\nCREATE A CHAIN OF CMS OBJECTS WRAPPED IN S/MIME:" << endl;
int r;
bool isok;
std::string s, inpFile, outFile, query;
std::string origFile = "sonnets.txt";
std::string alicekeyfile = "AlicePrivRSASign.p8e";
std::string alicecerfile = "AliceRSASignByCarl.cer";
std::string password = "password";
std::string bobkeyfile = "BobPrivRSAEncrypt.p8e";
std::string bobcerfile = "BobRSASignByCarl.cer";
std::string privateKey;
std::string digest1, digest2;
// Start with original input file
inpFile = origFile;
cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
// Create a CMS compressed-data object, a text file of 106081 bytes
outFile = "sonnet-compr.p7z";
r = dipki::Cms::MakeComprData(outFile, inpFile);
cout << "Cms::MakeComprData returns " << r << " (expected 0)" << endl;
assert(0 == r);
inpFile = outFile;
cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
// Wrap in S/MIME
outFile = "sonnet-compr-smime.txt";
r = dipki::Smime::Wrap(outFile, inpFile);
cout << "Smime::Wrap returns " << r << " (expected +ve)" << endl;
assert(r > 0);
inpFile = outFile;
cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
// Create a CMS signed-data object signed by Alice
outFile = "sonnet-sig-compr.p7m";
privateKey = dipki::Rsa::ReadPrivateKey(alicekeyfile, password);
r = dipki::Cms::MakeSigData(outFile, inpFile, alicecerfile, privateKey);
cout << "Cms::MakeSigData returns " << r << " (expected 0)" << endl;
assert(0 == r);
inpFile = outFile;
cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
// Wrap in S/MIME
outFile = "sonnet-sig-compr-smime.txt";
r = dipki::Smime::Wrap(outFile, inpFile);
cout << "Smime::Wrap returns " << r << " (expected +ve)" << endl;
assert(r > 0);
inpFile = outFile;
cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
// Create a CMS enveloped-data object encrypted for Bob
outFile = "sonnet-env-sig-compr.p7m";
r = dipki::Cms::MakeEnvData(outFile, inpFile, bobcerfile, dipki::Cms::CipherAlg::Aes128);
cout << "Cms::MakeEnvData returns " << r << " (expected 1)" << endl;
assert(1 == r);
inpFile = outFile;
cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
// Wrap in S/MIME
outFile = "sonnet-env-sig-compr-smime.txt";
r = dipki::Smime::Wrap(outFile, inpFile);
cout << "Smime::Wrap returns " << r << " (expected +ve)" << endl;
assert(r > 0);
inpFile = outFile;
cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
// NOW REVERSE THE PROCEDURE...
cout << "\nReverse the procedure (and check file types as we go)..." << endl;
// What S/MIME type are we starting with?
query = "smime-type";
s = dipki::Smime::Query(inpFile, query);
cout << "FILE: " << inpFile << ": " << query << " is '" << s << "'" << endl;
// Extract from S/MIME
outFile = "sonnet-out-env-sig-compr.p7m";
r = dipki::Smime::Extract(outFile, inpFile);
cout << "Smime::Extract returns " << r << " (expected +ve)" << endl;
assert(r > 0);
inpFile = outFile;
cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
// What ASN.1 type did we get?
s = dipki::Asn1::Type(inpFile);
cout << "Found a '" << s << "' file" << endl;
// Bob decrypts enveloped-file using his private key
outFile = "sonnet-out-sig-compr-smime.txt";
privateKey = dipki::Rsa::ReadPrivateKey(bobkeyfile, password);
assert(privateKey.length() > 0);
r = dipki::Cms::ReadEnvDataToFile(outFile, inpFile, privateKey);
cout << "Cms::ReadEnvDataToFile returns " << r << " (expected 0)" << endl;
assert(r == 0);
inpFile = outFile;
cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
// What S/MIME type are we starting with?
query = "smime-type";
s = dipki::Smime::Query(inpFile, query);
cout << "FILE: " << inpFile << ": " << query << " is '" << s << "'" << endl;
// Extract from S/MIME
outFile = "sonnet-out-sig-compr.p7m";
r = dipki::Smime::Extract(outFile, inpFile);
cout << "Smime::Extract returns " << r << " (expected +ve)" << endl;
assert(r > 0);
inpFile = outFile;
cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
// What ASN.1 type did we get?
s = dipki::Asn1::Type(inpFile);
cout << "Found a '" << s << "' file" << endl;
// Verify the signature in the signed-data object using its embedded certificate
isok = dipki::Cms::VerifySigData(inpFile);
cout << "Cms::VerifySigData returns " << (isok ? "true" : "false") << endl;
assert(isok);
// Extract contents of signed-data
outFile = "sonnet-out-compr-smime.txt";
r = dipki::Cms::ReadSigDataToFile(outFile, inpFile);
cout << "Cms::ReadSigDataToFile returns " << r << " (expected +ve)" << endl;
assert(r > 0);
inpFile = outFile;
cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
// What S/MIME type are we starting with?
query = "smime-type";
s = dipki::Smime::Query(inpFile, query);
cout << "FILE: " << inpFile << ": " << query << " is '" << s << "'" << endl;
// Extract from S/MIME
outFile = "sonnet-out-compr.p7z";
r = dipki::Smime::Extract(outFile, inpFile);
cout << "Smime::Extract returns " << r << " (expected +ve)" << endl;
assert(r > 0);
inpFile = outFile;
cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
// What ASN.1 type did we get?
s = dipki::Asn1::Type(inpFile);
cout << "Found a '" << s << "' file" << endl;
// Finally, read contents of CMS compressed-data object
outFile = "sonnet-out.txt";
r = dipki::Cms::ReadComprData(outFile, inpFile);
cout << "Cms::ReadComprData returns " << r << " (expected +ve)" << endl;
assert(r > 0);
inpFile = outFile;
cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
// Check we have no more S/MIME wrapping
query = "smime-type";
s = dipki::Smime::Query(inpFile, query);
cout << "FILE: " << inpFile << ": " << query << " is '" << s << "' (expecting empty)" << endl;
assert(s.length() == 0);
// Did we get the same as we started?
cout << "Compute SHA-1 digests and compare..." << endl;
digest1 = dipki::Hash::HexFromFile(origFile, dipki::Hash::Alg::Sha1);
cout << "SHA1('" << origFile << "')=" << digest1 << endl;
digest2 = dipki::Hash::HexFromFile(outFile, dipki::Hash::Alg::Sha1);
cout << "SHA1('" << outFile << "')=" << digest2 << endl;
assert(digest1 == digest2);
}
void test_CMS_Data_Bytes()
{
cout << "\nCMS SIGNED DATA USING RAW BYTES:" << endl;
// We will create a signed-data object with "raw" content as a UTF-8-encoded XML document.
std::string sigdataFile;
std::string prikeyFile = "AlicePrivRSASign.p8e";
std::string certFile = "AliceRSASignByCarl.cer";
std::string privateKey;
int r, n;
std::string query, s;
dipki::bvec_t contentArr, xmlArr;
// Create an XML document as a string with lots of obscure characters
std::wstring wxmlStr = L"\ufeff<?xml version=\"1.0\"?><doc>\n"
L"<name c='es'>Íñigo</name>\n"
L"<name c='fr'>Françoise</name>\n"
L"<name c='pl'>Błażej</name>\n"
L"<name c='cn'>大卫</name>\n"
L"</doc>";
// Does not work at all in basic Windows console!
//std::wcout << "WIDE: [" << wxmlStr << "]" << L"\r\n";
// Convert wide string to UTF-8 encoding
std::string xmlStr = dipki::Cnv::Utf8FromWide(wxmlStr);
cout << "UTF8: \n" << xmlStr << "]" << endl;
cout << "String length is " << xmlStr.length() << " bytes" << endl;
cout << "UTF-8-encoded in hex;" << endl;
cout << dipki::Cnv::ToHex(dipki::str2bvec(xmlStr)) << endl;
// Now sign this using Alice's private key
// (a) passing the UTF-8-encoded string
sigdataFile = "sigDataByAlice.p7m";
privateKey = dipki::Rsa::ReadPrivateKey(prikeyFile, "password");
r = dipki::Cms::MakeSigDataFromString(sigdataFile, xmlStr, certFile, privateKey, dipki::Cms::SigAlg::Rsa_Sha384);
cout << "Cms::MakeSigDataFromString returns " << r << " (expecting 0)" << endl;
// (b) passing the same but as raw bytes
sigdataFile = "sigDataByAlice1.p7m";
privateKey = dipki::Rsa::ReadPrivateKey(prikeyFile, "password");
xmlArr = dipki::str2bvec(xmlStr);
r = dipki::Cms::MakeSigDataFromBytes(sigdataFile, xmlArr, certFile, privateKey, dipki::Cms::SigAlg::Rsa_Sha384);
cout << "Cms::MakeSigDataFromBytes returns " << r << " (expecting 0)" << endl;
// signed-data objects with same content and key signed with shaXXXWithRSAEncryption should be identical (not so for PSS or ECDSA or Ed25519)
assert(dipki::Hash::HexFromFile("sigDataByAlice.p7m") == dipki::Hash::HexFromFile("sigDataByAlice1.p7m"));
// Extract the signed content to a new byte array
contentArr = dipki::Cms::ReadSigDataToBytes(sigdataFile);
cout << "Content length is " << contentArr.size() << " bytes" << endl;
// We know it's a valid UTF-8 string, so display as a string
cout << "" << dipki::bvec2str(contentArr) << "" << endl;
// Extract and save UTF-8-encoded XML to file
std::string xmlFile = "exampleUTF8.xml";
r = dipki::Cms::ReadSigDataToFile(xmlFile, sigdataFile);
cout << "Cms.ReadSigDataToFile returns " << r << " (expecting +ve)" << endl;
assert(r > 0);
cout << "Extracted content to file '" << xmlFile << "'" << endl;
cout << "File length=" << file_length(xmlFile) << endl;
// Check UTF-8 encoding in file we just made
n = dipki::Cnv::CheckUTF8File(xmlFile);
cout << "Cnv::CheckUTF8=" << n << " ==> " << dipki::Cnv::CheckUTF8CodeAsString(n) << endl;
cout << "\nCMS ENVELOPED DATA USING RAW BYTES:" << endl;
// Use the same XML data to make an enveloped data file for Bob, Alice and Carl
// But first we'll make a PKCS#7 certificate chain file and use that to create the env-data
std::string p7file = "bob_alice_carl.p7b";
// No need for input file or private key (P7 cert chain is a "degenerate" signed-data file)
r = dipki::Cms::MakeSigData(p7file, "", "BobRSASignByCarl.cer;AliceRSASignByCarl.cer;CarlRSASelf.cer", "",
dipki::Cms::SigAlg::Default, dipki::Cms::SigDataOptions::CertsOnly);
cout << "Cms::MakeSigData returns " << r << "" << endl;
cout << "Created P7 chain file '" << p7file << "'" << endl;
cout << "ASN.1 type=" << dipki::Asn1::Type(p7file) << endl;
std::string envdataFile = "envDataForBobAliceCarl.p7m";
// Use AES-128 for content encryption and RSA-OAEP with SHA-256 for key transport
// We can pass a P7 file instead of a list of certificates
r = dipki::Cms::MakeEnvDataFromBytes(envdataFile, xmlArr, p7file, dipki::Cms::CipherAlg::Aes128,
dipki::Cms::KeyEncrAlg::Rsa_Oaep, dipki::Cms::HashAlg::Sha256);
cout << "Cms.MakeEnvDataFromBytes returns " << r << " (= # recipients)" << endl;
assert(r > 1);
cout << "Created enveloped-data file '" << envdataFile << "'" << endl;
// Any of Bob, Alice or Carl can decrypt the message - we'll use Bob's private key
privateKey = dipki::Rsa::ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password");
dipki::bvec_t arrMsg = dipki::Cms::ReadEnvDataToBytes(envdataFile, privateKey);
// Display in hex
cout << "Received message in hex:" << endl;
cout << dipki::Cnv::ToHex(arrMsg) << endl;
cout << "Content length is " << arrMsg.size() << " bytes" << endl;
}
void test_CMS_SigValue()
{
int r;
cout << "\nCREATE A SIGNED-DATA CMS OBJECT USING A PRE-COMPUTED SIGNATURE VALUE:" << endl;
// This example creates an identical SignedData file to example 4.2 from [SMIME-EX].
// In this case, the signature value has been generated separately, perhaps by a smart card with Alice's private key details in it.
// The resulting file should be identical to the file 4.2.bin.
// Data to be signed in hex format:
std::string dataHex = "54:68:69:73:20:69:73:20:73:6f:6d:65:20:73:61:6d:70:6c:65:20:63:6f:6e:74:65:6e:74:2e";
// The signature (generated by the smart card) is:
std::string sigHex = "2F:23:82:D2:F3:09:5F:B8:0C:58:EB:4E:9D:BF:89:9A"
"81:E5:75:C4:91:3D:D3:D0:D5:7B:B6:D5:FE:94:A1:8A"
"AC:E3:C4:84:F5:CD:60:4E:27:95:F6:CF:00:86:76:75"
"3F:2B:F0:E7:D4:02:67:A7:F5:C7:8D:16:04:A5:B3:B5"
"E7:D9:32:F0:24:EF:E7:20:44:D5:9F:07:C5:53:24:FA"
"CE:01:1D:0F:17:13:A7:2A:95:9D:2B:E4:03:95:14:0B"
"E9:39:0D:BA:CE:6E:9C:9E:0C:E8:98:E6:55:13:D4:68"
"6F:D0:07:D7:A2:B1:62:4C:E3:8F:AF:FD:E0:D5:5D:C7";
std::string certFile = "AliceRSASignByCarl.cer";
std::string cmsFile = "BasicSignByAliceExternal.bin";
// Convert the hex strings into byte arrays (non-hex chars are stripped)
dipki::bvec_t data = dipki::Cnv::FromHex(dataHex);
cout << "MSG=" << dipki::Cnv::ToHex(data) << endl;
dipki::bvec_t sigval = dipki::Cnv::FromHex(sigHex);
cout << "SIG=" << dipki::Cnv::ToHex(sigval) << endl;
// Create the signed-data file
r = dipki::Cms::MakeSigDataFromSigValue(cmsFile, sigval, data, certFile);
cout << "Cms::MakeSigDataFromSigValue returns " << r << " (expecting 0)" << endl;
cout << "Createdfile '" << cmsFile << "'" << endl;
cout << "ASN.1 type=" << dipki::Asn1::Type(cmsFile) << endl;
// Check that output file matches `4.2.bin`.
assert(dipki::Hash::HexFromFile(cmsFile) == dipki::Hash::HexFromFile("4.2.bin"));
}
void test_CMS_Pseudo()
{
int r;
bool isok;
std::string s, dighex, digestValue;
cout << "\\nCREATE A SIGNED-DATA CMS OBJECT USING 'PSEUDO' PLACEHOLDER:" << endl;
std::string pseudofile = "BasicSignByAlice_pseudo.p7m";
r = dipki::Cms::MakeSigData(pseudofile, "excontent.txt", /*REQDIN*/
"AliceRSASignByCarl.cer", /*REQDIN*/
"", /* privkey not required with PSEUDO option */
dipki::Cms::SigAlg::Rsa_Sha256,
dipki::Cms::SigDataOptions::PseudoSig | dipki::Cms::SigDataOptions::AltSigAlgId
| dipki::Cms::SigDataOptions::IncludeAttributes | dipki::Cms::SigDataOptions::AddSignTime | dipki::Cms::SigDataOptions::AddSigningCertificate);
cout << "Cms::MakeSigData(PseudoSig) returns " << r << " (expecting 0)" << endl;
cout << "Createdfile '" << pseudofile << "'" << endl;
// Expecting bbbbbb... (this is *exactly* the correct length for the final signature)
cout << "signatureValue: " << dipki::Cms::QuerySigData(pseudofile, "signatureValue") << endl;
// Check signing time (not required, but just to check, out of interest)
// NB UTC/GMT time
cout << "signingTime: " << dipki::Cms::QuerySigData(pseudofile, "signingTime") << endl;
// Get digest value in hex - this is the digestValue over which the signature will be created.
dighex = dipki::Cms::QuerySigData(pseudofile, "DigestOfSignedAttrs");
cout << "DigestOfSignedAttrs: " << dighex << endl;
// Convert to base64
digestValue = dipki::Cnv::ToBase64(dipki::Cnv::FromHex(dighex));
cout << "digestValue: " << digestValue << endl;
// Pass the digestValue in base64 encoding to the signing agency.
// They will return the signatureValue (signInfo) in base64 created over the digestValue using "your" private key.
// User: digestValue --> SigningAgency
// SigningAgency: signatureValue --> User
// OK, so we fiddle it here to compute the signatureValue ourselves using Alice's private key...
std::string signatureValue = dipki::Sig::SignDigest(dipki::Cnv::FromBase64(digestValue), "AlicePrivRSASign.p8e", "password", dipki::Sig::Alg::Rsa_Sha256);
cout << "signatureValue: " << signatureValue << endl;
// Now create a new signed-data file from the pseudo file and the received signature Value
std::string signedFile = "BasicSignByAlice_signed.p7m";
r = dipki::Cms::MakeSigDataFromPseudo(signedFile, pseudofile, dipki::Cnv::FromBase64(signatureValue));
cout << "Cms::MakeSigDataFromPseudo returns " << r << " (expecting 0)" << endl;
cout << "Createdfile '" << signedFile << "'" << endl;
// Check the resulting file has a valid signature
isok = dipki::Cms::VerifySigData(signedFile);
cout << "Cms::VerifySigData returns " << bool2str(isok) << endl;
assert(isok);
}
void test_ReadJWK()
{
cout << "\nREAD IN RSA KEY REPRESENTED AS JSON JWK:" << endl;
// RSA public key as a JSON string
// Ref: RFC 7517 JSON Web Key (JWK) Appendix A.1
std::string json = "{\"kty\":\"RSA\","
"\"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx"
"4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs"
"tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2"
"QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI"
"SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb"
"w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\","
"\"e\":\"AQAB\","
"\"alg\":\"RS256\","
"\"kid\":\"2011-04-29\"}";
cout << "JSON key=" << json << "" << endl;
std::string publicKey = dipki::Rsa::ReadPublicKey(json);
assert(publicKey.length() > 0);
// Display some key properties
cout << "RSA key size = " << dipki::Rsa::KeyBits(publicKey) << " bits" << endl;
cout << "KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(publicKey) << std::dec << endl;
}
void test_HASH_Length()
{
cout << "\nTEST HASH LENGTH:" << endl;
cout << "Hash::Length(Sha256)=" << dipki::Hash::Length(dipki::Hash::Alg::Sha256) << endl;
cout << "Hash::Length(Btc160)=" << dipki::Hash::Length(dipki::Hash::Alg::Btc160) << endl;
}
void test_KDF_Bytes()
{
cout << "\nTEST KDF FUNCTION:" << endl;
int nbytes;
dipki::bvec_t kek, zz, info;
// ansx963_2001.rsp CAVS 12.0 'ANS X9.63-2001' information for sample
nbytes = 128 / 8;
zz = dipki::Cnv::FromHex("96c05619d56c328ab95fe84b18264b08725b85e33fd34f08");
kek = dipki::Kdf::Bytes(nbytes, zz, dipki::Kdf::KdfAlg::X963, dipki::Kdf::HashAlg::Sha256);
cout << "KEK=" << dipki::Cnv::ToHex(kek) << endl;
assert(dipki::Cnv::ToHex(kek) == "443024C3DAE66B95E6F5670601558F71");
// [RFC 5869] A.1. Test Case 1 Basic test case with SHA-256
nbytes = 42;
zz = dipki::Cnv::FromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
info = dipki::Cnv::FromHex("f0f1f2f3f4f5f6f7f8f9");
kek = dipki::Kdf::Bytes(nbytes, zz, dipki::Kdf::KdfAlg::Hkdf, dipki::Kdf::HashAlg::Sha256, info, "salt=000102030405060708090a0b0c");
cout << "KEK=" << dipki::Cnv::ToHex(kek) << endl;
assert(dipki::Cnv::ToHex(kek) == "3CB25F25FAACD57A90434F64D0362F2A2D2D0A90CF1A5A4C5DB02D56ECC4C5BF34007208D5B887185865");
// [RFC 5869] A.3. Test with SHA-256 and zero-length salt/info
nbytes = 42;
zz = dipki::Cnv::FromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
kek = dipki::Kdf::Bytes(nbytes, zz, dipki::Kdf::KdfAlg::Hkdf, dipki::Kdf::HashAlg::Sha256);
cout << "KEK=" << dipki::Cnv::ToHex(kek) << endl;
assert(dipki::Cnv::ToHex(kek) == "8DA4E775A563C18F715F802A063C5A31B8A11F5C5EE1879EC3454E5F3C738D2D9D201395FAA4B61A96C8");
}
void test_KDF_ForCms()
{
cout << "\nTEST KDF FOR CMS:" << endl;
dipki::bvec_t kek, zz, ukm;
zz = dipki::Cnv::FromHex("160E3F5588C6FB4E9CEE8BC3C1C5000AB86396468C3D1CAEC0CB6E21536B5513");
// Use default KDF X963 and SHA-1
kek = dipki::Kdf::ForCms(zz, dipki::Kdf::KeyWrapAlg::Aes128_wrap);
cout << "KEK=" << dipki::Cnv::ToHex(kek) << endl;
assert(dipki::Cnv::ToHex(kek) == "04D616C654CDF62BB186A5A088B60FB5");
// Use same ZZ but use HKDF, SHA-256, and add user key material
ukm = dipki::Cnv::FromHex("616263"); // "abc"
kek = dipki::Kdf::ForCms(zz, dipki::Kdf::KeyWrapAlg::Aes256_wrap, dipki::Kdf::KdfAlg::Hkdf, dipki::Kdf::HashAlg::Sha256, ukm);
cout << "KEK=" << dipki::Cnv::ToHex(kek) << endl;
assert(dipki::Cnv::ToHex(kek) == "1D06D6FD5C1EBFB33CAD875E6B99781D3D750875F573C9093CECBFBA6937ACC5");
cout << "BEGIN EXPECTING ERRORS..." << endl;
try {
// Default key wrap is not permitted here
cout << "Must specify a key wrap algorithm here, not Default..." << endl;
kek = dipki::Kdf::ForCms(zz, dipki::Kdf::KeyWrapAlg::Default);
cout << "KEK=" << dipki::Cnv::ToHex(kek) << endl;
}
catch (std::exception& e) {
cout << e.what() << endl;
}
cout << "...END EXPECTED ERRORS" << endl;
}
void test_CMS_EnvData_ecdh()
{
cout << "\nTEST CMS ENVDATA USING ECDH:" << endl;
int r;
int nrecips;
std::string query, certfile;
std::string prikeyalice, prikeydana;
dipki::bvec_t bv;
std::string out;
std::string msg = "This is some sample content.";
std::string cmsfile;
std::string certlist = "lamps-dana.encrypt.crt;lamps-alice.encrypt.crt";
nrecips = 2;
// (1) Create an enveloped CMS object to Dana (using ecdh) and Alice (using RSA)
cmsfile = "dana_alice_all_defaults.p7m";
r = dipki::Cms::MakeEnvDataFromString(cmsfile, msg, certlist);
cout << "Cms::MakeEnvDataFromString returns " << r << " (expecting " << nrecips << ")" << endl;
assert(r == nrecips);
cout << "FILE: " << cmsfile << endl;
query = "contentEncryptionAlgorithm";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "CountOfRecipientInfos";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "recipientInfoType";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "keyEncryptionAlgorithm";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "keyWrapAlgorithm";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "recipientInfoType/2";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "keyEncryptionAlgorithm/2";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
// Read in Alice's RSA and Dana's ECC private key (both unencrypted)
prikeyalice = dipki::Rsa::ReadPrivateKey("lamps-alice.decrypt.p8.pem", "");
assert(prikeyalice.length() > 0);
prikeydana = dipki::Ecc::ReadPrivateKey("lamps-dana.decrypt.p8.pem", "");
assert(prikeydana.length() > 0);
// Read CMS enveloped-data using Alice's RSA key
certfile = "lamps-alice.encrypt.crt";
bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeyalice, certfile);
assert(msg.length() > 0);
cout << "MSG=" << dipki::bvec2str(bv) << endl;
// Read CMS enveloped-data using Dana's ECC key
certfile = "lamps-dana.encrypt.crt";
bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeydana, certfile);
assert(msg.length() > 0);
cout << "MSG=" << dipki::bvec2str(bv) << endl;
// (2) Create enveloped CMS object using RSA-OAEP but defaults for ECDH plus SHA-256
cmsfile = "dana_alice_oaep_ecc_defaults.p7m";
r = dipki::Cms::MakeEnvDataFromString(cmsfile, msg, certlist, dipki::Cms::CipherAlg::Aes128, dipki::Cms::KeyEncrAlg::Rsa_Oaep, dipki::Cms::HashAlg::Sha256, dipki::Cms::EnvDataOptions::Oaep_Mgf1Sha1);
cout << "Cms::MakeEnvDataFromString returns " << r << " (expecting " << nrecips << ")" << endl;
assert(r == nrecips);
cout << "FILE: " << cmsfile << endl;
query = "contentEncryptionAlgorithm";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "CountOfRecipientInfos";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "recipientInfoType";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "keyEncryptionAlgorithm";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "recipientInfoType/2";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "keyEncryptionAlgorithm/2";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "oaepParams/2";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
// Read CMS enveloped-data using Alice's RSA key
certfile = "lamps-alice.encrypt.crt";
bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeyalice, certfile);
assert(msg.length() > 0);
cout << "MSG=" << dipki::bvec2str(bv) << endl;
// Read CMS enveloped-data using Dana's ECC key
certfile = "lamps-dana.encrypt.crt";
bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeydana, certfile);
assert(msg.length() > 0);
cout << "MSG=" << dipki::bvec2str(bv) << endl;
// (3) Use specific ECDH parameters
cmsfile = "dana_hkdf.p7m";
certlist = "lamps-dana.encrypt.crt";
nrecips = 1;
r = dipki::Cms::MakeEnvDataFromString(cmsfile, msg, certlist, dipki::Cms::CipherAlg::Aes256, dipki::Cms::KeyEncrAlg::Default, dipki::Cms::HashAlg::Sha384,
dipki::Cms::EnvDataOptions::Default_EnvDataOpt, dipki::Cms::Format::Default, dipki::Kdf::KdfAlg::Hkdf, dipki::Kdf::KeyWrapAlg::Aes256_wrap, "#xdeadbeef");
cout << "Cms::MakeEnvDataFromString returns " << r << " (expecting " << nrecips << ")" << endl;
assert(r == nrecips);
cout << "FILE: " << cmsfile << endl;
//cout << dipki::Asn1::TextDumpToString(cmsfile);
query = "contentEncryptionAlgorithm";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "CountOfRecipientInfos";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "recipientInfoType";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "keyEncryptionAlgorithm";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "keyWrapAlgorithm";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
// Read CMS enveloped-data using Dana's ECC key
certfile = "lamps-dana.encrypt.crt";
bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeydana, certfile);
assert(msg.length() > 0);
cout << "MSG=" << dipki::bvec2str(bv) << endl;
}
void test_CMS_EnvData_adv()
{
cout << "\nTEST CMS ENVDATA USING BASIC AND ADVANCED OPTIONS:" << endl;
int n;
std::string cmsfile, query;
// Create an enveloped CMS object (ktri type) to Bob using Bob's RSA key
n = dipki::Cms::MakeEnvData("cms2bob_aes128.p7m", "excontent.txt", "BobRSASignByCarl.cer", dipki::Cms::CipherAlg::Aes128, dipki::Cms::KeyEncrAlg::Rsa_Oaep);
cout << "Cms::MakeEnvData returns " << n << " (expecting 1)" << endl;
assert(1 == n);
cmsfile = "cms2bob_aes128.p7m";
cout << "FILE:" << cmsfile << endl;
cout << dipki::Asn1::Type(cmsfile) << endl;
query = "recipientInfoType";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "contentEncryptionAlgorithm";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
// Same but using authenticated encryption and creating an authEnvelopedData object
n = dipki::Cms::MakeEnvData("cms2bob_aes128auth.p7m", "excontent.txt", "BobRSASignByCarl.cer", dipki::Cms::CipherAlg::Aes128, dipki::Cms::KeyEncrAlg::Rsa_Oaep, dipki::Cms::HashAlg::Default, dipki::Cms::EnvDataOptions::Authenticated);
cout << "Cms::MakeEnvData returns " << n << " (expecting 1)" << endl;
assert(1 == n);
cmsfile = "cms2bob_aes128auth.p7m";
cout << "FILE:" << cmsfile << endl;
cout << dipki::Asn1::Type(cmsfile) << endl;
query = "recipientInfoType";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "contentEncryptionAlgorithm";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
// Create an enveloped CMS object (kari type) to Dana using Dana's ECC key
n = dipki::Cms::MakeEnvData("cms2dana_hkdf.p7m", "excontent.txt", "lamps-dana.encrypt.crt", dipki::Cms::CipherAlg::Aes256, dipki::Cms::KeyEncrAlg::Default, dipki::Cms::HashAlg::Sha384,
dipki::Cms::EnvDataOptions::Default_EnvDataOpt, dipki::Cms::Format::Default, false, dipki::Kdf::KdfAlg::Hkdf, dipki::Kdf::KeyWrapAlg::Aes256_wrap, "#xdeadbeef");
cout << "Cms::MakeEnvData returns " << n << " (expecting 1)" << endl;
assert(1 == n);
cmsfile = "cms2dana_hkdf.p7m";
cout << "FILE:" << cmsfile << endl;
query = "recipientInfoType";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
// Create an enveloped CMS object (kekri type) using a previously distributed symmetric key-encryption key (KEK)
n = dipki::Cms::MakeEnvData("cms_envdata_kekri.p7m", "excontent.txt", "type=@kekri,keyid=ourcommonkey", "#x0123456789ABCDEFF0E1D2C3B4A59687", dipki::Cms::CipherAlg::Aes256, dipki::Kdf::KeyWrapAlg::Aes128_wrap,
dipki::Cms::EnvDataOptions::Default_EnvDataOpt, dipki::Cms::Format::Default, dipki::Cms::HashAlg::Sha256);
cout << "Cms::MakeEnvData returns " << n << " (expecting 1)" << endl;
assert(1 == n);
cmsfile = "cms_envdata_kekri.p7m";
cout << "FILE:" << cmsfile << endl;
query = "recipientInfoType";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
query = "keyid";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
//TODO: make this work
query = "keyWrapAlgorithm";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
// Create an enveloped CMS object (pwri type) using password-based key management
n = dipki::Cms::MakeEnvData("cms_envdata_pwri.p7m", "excontent.txt", "type=@pwri", "password12345", dipki::Cms::CipherAlg::Aes192, dipki::Kdf::KeyWrapAlg::Default);
cout << "Cms::MakeEnvData returns " << n << " (expecting 1)" << endl;
assert(1 == n);
cmsfile = "cms_envdata_pwri.p7m";
cout << "FILE:" << cmsfile << endl;
query = "recipientInfoType";
cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
cout << "\nNow read in the enveloped-data objects we made above..." << endl;
std::string prikeystr;
dipki::bvec_t bv;
prikeystr = dipki::Rsa::ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password");
cmsfile = "cms2bob_aes128.p7m";
cout << "FILE:" << cmsfile << endl;
bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeystr);
assert(bv.size() > 0);
cout << "MSG='" << dipki::bvec2str(bv) << "'" << endl;
cmsfile = "cms2bob_aes128auth.p7m";
cout << "FILE:" << cmsfile << endl;
bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeystr);
assert(bv.size() > 0);
cout << "MSG='" << dipki::bvec2str(bv) << "'" << endl;
prikeystr = dipki::Ecc::ReadPrivateKey("lamps-dana.decrypt.p8.pem", "");
cmsfile = "cms2dana_hkdf.p7m";
cout << "FILE:" << cmsfile << endl;
bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeystr);
assert(bv.size() > 0);
cout << "MSG='" << dipki::bvec2str(bv) << "'" << endl;
cmsfile = "cms_envdata_kekri.p7m";
cout << "FILE:" << cmsfile << endl;
bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, "#x0123456789ABCDEFF0E1D2C3B4A59687");
assert(bv.size() > 0);
cout << "MSG='" << dipki::bvec2str(bv) << "'" << endl;
cmsfile = "cms_envdata_pwri.p7m";
cout << "FILE:" << cmsfile << endl;
bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, "password12345");
assert(bv.size() > 0);
cout << "MSG='" << dipki::bvec2str(bv) << "'" << endl;
}
void test_CNV_ShortPathName()
{
cout << "\nTEST SHORT PATH NAME FOR FILENAME IN CHINESE CHARACTERS:" << endl;
// Filename in Chinese characters
std::wstring wfname = L"你好.txt";
std::string shortname = dipki::Cnv::ShortPathName(wfname);
cout << "ShortPath='" << shortname << "'" << endl;
}
void test_FormatErrorMessage()
{
cout << "\nTEST FORMAT ERROR MESSAGE:" << endl;
int r = 0;
cout << dipki::Err::FormatErrorMessage(11) << endl;
// Try to create a signed-data object but passing a missing cert file
std::string privkey = dipki::Rsa::ReadPrivateKey("AlicePrivRSASign.p8e", "password");
try {
r = dipki::Cms::MakeSigData("not-to-be-made.p7m", "excontent.txt", "missing.cert", privkey, dipki::Cms::SigAlg::Rsa_Sha1);
}
catch (std::exception& e) {
cout << e.what() << endl;
}
}
void test_RSA_ReadPublicKey_CSR()
{
cout << "\nREAD PUBLIC KEY FROM CSR:" << endl;
std::string csrfile, keyfile, dn, extns;
std::string keystr;
int r;
// Create a new CSR for LAMPS WG alice
csrfile = "lamps-alice-csr.pem";
keyfile = "lamps-alice.p8"; // No password
dn = "O=IETF;OU=LAMPS WG;CN=Alice Lovelace;";
extns = "keyUsage=digitalSignature,nonRepudiation;extKeyUsage=emailProtection";
r = dipki::X509::CertRequest(csrfile, keyfile, "", dn, extns, dipki::X509::SigAlg::Rsa_Sha256);
cout << "X509::CertRequest returns " << r << " (expecting 0)" << endl;
// Dump details of CSR we just made...
cout << dipki::X509::TextDumpToString(csrfile, dipki::X509::OutputOpts::Ldap) << endl;
// Read in public key from this CSR file to an internal key string
// (New in [v20.7])
keystr = dipki::Rsa::ReadPublicKey(csrfile);
cout << "Keysize=" << dipki::Rsa::KeyBits(keystr) << " bits, ";
cout << "HashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(keystr) << std::dec << endl;
// EXPECTED: Keysize=2048 bits, HashCode=0xca0b84da
}
void test_X509_MakeCert_Ex()
{
cout << "\nMAKE X.509 CERT WITH LATEST OPTIONS V20.7" << endl;
int r;
std::string dn, extns;
std::string certname = "myca-newattributes2022.cer";
std::string keyfile = "lamps-ca.rsa.p8"; // No password
int serialNum = 0x88a;
dn = "C=DE;dnQualifier='distinguisher';initials='E.M.';pseudonym='Super Muster';generationQualifier='3rd.';CN=Erika Mustermann";
extns = "keyUsage=digitalSignature,nonRepudiation;extKeyUsage=emailProtection;rfc822Name=erika@example.com;cRLDistributionPoints=http://dodgycert.example.com/evca.crl;";
r = dipki::X509::MakeCertSelf(certname, keyfile, "", serialNum, 10, dn, extns, dipki::X509::KeyUsageOptions::NoKeyUsageOption, dipki::X509::SigAlg::Rsa_Sha256);
cout << "X509::MakeCertSelf returns " << r << " (expecting 0)" << endl;
// Dump details of cert we just made...
cout << dipki::X509::TextDumpToString(certname, dipki::X509::OutputOpts::Ldap) << endl;
// Query the certificate
cout << "cRLDistributionPointsURI='" << dipki::X509::QueryCert(certname, "cRLDistributionPointsURI") << "'" << endl;
}
void test_hash_SHA3() {
cout << "\nTESTING SHA-3..." << endl;
std::string hashstr;
dipki::bvec_t hashbv;
cout << "SHA-3-256('abc')=3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532" << endl;
hashstr = dipki::Hash::HexFromString("abc", dipki::Hash::Alg::Sha3_256);
cout << hashstr << endl;
hashstr = dipki::Hash::HexFromBytes(dipki::Cnv::FromHex("616263"), dipki::Hash::Alg::Sha3_256);
cout << hashstr << endl;
hashstr = dipki::Hash::HexFromFile("abc.txt", dipki::Hash::Alg::Sha3_256);
cout << hashstr << endl;
hashbv = dipki::Hash::Bytes(dipki::Cnv::FromHex("616263"), dipki::Hash::Alg::Sha3_256);
cout << dipki::Cnv::ToHex(hashbv) << endl;
// Hash of the empty string SHA3-224 ... SHA3-512
hashstr = dipki::Hash::HexFromString("", dipki::Hash::Alg::Sha3_224);
cout << hashstr << endl;
hashstr = dipki::Hash::HexFromString("", dipki::Hash::Alg::Sha3_256);
cout << hashstr << endl;
hashstr = dipki::Hash::HexFromString("", dipki::Hash::Alg::Sha3_384);
cout << hashstr << endl;
hashstr = dipki::Hash::HexFromString("", dipki::Hash::Alg::Sha3_512);
cout << hashstr << endl;
}
void test_Xof() {
cout << "\nTESTING XOF..." << endl;
dipki::bvec_t bv;
dipki::bvec_t msg = dipki::Cnv::FromHex("6ae23f058f0f2264a18cd609acc26dd4dbc00f5c3ee9e13ecaea2bb5a2f0bb6b");
cout << "ALG=Shake256" << endl;
cout << "MSG=" << dipki::Cnv::ToHex(msg) << endl;
cout << "OUTLEN=2000 bits=250 bytes" << endl;
// Output 2000 bits
bv = dipki::Xof::Bytes(2000 / 8, msg, dipki::Xof::XofAlg::Shake256);
cout << "OUT=" << dipki::Cnv::ToHex(bv) << endl;
assert(dipki::Cnv::ToHex(bv) ==
"B9B92544FB25CFE4EC6FE437D8DA2BBE00F7BDAFACE3DE97B8775A44D753C3ADCA3F7C6F183CC8647E229070439AA9539AE1F8F13470C9D3527FFFDEEF6C94F9F0520FF0C1BA8B16E16014E1AF43AC6D94CB7929188CCE9D7B02F81A2746F52BA16988E5F6D93298D778DFE05EA0EF256AE3728643CE3E29C794A0370E9CA6A8BF3E7A41E86770676AC106F7AE79E67027CE7B7B38EFE27D253A52B5CB54D6EB4367A87736ED48CB45EF27F42683DA140ED3295DFC575D3EA38CFC2A3697CC92864305407369B4ABAC054E497378DD9FD0C4B352EA3185CE1178B3DC1599DF69DB29259D4735320C8E7D33E8226620C9A1D22761F1D35BDFF79A");
// MGF1-SHA-256
msg = dipki::Cnv::FromHex("3b5c056af3ebba70d4c805380420585562b32410a778f558ff951252407647e3");
cout << "ALG=Mgf1_Sha256" << endl;
cout << "MSG=" << dipki::Cnv::ToHex(msg) << endl;
cout << "OUTLEN=34 bytes" << endl;
// Output 34 bytes
bv = dipki::Xof::Bytes(34, msg, dipki::Xof::XofAlg::Mgf1_Sha256);
cout << "OUT=" << dipki::Cnv::ToHex(bv) << endl;
assert(dipki::Cnv::ToHex(bv) ==
"5B7EB772AECF04C74AF07D9D9C1C1F8D3A90DCDA00D5BAB1DC28DAECDC86EB87611E");
// One-liner
cout << dipki::Cnv::ToHex(dipki::Xof::Bytes(34,
dipki::Cnv::FromHex("3b5c056af3ebba70d4c805380420585562b32410a778f558ff951252407647e3"),
dipki::Xof::XofAlg::Mgf1_Sha256)) << endl;
}
void test_Prf() {
cout << "\nTESTING PRF..." << endl;
dipki::bvec_t bv;
dipki::bvec_t key = dipki::Cnv::FromHex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F");
dipki::bvec_t msg = dipki::Cnv::FromHex("00010203");
std::string customString = "My Tagged Application";
cout << "ALG=Kmac128" << endl;
cout << "MSG=" << dipki::Cnv::ToHex(msg) << endl;
cout << "KEY=" << dipki::Cnv::ToHex(key) << endl;
cout << "CustomStr='" << customString << "'" << endl;
cout << "OUTLEN=" << 256 / 8 << endl;
bv = dipki::Prf::Bytes(256 / 8, msg, key, dipki::Prf::PrfAlg::Kmac128, customString);
cout << "OUT=" << dipki::Cnv::ToHex(bv) << endl;
assert(dipki::Cnv::ToHex(bv) == "3B1FBA963CD8B0B59E8C1A6D71888B7143651AF8BA0A7070C0979E2811324AA5");
}
void test_KMAC() {
cout << "\nTESTING KMAC..." << endl;
// Ref: `KMAC_samples.pdf` "Secure Hashing - KMAC-Samples" 2017-02-27
/* Sample #1
kmac_test(128, "404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F", "00010203",
256, "", "E5780B0D3EA6F7D3A429C5706AA43A00FADBD7D49628839E3187243F456EE14E");
*/
dipki::bvec_t bv;
dipki::bvec_t key = dipki::Cnv::FromHex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F");
dipki::bvec_t msg = dipki::Cnv::FromHex("00010203");
cout << "Sample #1" << endl;
cout << "ALG=Kmac128" << endl;
cout << "MSG=" << dipki::Cnv::ToHex(msg) << endl;
cout << "KEY=" << dipki::Cnv::ToHex(key) << endl;
cout << "OUTLEN=" << 256 / 8 << endl;
bv = dipki::Prf::Bytes(256 / 8, msg, key, dipki::Prf::PrfAlg::Kmac128);
cout << "OUT=" << dipki::Cnv::ToHex(bv) << endl;
assert(dipki::Cnv::ToHex(bv) == "E5780B0D3EA6F7D3A429C5706AA43A00FADBD7D49628839E3187243F456EE14E");
// One-liner
cout << dipki::Cnv::ToHex(dipki::Prf::Bytes(256 / 8, dipki::Cnv::FromHex("00010203"),
dipki::Cnv::FromHex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F"),
dipki::Prf::PrfAlg::Kmac128)) << endl;
// E5780B0D3EA6F7D3A429C5706AA43A00FADBD7D49628839E3187243F456EE14E
/* Sample #5
kmac_test(256, "404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F",
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7",
256, "", "75358CF39E41494E949707927CEE0AF20A3FF553904C86B08F21CC414BCFD691589D27CF5E15369CBBFF8B9A4C2EB17800855D0235FF635DA82533EC6B759B69");
*/
msg = dipki::Cnv::FromHex("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7");
cout << "Sample #5" << endl;
cout << "ALG=Kmac256" << endl;
cout << "MSG=" << dipki::Cnv::ToHex(msg) << endl;
cout << "KEY=" << dipki::Cnv::ToHex(key) << endl;
cout << "OUTLEN=" << 512 / 8 << endl;
bv = dipki::Prf::Bytes(512 / 8, msg, key, dipki::Prf::PrfAlg::Kmac256);
cout << "OUT=" << dipki::Cnv::ToHex(bv) << endl;
assert(dipki::Cnv::ToHex(bv) == "75358CF39E41494E949707927CEE0AF20A3FF553904C86B08F21CC414BCFD691589D27CF5E15369CBBFF8B9A4C2EB17800855D0235FF635DA82533EC6B759B69");
}
void do_all() {
test_Cnv();
test_Cnv_base58();
test_Cnv_byteutils();
test_Cnv_utf8();
test_Cipher();
test_Cipher_Aead();
test_Cipher_KeyWrap();
test_Cipher_Pad();
test_Cipher_File();
test_Rng();
test_Gen_Errors();
test_Asn1();
test_Hash();
test_Hmac();
test_Wipe();
test_Pbe();
test_Pem();
test_Smime();
test_Pwd(doPrompt);
test_Pfx();
test_Ocsp();
test_Compr();
test_Rsa();
test_Rsa_SaveKey();
test_Rsa_Xml();
test_Rsa_Encode();
test_Rsa_Encrypt();
test_Rsa_Sign();
test_Ecc();
test_Ecc_ByCurve();
test_Ecc_SaveKey();
test_Ecc_ECDH();
test_Sig();
test_Sig_Ecc();
test_Sig_Ecc_det();
test_Sig_PSS();
test_X509_MakeCert();
test_X509_Certs();
test_X509_CertRequest();
test_X509_CRL();
test_CMS_SigData();
test_CMS_EnvData();
test_CMS();
test_CMS_SigData_PSS();
test_CMS_SMIME_Chain();
test_CMS_Data_Bytes();
test_CMS_SigValue();
test_CMS_Pseudo();
test_ReadJWK();
test_HASH_Length();
test_KDF_Bytes();
test_KDF_ForCms();
test_CMS_EnvData_ecdh();
test_CMS_EnvData_adv();
test_CNV_ShortPathName();
test_FormatErrorMessage();
test_Cipher_GCM();
test_RSA_ReadPublicKey_CSR();
test_X509_MakeCert_Ex();
test_hash_SHA3();
test_hmac_SHA3();
test_Xof();
test_Prf();
test_KMAC();
}
int main(int argc, char *argv[])
{
// Set up console and locale for UTF-8 characters
std::setlocale(LC_ALL, "en_US.utf8");
#ifdef _WIN32
// Set console display page to UTF-8
SetConsoleOutputCP(CP_UTF8);
#endif
// Display basic info...
cout << "__cplusplus=" << __cplusplus << endl;
cout << "Gen::Version=" << dipki::Gen::Version() << endl;
cout << "Gen::CompileTime=" << dipki::Gen::CompileTime() << endl;
cout << "Gen::ModuleName=" << dipki::Gen::ModuleName() << endl;
cout << "Gen::Platform=" << dipki::Gen::Platform() << endl;
cout << "Gen::LicenceType=" << dipki::Gen::LicenceType() << endl;
cout << "Gen::ModuleInfo=" << dipki::Gen::ModuleInfo() << endl;
/* Handle command-line arguments
* [some] just do some tests (default = do all)
* [prompt] prompt for keystrokes and passwords
*/
// Local
bool doSome = false;
// Global
doPrompt = false;
// Put C-style arguments into a proper STL array
std::vector<std::string> arguments(argv, argv + argc);
//for (std::string& s : arguments) {
// // do stuff...
//}
for (unsigned int iarg = 1; iarg < arguments.size(); iarg++) {
if (arguments[iarg] == "some")
doSome = true;
if (arguments[iarg] == "prompt")
doPrompt = true;
}
//*************
// DO THE TESTS
//*************
try { // Overall try to catch any errors at all.
if (doSome) // Use "some" in the command line
{ // Do some tests - comment these out as required
cout << "DOING SOME TESTS..." << endl;
//test_Cnv();
//test_Cnv_base58();
//test_Cnv_byteutils();
//test_Cnv_utf8();
//test_Cipher();
//test_Cipher_Aead();
//test_Cipher_KeyWrap();
//test_Cipher_Pad();
//test_Cipher_File();
//test_Rng();
//test_Gen_Errors();
//test_Asn1();
//test_Hash();
//test_Hmac();
//test_Wipe();
//test_Pbe();
//test_Pem();
//test_Smime();
//test_Pwd(doPrompt);
//test_Pfx();
//test_Ocsp();
//test_Compr();
//test_Rsa();
//test_Rsa_SaveKey();
//test_Rsa_Xml();
//test_Rsa_Encode();
//test_Rsa_Encrypt();
//test_Rsa_Sign();
//test_Ecc();
//test_Ecc_ByCurve();
//test_Ecc_SaveKey();
//test_Ecc_ECDH();
//test_Sig();
//test_Sig_Ecc();
//test_Sig_Ecc_det();
//test_Sig_PSS();
//test_X509_MakeCert();
//test_X509_Certs();
//test_X509_CertRequest();
//test_X509_CRL();
//test_CMS_SigData();
//test_CMS_EnvData();
//test_CMS();
//test_CMS_SigData_PSS();
//test_CMS_SMIME_Chain();
//test_CMS_Data_Bytes();
//test_CMS_SigValue();
//test_CMS_Pseudo();
//test_ReadJWK();
//test_HASH_Length();
//test_KDF_Bytes();
//test_KDF_ForCms();
//test_CMS_EnvData_ecdh();
// [v20.6]
//test_CMS_EnvData_adv();
// [v21.0]
//test_CNV_ShortPathName();
//test_FormatErrorMessage();
//test_Cipher_GCM();
//test_RSA_ReadPublicKey_CSR();
//test_X509_MakeCert_Ex();
//test_hash_SHA3();
//test_hmac_SHA3();
test_Xof();
//test_Prf();
test_KMAC();
}
else {
cout << "DOING ALL TESTS..." << endl;
do_all();
}
cout << "\nALL DONE." << endl;
}
catch (std::exception& e) {
cout << e.what() << endl;
cout << "TESTS FAILED TO COMPLETE!!" << endl;
}
return 0;
}