/* $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; }