#! python3 # -*- coding: utf-8 -*- """Some tests for ``cryptosyspki`` the Python interface to CryptoSys PKI""" # test_pki.py: version 22.0.0 # $Date: 2023-10-21 13:31:00 $ # ************************** LICENSE ***************************************** # Copyright (C) 2016-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. # @license MIT # For a copy, see <http://opensource.org/licenses/MIT> # **************************************************************************** from cryptosyspki import * # @UnusedWildImport import cryptosyspki as pki # for pki.__version__ import os import sys import pytest import shutil from glob import iglob _MIN_PKI_VERSION = 220000 # Show some info about the core CryptoSys PKI DLL print("PKI version =", Gen.version()) print("module_name =", Gen.module_name()) print("compile_time =", Gen.compile_time()) print("platform =", Gen.core_platform()) print("licence_type =", Gen.licence_type()) print("module_info =", Gen.module_info()) # Show some system values print("sys.getdefaultencoding()=", sys.getdefaultencoding()) print("sys.getfilesystemencoding()=", sys.getfilesystemencoding()) print("sys.platform()=", sys.platform) print("cwd =", os.getcwd()) if Gen.version() < _MIN_PKI_VERSION: raise Exception('Require PKI version ' + str(_MIN_PKI_VERSION) + ' or greater') # GLOBAL VARS # Remember CWD where we started start_dir = os.getcwd() # Temp directory to use as CWD for tests - set by `setup_temp_dir()` ourtmp_dir = "" # Flag to delete tmp directory when finished - used in `reset_start_dir()` # Change with command-line argument `nodelete` - see `main()` delete_tmp_dir = True # JIGGERY-POKERY FOR A TEMP WORKING DIRECTORY # start_dir/ # test_py # this module # work/ # this _must_ exist # <all required test files> # pki_tmp.XXXXXXXX/ # created by `setup_temp_dir()` # <copy of all required test files> # <files created by tests> def setup_temp_dir(): """Set up a fresh temp directory to work in""" global ourtmp_dir # `work` should be a sub-directory of the cwd and must exist work_dir = os.path.join(start_dir, "work") print("\nExpecting to find work dir:", work_dir) assert os.path.isdir(work_dir) # It should contain all the required test files # Create a temp sub-directory in `work` ourtmp_dir = os.path.join(work_dir, "pki_tmp." + Cnv.tohex(Rng.bytestring(4))) os.mkdir(ourtmp_dir) assert (os.path.isdir(ourtmp_dir)) # copy the required temp files for f in iglob(os.path.join(work_dir, "*.*")): if (os.path.isfile(f) and not f.endswith('.zip')): shutil.copy(f, ourtmp_dir) # Set CWD to be inside temp os.chdir(ourtmp_dir) print("Working in new temp directory:", os.getcwd()) def reset_start_dir(): if not os.path.isdir(start_dir): return if (ourtmp_dir == start_dir): return os.chdir(start_dir) print("") # print("CWD:", os.getcwd()) # Remove the temp direcory if (delete_tmp_dir and 'pki_tmp' in ourtmp_dir): print("Removing temp directory:", ourtmp_dir) # time.sleep(2) shutil.rmtree(ourtmp_dir, ignore_errors=True) # MORE JIGGERY_POKERY FOR py.test @pytest.fixture(scope="module", autouse=True) def divider_module(request): print("\n --- module %s() start ---" % request.module.__name__) setup_temp_dir() def fin(): print("\n --- module %s() done ---" % request.module.__name__) reset_start_dir() request.addfinalizer(fin) @pytest.fixture(scope="function", autouse=True) def divider_function(request): print("\n --- function %s() start ---" % request.function.__name__) os.chdir(ourtmp_dir) def fin(): print("\n --- function %s() done ---" % request.function.__name__) os.chdir(start_dir) request.addfinalizer(fin) # FILE-RELATED UTILITIES def read_binary_file(fname): with open(fname, "rb") as f: return bytearray(f.read()) def write_binary_file(fname, data): with open(fname, "wb") as f: f.write(data) def read_text_file(fname, enc='utf8'): with open(fname, encoding=enc) as f: return f.read() def write_text_file(fname, s, enc='utf8'): with open(fname, "w", encoding=enc) as f: f.write(s) def _print_file(fname): """Print contents of text file.""" s = read_text_file(fname) print(s) def _print_file_hex(fname): """Print contents of file encoded in hexadecimal.""" b = read_binary_file(fname) print(Cnv.tohex(b)) def _dump_file(fname): """Print contents of text file with filename header and rulers.""" s = read_text_file(fname) ndash = (24 if len(s) > 24 else len(s)) print("FILE:", fname) print("-" * ndash) print(s) print("-" * ndash) def _dump_and_print_asn1(fname, opts=0): print("FILE:", fname) try: s = Asn1.text_dump_tostring(fname, opts) print(s) except PKIError as e: print("Woops! PKIError:", e) def _dump_and_print_x509(fname, opts=0): try: s = X509.text_dump_tostring(fname, opts) print(s) except PKIError as e: print("Woops! PKIError:", e) ############# # THE TESTS # ############# def test_version(): assert Gen.version() >= _MIN_PKI_VERSION def test_error_lookup(): print("\nLOOKUP SOME ERROR CODES...") for n in range(10): s = Gen.error_lookup(n) print("error_lookup(" + str(n) + ")=" + s) assert (len(s) > 0) def test_cnv(): print("\nTEST CNV FUNCTIONS...") # hex --> bytes --> base64 b = Cnv.fromhex("FE DC BA 98 76 54 32 10") print("b=0x" + Cnv.tohex(b)) print("b64(b)=" + Cnv.tobase64(b)) assert (Cnv.tobase64(b) == "/ty6mHZUMhA=") # base64 --> bytes --> hex --> base64 b = Cnv.frombase64("/ty6mHZUMhA=") print("b=0x" + Cnv.tohex(b)) assert (Cnv.tohex(b) == "FEDCBA9876543210") print("b64(b)=" + Cnv.tobase64(b)) assert (Cnv.tobase64(b) == "/ty6mHZUMhA=") # hex --> bytes --> base58 b = Cnv.fromhex("00010966776006953D5567439E5E39F86A0D273BEED61967F6") print("b=0x" + Cnv.tohex(b)) print("b58(b)=" + Cnv.tobase58(b)) assert (Cnv.tobase58(b) == "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM") # base58 --> bytes --> hex h = Cnv.tohex(Cnv.frombase58("16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM")) print(h) assert (h == "00010966776006953D5567439E5E39F86A0D273BEED61967F6") # reverse bytes print("Using Cnv.reverse_bytes()...") b = Cnv.fromhex("DEADBEEF01") print("INPUT: ", Cnv.tohex(b)) r = Cnv.reverse_bytes(b) print("OUTPUT:", Cnv.tohex(r)) assert (Cnv.tohex(r) == "01EFBEADDE") # Possible corner cases... print("Test empty string...") b = Cnv.fromhex("") print("INPUT: ", Cnv.tohex(b)) r = Cnv.reverse_bytes(b) print("OUTPUT:", Cnv.tohex(r)) assert (Cnv.tohex(r) == "") b = Cnv.fromhex("01") print("INPUT: ", Cnv.tohex(b)) r = Cnv.reverse_bytes(b) print("OUTPUT:", Cnv.tohex(r)) assert (Cnv.tohex(r) == "01") b = Cnv.fromhex("0102") print("INPUT: ", Cnv.tohex(b)) r = Cnv.reverse_bytes(b) print("OUTPUT:", Cnv.tohex(r)) assert (Cnv.tohex(r) == "0201") print("Using Cnv.num_from_bytes()...") b = Cnv.fromhex("DEADBEEF") print("INPUT:", Cnv.tohex(b)) # Default big-endian order n = Cnv.num_from_bytes(b) print("BE:", hex(n)) assert (0xdeadbeef == n) # Little-endian order n = Cnv.num_from_bytes(b, endn=Cnv.EndianNess.LITTLE_ENDIAN) print("LE:", hex(n)) assert (0xEFBEADDE == n) # Input shorter than 4 bytes is padded on the right with zeros b = b[:3] print("INPUT:", Cnv.tohex(b)) n = Cnv.num_from_bytes(b) print("BE:", hex(n)) assert (0xDEADBE00 == n) n = Cnv.num_from_bytes(b, endn=Cnv.EndianNess.LITTLE_ENDIAN) print("LE:", hex(n)) assert (0xBEADDE == n) print("Using Cnv.num_to_bytes()...") n = 0xDEADBEEF b = Cnv.num_to_bytes(n) print("BE:", Cnv.tohex(b)) b = Cnv.num_to_bytes(n, endn=Cnv.EndianNess.LITTLE_ENDIAN) print("LE:", Cnv.tohex(b)) n = 0x01 b = Cnv.num_to_bytes(n) print("BE:", Cnv.tohex(b)) b = Cnv.num_to_bytes(n, endn=Cnv.EndianNess.LITTLE_ENDIAN) print("LE:", Cnv.tohex(b)) def test_cnv_utf8(): print("\nTEST CNV UTF-8 CHECKS...") print("Bytes representing simple ASCII characters") s = b'abc' print("s=0x" + Cnv.tohex(s)) n = Cnv.utf8_check(s) print("Cnv.utf8_check(s)=", n, "(expecting 1)") print(n, '==>', Cnv.utf8_check_to_string(n)) assert (1 == n) # A string containing a Latin-1 character, LATIN SMALL LETTER E WITH ACUTE # -- this is invalid UTF-8 print("Bytes representing a string containing a Latin-1 character") s = b"M\xe9xico" print("s=0x" + Cnv.tohex(s)) n = Cnv.utf8_check(s) print("Cnv.utf8_check(s)=", n, "(expecting 0)") print(n, '==>', Cnv.utf8_check_to_string(n)) assert (0 == n) # A byte array with a valid UTF-8-encoded array of chinese characters: # zhong guo (U+4E2D, U+56FD) b = Cnv.fromhex('e4b8ade59bbd') print("Chinese characters: zhong guo (U+4E2D, U+56FD) encoded in UTF-8") print("b=0x" + Cnv.tohex(b)) n = Cnv.utf8_check(b) print("Cnv.utf8_check(b)=", n, "(expecting 3)") print(n, '==>', Cnv.utf8_check_to_string(n)) assert (3 == n) # lookup invalid code print("Cnv.utf8_check_to_string(42)=>", Cnv.utf8_check_to_string(42)) print("Bad UTF-8 (chopped)") b = b"\xc3\xb3\xc3\xa9\xc3\xad\xc3\xa1\xc3" print("b=0x" + Cnv.tohex(b)) n = Cnv.utf8_check(b) print("Cnv.utf8_check(b)=", n, "(expecting 0)") print(n, '==>', Cnv.utf8_check_to_string(n)) assert (0 == n) print("Bad UTF-8 (illegal)") b = b"\xef\xbf\xbf" print("b=0x" + Cnv.tohex(b)) n = Cnv.utf8_check(b) print("Cnv.utf8_check(b)=", n, "(expecting 0)") print(n, '==>', Cnv.utf8_check_to_string(n)) assert (0 == n) print("Check some files...") fname = 'test-iso88591.xml' n = Cnv.utf8_check_file(fname) print("Cnv.utf8_check_file('" + fname + "')=", n, "(expecting 0)") print(n, '==>', Cnv.utf8_check_to_string(n)) assert (0 == n) fname = 'test-utf8.xml' n = Cnv.utf8_check_file(fname) print("Cnv.utf8_check_file('" + fname + "')=", n, "(expecting 2)") print(n, '==>', Cnv.utf8_check_to_string(n)) assert (2 == n) fname = 'test-daiwei.xml' n = Cnv.utf8_check_file(fname) print("Cnv.utf8_check_file('" + fname + "')=", n, "(expecting 3)") print(n, '==>', Cnv.utf8_check_to_string(n)) assert (3 == n) def test_cipher(): print("\nTEST BLOCK CIPHER FUNCTIONS...") algstr = "Tdea/CBC/PKCS5" print(algstr) key = bytearray.fromhex('737C791F25EAD0E04629254352F7DC6291E5CB26917ADA32') iv = bytearray.fromhex("B36B6BFB6231084E") pt = bytearray.fromhex("5468697320736F6D652073616D706520636F6E74656E742E") ct = Cipher.encrypt(pt, key, iv, algstr) print(Cnv.tohex(ct)) b = bytearray.fromhex("5468697320736F6D652073616D706520636F6E74656E742E") print(b) assert (ct == bytearray.fromhex( "D76FD1178FBD02F84231F5C1D2A2F74A4159482964F675248254223DAF9AF8E4")) p1 = Cipher.decrypt(ct, key, iv, algstr) print(p1) assert (p1 == pt) print("Use default ECB mode (IV is ignored)") ct = Cipher.encrypt(pt, key, alg=Cipher.Alg.TDEA) print(Cnv.tohex(ct)) p1 = Cipher.decrypt(ct, key, alg=Cipher.Alg.TDEA) print(p1) assert (p1 == pt) ct = Cipher.encrypt(pt, key, iv, mode=Cipher.Mode.CBC, alg=Cipher.Alg.TDEA) print(Cnv.tohex(ct)) p1 = Cipher.decrypt(ct, key, iv, mode=Cipher.Mode.CBC, alg=Cipher.Alg.TDEA) print(p1) assert (p1 == pt) algstr = "Aes128/CBC/pkcs5" print(algstr) key = bytearray.fromhex('0123456789ABCDEFF0E1D2C3B4A59687') iv = bytearray.fromhex("FEDCBA9876543210FEDCBA9876543210") # In Python 3 we must must pass plaintext as bytes; ASCII strings no longer work pt = b"Now is the time for all good men to" ct = Cipher.encrypt(pt, key, iv, algstr) print(Cnv.tohex(ct)) assert (ct == bytearray.fromhex( "C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E17753C7E8DF5975A36677355F5C6584228B")) # Now decrypt using flags instead of alg string p1 = Cipher.decrypt(ct, key, iv, alg=Cipher.Alg.AES128, mode=Cipher.Mode.CBC, pad=Cipher.Pad.PKCS5) print("P':", p1) assert (p1 == pt) algstr = "Aes128/ECB/OneAndZeroes" print(algstr) ct = Cipher.encrypt(pt, key, algmodepad=algstr) print("CT:", Cnv.tohex(ct)) p1 = Cipher.decrypt(ct, key, algmodepad="Aes128/ECB/NoPad") print("Pn:", Cnv.tohex(p1)) p1 = Cipher.decrypt(ct, key, algmodepad=algstr) print("P':", Cnv.tohex(p1)) print("P':", p1) assert (p1 == pt) def test_cipher_hex(): print("\nTEST CIPHER FUNCTIONS USING HEX-ENCODED PARAMETERS...") algstr = "Tdea/CBC/PKCS5" print("ALG:", algstr) keyhex = '737C791F25EAD0E04629254352F7DC6291E5CB26917ADA32' ivhex = "B36B6BFB6231084E" pthex = "5468697320736F6D652073616D706520636F6E74656E742E" okhex = "D76FD1178FBD02F84231F5C1D2A2F74A4159482964F675248254223DAF9AF8E4" print("KY:", keyhex) print("IV:", ivhex) print("PT:", pthex) cthex = Cipher.encrypt_hex(pthex, keyhex, ivhex, algstr) print("CT:", cthex) print("OK:", okhex) assert cthex == okhex, "Cipher.encrypt_hex failed" print("About to decrypt...") # Decrypt using flags instead of alg string p1hex = Cipher.decrypt_hex(cthex, keyhex, ivhex, alg=Cipher.Alg.TDEA, mode=Cipher.Mode.CBC, pad=Cipher.Pad.PKCS5) print("P':", p1hex) assert p1hex == pthex # Another example, this time with the IV prefixed to the ciphertext algstr = "Aes128/CBC/OneAndZeroes" keyhex = '0123456789ABCDEFF0E1D2C3B4A59687' ivhex = "FEDCBA9876543210FEDCBA9876543210" pthex = "4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E20746F" # IV||CT okhex = "FEDCBA9876543210FEDCBA9876543210C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E1771D4CDA34FBFB7E74B321F9A2CF4EA61B" print("KY:", keyhex) print("IV:", ivhex) print("PT:", pthex) cthex = Cipher.encrypt_hex(pthex, keyhex, ivhex, algstr, opts=Cipher.Opts.PREFIXIV) print("CT:", cthex) print("OK:", okhex) assert cthex == okhex, "Cipher.encrypt_hex failed" # Decrypt using flags instead of alg string - this time we don't need the IV argument p1hex = Cipher.decrypt_hex(cthex, keyhex, None, alg=Cipher.Alg.AES128, mode=Cipher.Mode.CBC, pad=Cipher.Pad.ONEANDZEROES, opts=Cipher.Opts.PREFIXIV) print("P':", p1hex) assert (p1hex == pthex) def test_cipher_block(): print("\nTEST CIPHER FUNCTIONS WITH EXACT BLOCK LENGTHS...") key = Cnv.fromhex("0123456789ABCDEFF0E1D2C3B4A59687") iv = Cnv.fromhex("FEDCBA9876543210FEDCBA9876543210") print("KY:", Cnv.tohex(key)) print("IV:", Cnv.tohex(iv)) # In Python 3 plaintext must be bytes, not ASCII string pt = b"Now is the time for all good men" print("PT:", pt) print("PT:", Cnv.tohex(pt)) okhex = "C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E177" ct = Cipher.encrypt_block( pt, key, iv, alg=Cipher.Alg.AES128, mode=Cipher.Mode.CBC) print("CT:", Cnv.tohex(ct)) print("OK:", okhex) assert (okhex.upper() == Cnv.tohex(ct)) p1 = Cipher.decrypt_block( ct, key, iv, alg=Cipher.Alg.AES128, mode=Cipher.Mode.CBC) print("P1:", Cnv.tohex(p1)) print("P1:", p1) # Using defaults (TDEA/ECB) key = Rng.bytestring(Cipher.keybytes(Cipher.Alg.TDEA)) print("KY:", Cnv.tohex(key)) ct = Cipher.encrypt_block(pt, key, iv) print("CT:", Cnv.tohex(ct)) p1 = Cipher.decrypt_block(ct, key, iv) print("P1:", Cnv.tohex(p1)) print("P1:", p1) def test_cipher_file(): print("\nTEST CIPHER FILE FUNCTIONS...") file_pt = "hello.txt" write_text_file(file_pt, "hello world\r\n") print(file_pt + ":", ) _print_file_hex(file_pt) key = Cnv.fromhex("fedcba9876543210fedcba9876543210") iv = Rng.bytestring(Cipher.blockbytes(Cipher.Alg.AES128)) print("IV:", Cnv.tohex(iv)) file_ct = "hello.aes128.enc.dat" n = Cipher.file_encrypt(file_ct, file_pt, key, iv, "aes128-ctr", opts=Cipher.Opts.PREFIXIV) assert (n == 0) print(file_ct + ":", ) _print_file_hex(file_ct) file_chk = "hello.aes128.chk.txt" n = Cipher.file_decrypt(file_chk, file_ct, key, iv, "aes128-ctr", opts=Cipher.Opts.PREFIXIV) assert (n == 0) print(file_chk + ":", ) _print_file_hex(file_chk) # check files are equal assert (read_binary_file(file_pt) == read_binary_file(file_chk)) def test_cipher_gcm(): print("\nTEST CIPHER GCM...") file_pt = "hello.txt" write_text_file(file_pt, "hello world\r\n") print(file_pt + ":", ) _print_file_hex(file_pt) key = Cnv.fromhex("fedcba9876543210fedcba9876543210") print("KY:", Cnv.tohex(key)) # NB Require exact 12-byte IV for GCM iv = Cnv.fromhex("000102030405060708090A0B") print("IV:", Cnv.tohex(iv)) file_ct = "hello.aes128.gcm.enc.dat" n = Cipher.file_encrypt(file_ct, file_pt, key, iv, "aes128-gcm", opts=Cipher.Opts.PREFIXIV) assert (n == 0) print(file_ct + ":", ) _print_file_hex(file_ct) file_chk = "hello.aes128.gcm.chk.txt" n = Cipher.file_decrypt(file_chk, file_ct, key, iv, "aes128-gcm", opts=Cipher.Opts.PREFIXIV) assert (n == 0) print(file_chk + ":", ) _print_file_hex(file_chk) # check files are equal assert (read_binary_file(file_pt) == read_binary_file(file_chk)) print("Encrypt using AES-GCM with hex-encoded parameters...") # Same as EncryptAEAD except without AAD and with hex-encoded arguments keyhex = "2B7E151628AED2A6ABF7158809CF4F3C" ivhex = "000102030405060708090A0B" pthex = Cnv.tohex("This is some sample content.".encode()) print("PT =", pthex) cthex = Cipher.encrypt_hex(pthex, keyhex, ivhex, "aes128-gcm") print("CT =", cthex) # CT = 0FA752259801FD6293B779E382FAD5FA7B5664D62EB63AA66064E189024C709ED4D580FB5E04E001C2D8DF97 assert len(cthex) > 0 dthex = Cipher.decrypt_hex(cthex, keyhex, ivhex, "aes128-gcm") print("DT =", dthex) assert len(dthex) > 0, "Cipher.decrypt failed" print("DT =", Cnv.fromhex(dthex).decode()) # Check decrypted hex is equal to original assert dthex.upper() == pthex.upper() def test_cipher_keywrap(): print("\nTEST CIPHER KEY WRAP FUNCTIONS...") # AES-128 keydata = Cnv.fromhex("00112233 44556677 8899aabb ccddeeff") kek = Cnv.fromhex("c17a44e8 e28d7d64 81d1ddd5 0a3b8914") wk = Cipher.key_wrap(keydata, kek, Cipher.Alg.AES128) print("WK=", Cnv.tohex(wk)) assert (Cnv.tohex(wk) == "503D75C73630A7B02ECF51B9B29B907749310B77B0B2E054") # Unwrap k = Cipher.key_unwrap(wk, kek, Cipher.Alg.AES128) print("UNWRAPPED K=", Cnv.tohex(k)) assert (k == keydata) # AES-256 keydata = Cnv.fromhex( "8cbedec4 8d063e1b a46be8e3 69a9c398 d8e30ee5 42bc347c 4f30e928 ddd7db49") kek = Cnv.fromhex( "9e84ee99 e6a84b50 c76cd414 a2d2ec05 8af41bfe 4bf3715b f894c8da 1cd445f6") wk = Cipher.key_wrap(keydata, kek, Cipher.Alg.AES256) print("WK=", Cnv.tohex(wk)) assert (Cnv.tohex( wk) == "EAFB901F82B98D37F17497063DE3E5EC7246AB57200AE73EDDDDF24AA403DAFA0C5AE151D1746FA4") # Unwrap k = Cipher.key_unwrap(wk, kek, Cipher.Alg.AES256) print("UNWRAPPED K=", Cnv.tohex(k)) assert (k == keydata) # Triple DES print("Using Triple DES the result is always different, but will be 16 bytes longer...") keydata = Cnv.fromhex( "84e7f2d8 78f89fcc cd2d5eba fc56daf7 3300f27e f771cd68") kek = Cnv.fromhex("8ad8274e 56f46773 8edd83d4 394e5e29 af7c4089 e4f8d9f4") wk = Cipher.key_wrap(keydata, kek, Cipher.Alg.TDEA) print("WK=", Cnv.tohex(wk)) assert len(wk) == len(keydata) + 16 # Unwrap k = Cipher.key_unwrap(wk, kek, Cipher.Alg.TDEA) print("UNWRAPPED K=", Cnv.tohex(k)) assert (k == keydata) def test_cipher_pad(): print("\nTEST CIPHER PAD....") data = Cnv.fromhex('FFFFFFFFFF') print("Input data :", Cnv.tohex(data)) padded = Cipher.pad(data, Cipher.Alg.TDEA) print("Padded data:", Cnv.tohex(padded)) unpadded = Cipher.unpad(padded, Cipher.Alg.TDEA) print("Unpadded :", Cnv.tohex(unpadded)) padded = Cipher.pad(data, Cipher.Alg.TDEA, Cipher.Pad.ONEANDZEROES) print("Padded data:", Cnv.tohex(padded)) unpadded = Cipher.unpad(padded, Cipher.Alg.TDEA, Cipher.Pad.ONEANDZEROES) print("Unpadded :", Cnv.tohex(unpadded)) # Pad the empty string data = Cnv.fromhex('') print("Input data :", Cnv.tohex(data)) padded = Cipher.pad(data, Cipher.Alg.AES128) print("Padded data:", Cnv.tohex(padded)) unpadded = Cipher.unpad(padded, Cipher.Alg.AES128) print("Unpadded :", Cnv.tohex(unpadded)) # Pass data as hex strings datahex = 'aaaaaa' print("Input data :", datahex) paddedhex = Cipher.pad_hex(datahex, Cipher.Alg.TDEA) print("Padded data:", paddedhex) unpaddedhex = Cipher.unpad_hex(paddedhex, Cipher.Alg.TDEA) print("Unpadded :", unpaddedhex) paddedhex = Cipher.pad_hex( datahex, Cipher.Alg.TDEA, Cipher.Pad.ONEANDZEROES) print("Padded data:", paddedhex) unpaddedhex = Cipher.unpad_hex( paddedhex, Cipher.Alg.TDEA, Cipher.Pad.ONEANDZEROES) print("Unpadded :", unpaddedhex) def test_rsa_makekeys(): print("\nTEST RSA KEY FUNCTIONS....") print("Making a new 512-bit RSA key pair...") rsaprikeyfile = "myrsaprivate.p8" rsapubkeyfile = "myrsapublic.p1" # We use 512 bits here for speed. In practice 512 bits is insecure. Use at # least 1024 r = Rsa.make_keys(rsapubkeyfile, rsaprikeyfile, 512, Rsa.PublicExponent.RSAEXP_EQ_65537, 'password') assert (0 == r) # Read from new key file into an "internal" key string prikeystr = Rsa.read_private_key(rsaprikeyfile, 'password') # Internal key string should be treated as a "blob". print("prikeystr =", prikeystr) assert (len(prikeystr) > 0) nbits = Rsa.key_bits(prikeystr) print("nbits = ", nbits) assert (nbits > 0) print("hashcode =", Rsa.key_hashcode(prikeystr)) pubkeystr = Rsa.read_public_key(rsapubkeyfile) print("pubkeystr =", pubkeystr) assert (len(pubkeystr) > 0) nbits = Rsa.key_bits(pubkeystr) print("nbits = ", nbits) assert (nbits > 0) print("hashcode =", Rsa.key_hashcode(pubkeystr)) s = Rsa.key_value(pubkeystr, "Exponent") print("exponent in base64:", s) s = Rsa.key_value(pubkeystr, "MODULUS") print("modulus in base64:", s) # Create an XML representation of the internal string - force values in # non-standard hex s = Rsa.to_xmlstring(pubkeystr, Rsa.XmlOptions.HEXBINARY) print("xml (hex):", s) # Again using standard default base64 values s = Rsa.to_xmlstring(pubkeystr) print("xml:", s) # Go back from XML string to a new internal string (this will not be the # same as before) s = Rsa.from_xmlstring(s) print("new keystr:", s) # But should have the same key hashcode print("hashcode =", Rsa.key_hashcode(s)) def test_rsa_errors(): print("\nTry to use an invalid keystr...") try: Rsa.key_hashcode('') except PKIError as e: print("(Expected) PKIError:", e) def test_rsa_savekeys(): print("\nTEST READING RSA KEYS THEN RE-SAVING IN DIFFERENT FORMAT....") # Read in a private key fname = "AlicePrivRSASign.p8e" print("FILE:", fname) prikeystr = Rsa.read_private_key(fname, "password") print("KeyBits:", Rsa.key_bits(prikeystr)) print("KeyIsPrivate:", Rsa.key_isprivate(prikeystr)) print("KeyHashCode:", Rsa.key_hashcode(prikeystr)) print("Save with stronger encryption...") fname = "alice-stronger.p8e" Rsa.save_enc_key(fname, prikeystr, "password123", # Note stronger password here :-) pbescheme=Rsa.PbeScheme.PBKDF2_AES128, params="count=5999", fileformat=Rsa.Format.PEM) # Note change [v22.0] here. Formerly it would have been # pbescheme=Rsa.PbeScheme.PBKDF2_AES128, count=5999, fileformat=Rsa.Format.PEM) _dump_and_print_asn1(fname) print("FILE:", fname, "-->", Asn1.type(fname)) # Check we can read and that key is the same keystrchk = Rsa.read_private_key(fname, "password123") print("KeyHashCode:", Rsa.key_hashcode(keystrchk)) assert (Rsa.key_hashcode(keystrchk) == Rsa.key_hashcode(prikeystr)) print("Save without encryption...") fname = "alice-noencrypt.p8" Rsa.save_key(fname, prikeystr) print("FILE:", fname, "-->", Asn1.type(fname)) # Check we can read and that key is the same keystrchk = Rsa.read_private_key(fname) print("KeyHashCode:", Rsa.key_hashcode(keystrchk)) assert (Rsa.key_hashcode(keystrchk) == Rsa.key_hashcode(prikeystr)) print("Convert private key string to a public key...") pubkeystr = Rsa.publickey_from_private(prikeystr) print("KeyBits:", Rsa.key_bits(pubkeystr)) print("KeyIsPrivate:", Rsa.key_isprivate(pubkeystr)) print("KeyHashCode:", Rsa.key_hashcode(pubkeystr)) print("Check the public and private key strings are matched...") ismatch = Rsa.key_match(prikeystr, pubkeystr) print("Rsa.key_match() returns", ismatch) assert (ismatch) print("Save to a new file in Open-SSL format...") fname = "alice-ssl.pub" Rsa.save_key(fname, pubkeystr, fileformat=Rsa.Format.SSL) print("FILE:", fname, "-->", Asn1.type(fname)) # Check we can read and that key is the same keystrchk = Rsa.read_public_key(fname) print("KeyHashCode:", Rsa.key_hashcode(keystrchk)) assert (Rsa.key_hashcode(keystrchk) == Rsa.key_hashcode(pubkeystr)) def test_rsa_sign(): print("\nTEST RSA SIGN....") print("Sign in two parts: encode then do raw RSA with private key...") # See also Sig.sign() for a cleaner way # Read in a private key prikeystr = Rsa.read_private_key("AlicePrivRSASign.p8e", "password") print(prikeystr) message = b'abc' # We need the length of the RSA key modulus in bytes keybytes = Rsa.key_bytes(prikeystr) print("KEYBYTES =", keybytes) # 1. Encode the message in a block of the correct size # -- this computes the message digest value automatically b = Rsa.encode_msg_for_signature(keybytes, message) print("BLK=[" + Cnv.tohex(b) + "]") # 2. Encrypt the block using "raw" RSA transform sig = Rsa.raw_private(b, prikeystr) print("SIG=[" + Cnv.tohex(sig) + "]") # To verify the signature we read in the public key pubkeystr = Rsa.read_public_key("AliceRSASignByCarl.cer") print(pubkeystr) # 1. Decrypt the signature to a block using "raw" RSA transform blk = Rsa.raw_public(sig, pubkeystr) print("BLK=[" + Cnv.tohex(blk) + "]") # 2a. Decode to extract the full digestinfo # -- normally we don't do this, but we test it here dig = Rsa.decode_digest_for_signature(blk, True) print("DIGINFO=[" + Cnv.tohex(dig) + "]") # 2b. Decode to extract the digest dig = Rsa.decode_digest_for_signature(blk) print("DIG=[" + Cnv.tohex(dig) + "]") # Check we got a match digvalue = Hash.data(b'abc') print("SHA1('abc')=", Cnv.tohex(digvalue)) assert (dig == digvalue) print("Do again but start with digest value, and use SHA-256...") digvalue = Hash.data(b'abc', Hash.Alg.SHA256) print("SHA256('abc')=", Cnv.tohex(digvalue)) b = Rsa.encode_msg_for_signature( keybytes, digvalue, hashalg=Hash.Alg.SHA256, digest_only=True) print("BLK=[" + Cnv.tohex(b) + "]") sig = Rsa.raw_private(b, prikeystr) print("SIG=[" + Cnv.tohex(sig) + "]") print("BLK=[" + Cnv.tohex(b) + "]") # decode to extract the digest dig = Rsa.decode_digest_for_signature(b) print("DIG=[" + Cnv.tohex(dig) + "]") def test_rsa_encrypt(): print("\nTEST RSA ENCRYPT....") print("Encrypt in two parts: encode then do raw RSA with public key...") message = b'Hi Bob.' # Note usually we use RSA to encrypt a session key. print("MSG:", message) # Read in Bob's public key pubkeystr = Rsa.read_public_key("BobRSASignByCarl.cer") print(pubkeystr) # We need the length of the RSA key modulus in bytes keybytes = Rsa.key_bytes(pubkeystr) print("KEYBYTES =", keybytes) blk = Rsa.encode_msg_for_encryption(keybytes, message) print("BLK=[" + Cnv.tohex(blk) + "]") ct = Rsa.raw_public(blk, pubkeystr) print("Note that the ciphertext block will be different each time...") print("CT =[" + Cnv.tohex(ct) + "]") print("Decrypt in two parts: do raw RSA with private key then decode...") # Read in a private key prikeystr = Rsa.read_private_key("BobPrivRSAEncrypt.p8e", "password") print(prikeystr) blk = Rsa.raw_private(ct, prikeystr) print("BLK=[" + Cnv.tohex(blk) + "]") pt = Rsa.decode_msg_for_encryption(blk) print("PT =[" + Cnv.tohex(ct) + "]") # in this case we expect plain ASCII text print("PT='" + str(pt) + "'") assert (pt == message) print("Again using one-step encrypt() and decrypt() this time with OEAP method...") # Use key strings we read in above print("MSG:", message) ct = Rsa.encrypt(message, pubkeystr, method=Rsa.EME.OAEP) print("CT =[" + Cnv.tohex(ct) + "]") pt = Rsa.decrypt(ct, prikeystr, method=Rsa.EME.OAEP) print("PT='" + str(pt) + "'") assert (pt == message) print("") print("RSAES-OAEP Encryption Example 1.1 from `oaep-vect.txt` in `pkcs-1v2-1-vec.zip`") print("Encrypt using RSA-OAEP but set seed to be a fixed value to compare with test vector") # Use key files directly: RSA key file 1024-bit pubkeyfile = "rsa-oaep-1.pub" prikeyfile = "rsa-oaep-1.p8" # unencrypted, no password # Message to be encrypted msg = Cnv.fromhex("6628194e12073db03ba94cda9ef9532397d50dba79b987004afefe34") print("MSG:", Cnv.tohex(msg)) ct = Rsa.encrypt(msg, pubkeyfile, method=Rsa.EME.OAEP, params="seed=18b776ea21069d69776a33e96bad48e1dda0a5ef") print("CT = " + Cnv.tohex(ct)) # Known answer from test vector okhex = "354fe67b4a126d5d35fe36c777791a3f7ba13def484e2d3908aff722fad468fb21696de95d0be911c2d3174f8afcc201035f7b6d8e69402de5451618c21a535fa9d7bfc5b8dd9fc243f8cf927db31322d6e881eaa91a996170e657a05a266426d98c88003f8477c1227094a0d9fa1e8c4024309ce1ecccb5210035d47ac72e8a" print("OK = " + okhex) assert (Cnv.tohex(ct).lower() == okhex.lower()) # Decrypt - the private key is unencrypted with no password pt = Rsa.decrypt(ct, prikeyfile, "", method=Rsa.EME.OAEP) print("PT = " + Cnv.tohex(pt)) assert (Cnv.tohex(pt).lower() == Cnv.tohex(msg).lower()) print("Encrypt using RSA-OAEP using SHA-256 for encoding hash function and SHA-1 for MGF hash function...") # The result will be different each time ct = Rsa.encrypt(msg, pubkeyfile, method=Rsa.EME.OAEP, hashalg=Rsa.HashAlg.SHA256, advopts=Rsa.AdvOpts.MGF1_SHA1) print("CT = " + Cnv.tohex(ct)) # Decrypt - we must specify the parameters used to encrypt pt = Rsa.decrypt(ct, prikeyfile, "", method=Rsa.EME.OAEP, hashalg=Rsa.HashAlg.SHA256, advopts=Rsa.AdvOpts.MGF1_SHA1) print("PT = " + Cnv.tohex(pt)) assert (Cnv.tohex(pt).lower() == Cnv.tohex(msg).lower()) def test_x509_generate(): print("\nTEST X509 FUNCTIONS....") # For convenience we hardcode the password - DON'T DO THIS IN PRACTICE! mypassword = 'password' print("Make a self-signed X.509 certificate:") # Generate a new RSA key pair for the CA # (in practice, do this once) print("Generating a new RSA keypair for the CA...") ca_prikeyfile = 'thecaprikey.p8' ca_pubkeyfile = 'thecapubkey.p1' n = Rsa.make_keys(ca_pubkeyfile, ca_prikeyfile, 1024, Rsa.PublicExponent.RSAEXP_EQ_65537, mypassword) assert (0 == n) assert (os.path.isfile(ca_prikeyfile)) assert (os.path.isfile(ca_pubkeyfile)) # Now use these to create a self-signed X.509 certificate (we only need # the private key file) ca_certfile = 'theca.cer' n = X509.make_cert_self(ca_certfile, ca_prikeyfile, mypassword, 0x01, 5, "C=AU;CN=theCA") print("X509.make_cert_self() returns:", n) assert (0 == n) assert (os.path.isfile(ca_certfile)) print("Created new self-signed X.509 certificate '" + ca_certfile + "'") # Show its contents... _dump_and_print_x509(ca_certfile) # Generate a new RSA key pair for the user # (in practice, do this once) print("Generating a new RSA 1024-bit keypair for the USER...") user_prikeyfile = 'myuserprikey.p8' user_pubkeyfile = 'myuserpubkey.p1' n = Rsa.make_keys(user_pubkeyfile, user_prikeyfile, 1024, Rsa.PublicExponent.RSAEXP_EQ_65537, mypassword) assert (0 == n) assert (os.path.isfile(ca_prikeyfile)) assert (os.path.isfile(ca_pubkeyfile)) # Use the user's public key as the subject of an X.509 cert issued by the # CA my_certfile = 'mycert.cer' n = X509.make_cert(my_certfile, ca_certfile, user_pubkeyfile, ca_prikeyfile, mypassword, 0x101, 4, "C=AU;CN=me", extns="rfc822name=me@myorg.com;keyusage=digitalSignature,nonRepudiation;notBefore=2017-01-01") print("X509.make_cert() returns:", n) assert (0 == n) assert (os.path.isfile(my_certfile)) print("Created X.509 certificate '" + my_certfile + "'") _dump_and_print_x509(my_certfile) # Create a Certificate Signing Request for the user my_csrfile = 'mycsr.p10' n = X509.cert_request(my_csrfile, user_prikeyfile, mypassword, "C=AU;CN=me;O=myorg", extns="rfc822name=me.again@myorg.com;keyusage=dataEncipherment,keyAgreement;ipaddress=127.0.0.1") print("X509.cert_request() returns:", n) assert (0 == n) assert (os.path.isfile(my_csrfile)) print("Created PKCS#10 certificate signing request '" + my_csrfile + "'") _dump_and_print_x509(my_csrfile) # Now use this CSR to create another X.509 cert issued by the CA # -- set `distname = ""` and pass the CSR file in the `subject_pubkeyfile` parameter my_certfilefromcsr = 'mycertfromcsr.cer' n = X509.make_cert(my_certfilefromcsr, ca_certfile, my_csrfile, ca_prikeyfile, mypassword, 0x102, 2, "", sigalg=X509.SigAlg.RSA_SHA256) print("X509.make_cert() returns:", n) assert (0 == n) assert (os.path.isfile(my_certfilefromcsr)) print("Created X.509 certificate '" + my_certfilefromcsr + "'") _dump_and_print_x509(my_certfilefromcsr) print("Check the keyUsage flags...") n = X509.key_usage_flags(my_certfilefromcsr) print("keyUsage bits: n =", format(n, "#08b")) mask = X509.KeyUsageFlags.DATAENCIPHERMENT print("n & KeyUsageFlags.DATAENCIPHERMENT =", bool(n & mask)) mask = X509.KeyUsageFlags.KEYAGREEMENT print("n & KeyUsageFlags.KEYAGREEMENT =", bool(n & mask)) mask = X509.KeyUsageFlags.CRLSIGN print("n & KeyUsageFlags.CRLSIGN =", bool(n & mask)) # Create a Certificate Revocation List (CRL) revoking the cert made above with serial number 0x101 # (Dates need to be hardcoded) ca_crlfile = 'theca.crl' revokedcertlist = "#x101,2020-04-25" n = X509.make_crl(ca_crlfile, ca_certfile, ca_prikeyfile, mypassword, revokedcertlist, extns="thisUpdate=2020-04-25T00:01;nextUpdate=2020-12-31", sigalg=X509.SigAlg.RSA_SHA256, opts=X509.Opts.FORMAT_PEM) print("X509.make_crl() returns:", n) assert (0 == n) assert (os.path.isfile(ca_crlfile)) print("Created CRL file '" + ca_crlfile + "'") _dump_and_print_x509(ca_crlfile) # Query the certificates we made above fname = ca_certfile query = 'subjectName' res = X509.query_cert(fname, query) print("X509.query_cert(" + fname + ", " + query + "):", res) query = 'isCA' res = X509.query_cert(fname, query) print("X509.query_cert(" + fname + ", " + query + "):", res) fname = my_certfile res = X509.query_cert(fname, query) print("X509.query_cert(" + fname + ", " + query + "):", res) fname = my_certfilefromcsr query = 'keyUsageString' res = X509.query_cert(fname, query) print("X509.query_cert(" + fname + ", " + query + "):", res) print("\nTry an invalid query string...") try: res = X509.query_cert(fname, 'badquery') except PKIError as e: print("(Expected) PKIError:", e) print("\nSee if our certificates have been revoked at any time...") # This cert has not been revoked fname = my_certfilefromcsr isrevoked = X509.cert_is_revoked(fname, ca_crlfile) print("X509.cert_is_revoked('" + fname + "') returns", isrevoked) assert (not isrevoked) # This cert was revoked on 2020-04-25 (yes, we can work in the future!) fname = my_certfile print(fname, X509.query_cert(fname, "serialNumber")) isrevoked = X509.cert_is_revoked(fname, ca_crlfile) print("X509.cert_is_revoked('" + fname + "') returns", isrevoked) assert (isrevoked) print("See if certificate was revoked on a certain date...") fname = my_certfile isodate = "2016-01-01" isrevoked = X509.cert_is_revoked(fname, ca_crlfile, isodate=isodate) print("X509.cert_is_revoked('" + fname + ", " + isodate + "') returns", isrevoked) assert (not isrevoked) print("\nRead in X.509 cert as a base64 string") s = X509.read_string_from_file(my_certfile) print(s) print("Now save from this string to a new file in PEM textual format...") fname = 'newcert.cer' n = X509.save_file_from_string(fname, s, in_pem_format=True) print("Created new cert file '" + fname + "'") assert (os.path.isfile(fname)) _dump_file(fname) print("\nCheck if certs are valid now...") fname = 'AliceRSASignByCarl.cer' print("FILE:", fname) isvalid = X509.cert_is_valid_now(fname) s = X509.query_cert(fname, "NotAfter") print(s) print("X509.cert_is_valid_now('" + fname + "')=", isvalid) assert (isvalid) # CAUTION: will not work after year 2039! fname = 'dims.cer' isvalid = X509.cert_is_valid_now(fname) print("X509.cert_is_valid_now('" + fname + "')=", isvalid) assert (not isvalid) print("\nCompute cert thumbprints...") fname = 'AliceRSASignByCarl.cer' print("FILE:", fname) thumb = X509.cert_thumb(fname) print("X509.cert_thumb(SHA-1):", thumb) assert (thumb == 'b30c48855055c2e64ce3196492d4b83831a6b3cb') thumb = X509.cert_thumb(fname, X509.HashAlg.SHA256) print("X509.cert_thumb(SHA-256):", thumb) def test_x509_analyze(): print("\nTESTING X.509 ANALYZE...") fname = 'AliceRSASignByCarl.cer' print("FILE:", fname) query = "serialNumber" res = X509.query_cert(fname, query) print("X509.query_cert(" + query + "):", res) print("Use `opts=X509.Opts.DECIMAL`...") res = X509.query_cert(fname, query, opts=X509.Opts.DECIMAL) print("X509.query_cert(" + query + "):", res) h = X509.cert_thumb(fname) print("cert_thumb():", h) h = X509.cert_hashissuersn(fname) print("hash(issuer+serialnumber):", h) fname = 'dims.cer' print("FILE:", fname) query = "issuerName" res = X509.query_cert(fname, query) print("X509.query_cert(" + query + "):", res) print("Use `opts=X509.Opts.LDAP`...") res = X509.query_cert(fname, query, opts=X509.Opts.LDAP) print("X509.query_cert(" + query + "):", res) fname = 'smallca.cer' print("FILE:", fname) query = "notAfter" res = X509.query_cert(fname, query) print("X509.query_cert(" + query + "):", res) query = "cRLDistributionPointsURI" res = X509.query_cert(fname, query) print("X509.query_cert(" + query + "):", res) # Test UTF-8-encoded output for a certificate with both Spanish and Chinese chars # CAUTION: these may not print properly in a console or may cause a 'UnicodeEncodeError' if stdout is redirected to a file fname = "maria-mx.cer" print("FILE:", fname) query = "issuerName" res = X509.query_cert(fname, query, opts=X509.Opts.UTF8) print("X509.query_cert(" + query + "):", res) query = "subjectName" res = X509.query_cert(fname, query, opts=X509.Opts.UTF8) print("X509.query_cert(" + query + "):", res) print(X509.text_dump_tostring(fname, opts=X509.Opts.UTF8)) # Extract the public key from the X.509 cert keystr = Rsa.read_public_key(fname) print("Public key bits:", Rsa.key_bits(keystr)) hcode = Rsa.key_hashcode(keystr) print("Rsa.key_hashcode():", hcode) h = X509.cert_thumb(fname, X509.HashAlg.MD5) print("X509.cert_thumb(MD5):", h) h = X509.cert_hashissuersn(fname) print("hash(issuer+serialnumber):", h) def test_x509_validate(): print("\nTESTING X.509 VALIDATE...") print("1. A valid certificate and its issuer:") certfile = "AliceRSASignByCarl.cer" issuerfile = "CarlRSASelf.cer" print("CERTFILE:", certfile) print("ISSUERFILE:", issuerfile) print("Is cert valid now?") isok = X509.cert_is_valid_now(certfile) print("cert_is_valid_now:", isok) # This will fail in the year 2040 :-) assert (isok) print("Was cert signed by issuer?") isok = X509.cert_is_verified(certfile, issuerfile) print("cert_is_verified:", isok) assert (isok) print("Validate the certificate path...") certlist = certfile + ";" + issuerfile print("CERTLIST:", certlist) isok = X509.cert_path_is_valid(certlist) print("cert_path_is_valid:", isok) assert (isok) print("2. A valid but expired certificate and its issuer:") certfile = "dims.cer" issuerfile = "UTNUSERFirst-Object.cer" print("CERTFILE:", certfile) print("ISSUERFILE:", issuerfile) print("Is cert valid now?") d = X509.query_cert(certfile, "notAfter") print(" X509.query_cert('notAfter'):", d) isok = X509.cert_is_valid_now(certfile) print("cert_is_valid_now:", isok, "(expected False)") # This will fail if you go back in time to before Nov 2011 :-) assert (not isok) print("Was cert signed by issuer?") isok = X509.cert_is_verified(certfile, issuerfile) print("cert_is_verified:", isok) assert (isok) print("Validate the certificate path...") certlist = certfile + ";" + issuerfile print("CERTLIST:", certlist) print("a) This will fail because a cert has expired...") try: isok = X509.cert_path_is_valid(certlist) except PKIError as e: print("(Expected):", e) print("b) Now try again with X509.Opts.NO_TIMECHECK...") isok = X509.cert_path_is_valid(certlist, no_timecheck=True) print("cert_path_is_valid(NO_TIMECHECK):", isok) print("3. A valid certificate but the wrong issuer:") certfile = "AliceRSASignByCarl.cer" issuerfile = "UTNUSERFirst-Object.cer" print("CERTFILE:", certfile) print("ISSUERFILE:", issuerfile) print("Was cert signed by issuer?") isok = X509.cert_is_verified(certfile, issuerfile) print("cert_is_verified:", isok, "(expected False)") assert (not isok) def test_x509_extract(): print("\nTESTING X.509 EXTRACT...") print("Extract cert files from a P7 chain file") p7file = "bob.p7b" print("P7 FILE:", p7file) n = X509.get_cert_count_from_p7(p7file) print("X509.get_cert_count_from_p7()=", n) assert (n > 0) # Extract each cer file from p7 file for i in range(1, n + 1): print("Count:", i) fname = "bobcert" + str(i) + ".cer" print(" OUTFILE:", fname) r = X509.get_cert_from_p7(fname, p7file, i) print(" X509.get_cert_from_p7() returns:", r) assert (r > 0) print(" X509_thumb():", X509.cert_thumb(fname)) print("Extract cert files from a PFX (p12) file") pfxfile = "alice.pfx" print("PFX FILE:", pfxfile) fname = 'alice_cert.cer' print(" OUTFILE:", fname) r = X509.get_cert_from_pfx(fname, pfxfile, "password") assert (r > 0) print(" ASN1 TYPE(" + fname + ")=" + Asn1.type(fname)) print(" X509_thumb():", X509.cert_thumb(fname)) # Show thumbprints of known certificate files... print("X509_thumb(Carl): ", X509.cert_thumb("CarlRSASelf.cer")) print("X509_thumb(Alice):", X509.cert_thumb("AliceRSASignByCarl.cer")) print("X509_thumb(Bob): ", X509.cert_thumb("BobRSASignByCarl.cer")) print("Extract all cert files as P7 chain from a PFX file") pfxfile = "alice.pfx" print("PFX FILE:", pfxfile) fname = 'alice_certs.p7' print(" OUTFILE:", fname) r = X509.get_p7chain_from_pfx(fname, pfxfile, "password") assert (r > 0) print(" ASN1 TYPE(" + fname + ")=" + Asn1.type(fname)) def test_rng(): print("\nTESTING RANDOM NUMBER GENERATOR...") # Initialize from seed file. File is created if it does not exist. # Optional but recommended for extra security seedfile = 'myseedfile.dat' n = Rng.initialize(seedfile) assert (0 == n) print('Rng.initialize() returns', n, ". Contents of seed file:") sd = read_binary_file(seedfile) print(Cnv.tohex(sd)) assert (len(sd) == Rng.SEED_BYTES) print("5 random byte arrays") for i in range(5): b = Rng.bytestring((i + 2) * 2) print(Cnv.tohex(b).lower()) print("5 random numbers in the range [-1 million, +1 million]") for i in range(5): r = Rng.number(-1000000, 1000000) print(r) assert (-1000000 <= r and r <= 1000000) print("5 random octet values") s = "" # fudge to do in one line for i in range(5): r = Rng.octet() assert (0 <= r and r <= 255) s += str(r) + " " print(s) # Update seedfile n = Rng.update_seedfile(seedfile) assert (0 == n) print('Rng.update_seedfile() returns', n, ". Contents of seed file:") sd = read_binary_file(seedfile) print(Cnv.tohex(sd)) assert (len(sd) == Rng.SEED_BYTES) def test_hash(): print("\nTESTING Hash...") # write a file containing the 3 bytes 'abc' write_text_file('abc.txt', 'abc') _dump_file('abc.txt') abc_hex = Cnv.tohex(b'abc') print("'abc' in hex:", abc_hex) # Use default SHA-1 algorithm print("Using default SHA-1...") b = Hash.data(b'abc') print("Hash.data('abc'):", Cnv.tohex(b)) h = Hash.hex_from_data(b'abc') print("Hash.hex_from_data('abc'):", h) h = Hash.hex_from_data(bytearray.fromhex('616263')) print("Hash.hex_from_data('abc'):", h) h = Hash.hex_from_hex(abc_hex) print("Hash.hex_from_hex(abc_hex):", h) b = Hash.file('abc.txt') print("Hash.file('abc.txt'):", Cnv.tohex(b)) h = Hash.hex_from_file('abc.txt') print("Hash.hex_from_file('abc.txt'):", h) print("Using SHA-256...") b = Hash.data(b'abc', Hash.Alg.SHA256) print("Hash.data('abc'):", Cnv.tohex(b)) h = Hash.hex_from_hex(abc_hex, Hash.Alg.SHA256) print("Hash.hex_from_hex(abc_hex):", h) b = Hash.file('abc.txt', Hash.Alg.SHA256) print("Hash.file('abc.txt'):", Cnv.tohex(b)) h = Hash.hex_from_file('abc.txt', Hash.Alg.SHA256) print("Hash.hex_from_file('abc.txt'):", h) # compute SHA256(SHA256('abc')) using Hash.double() b = Hash.double(b'abc', Hash.Alg.SHA256) print("Hash.double('abc',SHA256):", Cnv.tohex(b)) # and again by composition b2 = Hash.data(Hash.data(b'abc', Hash.Alg.SHA256), Hash.Alg.SHA256) print("SHA256(SHA256('abc')): ", Cnv.tohex(b2)) def test_hash_sha3(): print("\nTESTING Hash(SHA3)...") # write a file containing the 3 bytes 'abc' write_text_file('abc.txt', 'abc') _dump_file('abc.txt') abc_hex = Cnv.tohex(b'abc') print("'abc' in hex:", abc_hex) b = Hash.data(b'abc', Hash.Alg.SHA3_224) print("Hash.data('abc'):", Cnv.tohex(b)) assert (b == Cnv.fromhex('e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf')) h = Hash.hex_from_hex(abc_hex, Hash.Alg.SHA3_256) print("Hash.hex_from_hex(abc_hex):", h) assert (Cnv.fromhex(h) == Cnv.fromhex('3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532')) b = Hash.file('abc.txt', Hash.Alg.SHA3_384) print("Hash.file('abc.txt'):", Cnv.tohex(b)) assert (b == Cnv.fromhex( 'ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25')) h = Hash.hex_from_file('abc.txt', Hash.Alg.SHA3_512) print("Hash.hex_from_file('abc.txt'):", h) assert (Cnv.fromhex(h) == Cnv.fromhex( 'b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0')) def test_hmac(): print("\nTESTING Hmac...") print("Test case 4 from RFC 2202 and RFC 4231") key = Cnv.fromhex('0102030405060708090a0b0c0d0e0f10111213141516171819') print("key: ", Cnv.tohex(key)) # data = 0xcd repeated 50 times data = bytearray([0xcd] * 50) print("data:", Cnv.tohex(data)) b = Hmac.data(data, key) print("HMAC-SHA-1: ", Cnv.tohex(b)) assert (b == Cnv.fromhex('4c9007f4026250c6bc8414f9bf50c86c2d7235da')) b = Hmac.data(data, key, Hmac.Alg.MD5) print("HMAC-MD5: ", Cnv.tohex(b)) assert (b == Cnv.fromhex('697eaf0aca3a3aea3a75164746ffaa79')) b = Hmac.data(data, key, Hmac.Alg.SHA256) print("HMAC-SHA-256:", Cnv.tohex(b)) assert (b == Cnv.fromhex( '82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b')) h = Hmac.hex_from_data(data, key, Hmac.Alg.SHA256) print("HMAC-SHA-256:", h) assert (h == '82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b') b = Hmac.data(data, key, Hmac.Alg.SHA512) print("HMAC-SHA-512:", Cnv.tohex(b)) assert (b == Cnv.fromhex( 'b0ba465637458c6990e5a8c5f61d4af7 e576d97ff94b872de76f8050361ee3db a91ca5c11aa25eb4d679275cc5788063 a5f19741120c4f2de2adebeb10a298dd')) print("Test case 7 from RFC 4231") key = bytearray([0xaa] * 131) print("key: ", Cnv.tohex(key).lower()) data = b"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." print("data:", data) b = Hmac.data(data, key, Hmac.Alg.SHA224) print("HMAC-SHA-224:", Cnv.tohex(b)) assert (b == Cnv.fromhex( '3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1')) # HMAC hex <-- hex print("Test case 1 from RFC 2202 and RFC 4231") keyhex = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" # (20 bytes) datahex = "4869205468657265" # ("Hi There") print("key: ", keyhex) print("data:", datahex) h = Hmac.hex_from_hex(datahex, keyhex) print("HMAC-SHA-1:", h) assert (h == "b617318655057264e28bc0b6fb378c8ef146be00") h = Hmac.hex_from_hex(datahex, keyhex, Hmac.Alg.SHA256) print("HMAC-SHA-256:", h) assert (h == "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7") def test_hmac_sha3(): print("\nTESTING Hmac(SHA-3)...") print("NIST HMAC_SHA3-256.pdf Sample #1") key = Cnv.fromhex('000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F') print("key: ", Cnv.tohex(key)) data = b'Sample message for keylen<blocklen' print("data:", data.decode()) b = Hmac.data(data, key, Hmac.Alg.SHA3_256) print("HMAC-SHA-3-256:", Cnv.tohex(b)) assert (b == Cnv.fromhex('4fe8e202c4f058e8dddc23d8c34e467343e23555e24fc2f025d598f558f67205')) print("NIST HMAC_SHA3-512.pdf Sample #3") key = Cnv.fromhex("""000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F 202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F 404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F 606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F 8081828384858687""") print("key: ", Cnv.tohex(key)) data = b'Sample message for keylen>blocklen' print("data:", data.decode()) b = Hmac.data(data, key, Hmac.Alg.SHA3_512) print("HMAC-SHA-3-512:", Cnv.tohex(b)) assert (b == Cnv.fromhex( '5f464f5e5b7848e3885e49b2c385f0694985d0e38966242dc4a5fe3fea4b37d46b65ceced5dcf59438dd840bab22269f0ba7febdb9fcf74602a35666b2a32915')) def test_wipe(): print("\nTESTING Wipe...") print("Note that Wipe.data() just zeroizes the data, it does not change the length") b = Cnv.fromhex('3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1') print("BEFORE b=", Cnv.tohex(b)) Wipe.data(b) print("AFTER Wipe.data() b=", Cnv.tohex(b)) print("AFTER Wipe.data()", str(b)) print([c for c in b]) assert all([c == 0 for c in b]) # works with a bytes type but not with an immutable string type s = b"a string" print("BEFORE s='" + str(s) + "'") print([c for c in s]) Wipe.data(s) print("AFTER Wipe.data()", str(s)) print([c for c in s]) assert all([c == 0 for c in s]) # write a file containing some text fname = 'tobedeleted.txt' write_text_file(fname, 'Some secret text in this file.') _dump_file(fname) assert (os.path.isfile(fname)) Wipe.file(fname) print("After Wipe.file(), isfile() returns", os.path.isfile(fname)) assert (not os.path.isfile(fname)) def test_asn1(): print("\nTESTING ASN.1...") fname = "smallca.cer" print("FILE:", fname) t = Asn1.type(fname) print("Asn1.type():", t) dumpfile = 'asn1dump.txt' Asn1.text_dump(dumpfile, fname, opts=Asn1.Opts.ADDLEVELS) print("Asn1.text_dump():") _print_file(dumpfile) def test_ocsp(): print("\nTESTING Ocsp...") # Create an OCSP request to check a code-signing certificate issued by the holder # of certificate in the file `UTNUSERFirst-Object.cer` issuercert = "UTNUSERFirst-Object.cer" print("Issuer Cert=", issuercert) certfile = "dims.cer" print("Cert File to check=", certfile) req = Ocsp.make_request(issuercert, certfile) print("OCSPRequest=", req) assert len(req) > 0 # We can analyze the ASN.1 data structure from the base64 string _dump_and_print_asn1(req) # Pass a hex serial number instead of filename serialnum = "#x 00 FB C7 23 22 8C 8C 80 22 D8 85 92 23 DE E7 06 60" print("Cert SerialNumber=", serialnum) req1 = Ocsp.make_request(issuercert, serialnum) print("OCSPRequest=", req1) # These should be the same assert (req1 == req) # Now read a response responsefile = "ocsp_response_ok_dims.dat" print("ResponseFile=", responsefile) resp = Ocsp.read_response(responsefile, issuercert) print("OCSPResponse:", resp) def test_ecc(): print("\nTESTING Ecc...") pubkeyfile = "myeckeyp256.pub" prikeyfile = "myeckeyp256.p8" password = "password" curvename = "P-256" # Create a new pair of ECC keys, saved as DER-encoded files n = Ecc.make_keys(pubkeyfile, prikeyfile, curvename, password) assert (0 == n) _dump_and_print_asn1(pubkeyfile) print(pubkeyfile + ": " + Asn1.type(pubkeyfile)) print(prikeyfile + ": " + Asn1.type(prikeyfile)) # Read in private key to an internal key string intpristr = Ecc.read_private_key(prikeyfile, password) # This will be different each time, even for the same key print(intpristr) # But the key hash code will be the same print("key_hash_code =", Ecc.key_hashcode(intpristr)) # Query this string for info query = "keyBits" r = Ecc.query_key(intpristr, query) print("Ecc.query_key(" + query + ")=", r) query = "curveName" r = Ecc.query_key(intpristr, query) print("Ecc.query_key(" + query + ")=", r) query = "privateKey" r = Ecc.query_key(intpristr, query) print("Ecc.query_key(" + query + ")=", r) # Read in a key from its hex representation print("A NIST P-192 public key in X9.63 uncompressed format") keyhex = "0496C248BE456192FA1380CCF615D171452F41FF31B92BA733524FD77168DEA4425A3EA8FD79B98DC7AFE83C86DCC39A96" curvename = "prime192v1" # A synonym for "P-192" print("KEYHEX:", keyhex) print("CURVE: ", curvename) intpubstr = Ecc.read_key_by_curve(keyhex, curvename) print("keyBits=", Ecc.query_key(intpubstr, "keyBits")) f = Ecc.query_key(intpubstr, "isPrivate") print("isPrivate=", f) assert (not f) print("A Bitcoin private key in base58 form") keyb58 = "6ACCbmy9qwiFcuVgvxNNwMPfoghobzznWrLs3v7t3RmN" curvename = "secp256k1" print("KEYB58:", keyb58) print("CURVE: ", curvename) intpristr = Ecc.read_key_by_curve( Cnv.tohex(Cnv.frombase58(keyb58)), curvename) print("keyBits=", Ecc.query_key(intpristr, "keyBits")) f = Ecc.query_key(intpristr, "isPrivate") print("isPrivate=", f) assert (f) print("key_hash_code =", Ecc.key_hashcode(intpristr)) print("Extract the public key in hex form from the internal private key string") pubkey = Ecc.query_key(intpristr, 'publicKey') print("publicKey=", pubkey) assert pubkey == '04654bacc2fc7a3bde0f8eb95dc5aac9ba1df732255cf7f2eb7e1e8e6edbb1f4188ff3752ac4bdf1e3a31a488747745dddcbabd33a10c3b52d737c092851da13c0' print("Extract the public key as an internal key string") intpubstr = Ecc.publickey_from_private(intpristr) print("intpubstr=", intpubstr) print("key_hash_code =", Ecc.key_hashcode(intpubstr)) print("Query this internal public key string...") query = "keybits" print("Ecc.query_key(" + query + ")=", Ecc.query_key(intpubstr, query)) query = "curvename" print("Ecc.query_key(" + query + ")=", Ecc.query_key(intpubstr, query)) query = "isPrivate" print("Ecc.query_key(" + query + ")=", Ecc.query_key(intpubstr, query)) print("Save keys in various new file forms...") # Note we must save from the internal key string forms # Default unencrypted key files... newkeyfile = 'myecpublic.key' n = Ecc.save_key(newkeyfile, intpubstr) # Show what type of file we made print("File:", newkeyfile, "-->", Asn1.type(newkeyfile)) # and read it back in to check it's really OK... s = Ecc.read_public_key(newkeyfile) assert (Ecc.query_key(s, 'keyBits') == 256) newkeyfile = 'myecprivate.key' n = Ecc.save_key(newkeyfile, intpristr) print("File:", newkeyfile, "-->", Asn1.type(newkeyfile)) s = Ecc.read_private_key(newkeyfile) assert (Ecc.query_key(s, 'keyBits') == 256) # Alternative PKCS#8 key type (unencrypted) newkeyfile = 'myecprivate.p8' n = Ecc.save_key(newkeyfile, intpristr, keytype=Ecc.KeyType.PKCS8, fileformat=Ecc.Format.PEM) print("File:", newkeyfile, "-->", Asn1.type(newkeyfile)) s = Ecc.read_private_key(newkeyfile) assert (Ecc.query_key(s, 'keyBits') == 256) # Encrypted private key (always PKCS#8) newkeyfile = 'myecprivate_enc.p8' n = Ecc.save_enc_key(newkeyfile, intpristr, 'password') print("File:", newkeyfile, "-->", Asn1.type(newkeyfile)) s = Ecc.read_private_key(newkeyfile, 'password') assert (Ecc.query_key(s, 'keyBits') == 256) # with stronger encryption newkeyfile = 'myecprivate_encx.p8' n = Ecc.save_enc_key(newkeyfile, intpristr, 'password', pbescheme=Ecc.PbeScheme.PBKDF2_AES256, params="count=5999;prf=hmacWithSHA256;") print("File:", newkeyfile, "-->", Asn1.type(newkeyfile)) s = Ecc.read_private_key(newkeyfile, 'password') assert (Ecc.query_key(s, 'keyBits') == 256) # Dump this _dump_and_print_asn1(newkeyfile) def test_ecc_brainpool(): print("\nTESTING ECC BRAINPOOL...") pubkeyfile = "myeckeyBrainpool384.pub" prikeyfile = "myeckeyBrainpool384.p8e" password = "password" curvename = "brainpoolP384r1" # Create a new pair of ECC keys, saved as DER-encoded files with stronger encryption n = Ecc.make_keys(pubkeyfile, prikeyfile, curvename, password, Ecc.PbeScheme.PBKDF2_AES256, "count=8999;prf=hmacWithSha512", Ecc.Format.PEM) assert (0 == n) _dump_and_print_asn1(pubkeyfile) print(pubkeyfile + ": " + Asn1.type(pubkeyfile)) print(prikeyfile + ": " + Asn1.type(prikeyfile)) # Read in private key to an internal key string intpristr = Ecc.read_private_key(prikeyfile, password) # This will be different each time, even for the same key print(intpristr) # But the key hash code will be the same print("pri_key_hash_code =", Ecc.key_hashcode(intpristr)) print("pub_key_hash_code =", Ecc.key_hashcode(Ecc.read_public_key(pubkeyfile))) # Query this string for info query = "keyBits" r = Ecc.query_key(intpristr, query) print("Ecc.query_key(" + query + ")=", r) query = "curveName" r = Ecc.query_key(intpristr, query) print("Ecc.query_key(" + query + ")=", r) query = "privateKey" r = Ecc.query_key(intpristr, query) print("Ecc.query_key(" + query + ")=", r) print("Sign 'abc' using ECDSA...") msg = b'abc' print("MSG =", Cnv.tohex(msg)) # Compute the signature value NB this will be different each time because we use a new key each time sigval = Sig.sign_data(msg, intpristr, "", Sig.Alg.ECDSA_SHA384, Sig.Opts.DETERMINISTIC) print("SIG =", sigval) # Verify the signature isok = Sig.data_is_verified(sigval, msg, pubkeyfile, Sig.Alg.ECDSA_SHA384) print("Sig.data_is_verified returns ", isok) def test_ecc_dh_shared_secret(): print("\nTEST ECC DIFFIE-HELLMAN SHARED SECRET...") ''' Ref: CAVS 14.1 ECC CDH Primitive (SP800 - 56A Section 5.7.1.2) Test Information for "testecccdh" https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/components/ecccdhtestvectors.zip Extract: ---------------------------------------- [P-256] COUNT = 0 QCAVSx = 700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287 QCAVSy = db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac dIUT = 7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534 QIUTx = ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230 QIUTy = 28af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141 ZIUT = 46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b -------------------------------------- ''' # Read in private key (dIUT) prikeystr = Ecc.read_key_by_curve("7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534", Ecc.CurveName.P_256) # Compose public key from QCAVSx+y in hex form pubkeyhex = "04" + "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287" \ + "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac" pubkeystr = Ecc.read_key_by_curve(pubkeyhex, Ecc.CurveName.P_256) # Compute shared secret zz = Ecc.dh_shared_secret(prikeystr, pubkeystr) print("Computed DH shared secret =", Cnv.tohex(zz)) # Compare to expected result (ZIUT) okhex = "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b" print("Expected DH shared secret =", okhex) assert (Cnv.tohex(zz).lower() == okhex.lower()) def test_ecc_dh_shared_secret_x25519(): print("\nTEST X25519 ECDH DIFFIE-HELLMAN SHARED SECRET...") ''' // Ref: RFC7748 Section 6.1 // https://tools.ietf.org/html/rfc7748#section-6.1 Test vector: Alice's private key, a: 77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a Alice's public key, X25519(a, 9): 8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a Bob's private key, b: 5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb Bob's public key, X25519(b, 9): de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f Their shared secret, K: 4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742 ''' okhex = "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742" # NOTE: for X25519 curve keys we must specify private or public (because they are both the same length) # Read in Alice's private key prikeystr = Ecc.read_key_by_curve("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a", Ecc.CurveName.X25519, ispublic=False) # Read in Bob's public key pubkeystr = Ecc.read_key_by_curve("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f", Ecc.CurveName.X25519, ispublic=True) print("Our private key: ", Ecc.query_key(prikeystr, "privateKey")) print("Their public key:", Ecc.query_key(pubkeystr, "publicKey")) # Compute shared secret zz = Ecc.dh_shared_secret(prikeystr, pubkeystr) print("Computed DH shared secret =", Cnv.tohex(zz)) # Compare to expected result print("Expected DH shared secret =", okhex) assert (Cnv.tohex(zz).lower() == okhex.lower()) # OTHER WAY AROUND # Read in Bobs's private key prikeystr = Ecc.read_key_by_curve("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb", Ecc.CurveName.X25519, ispublic=False) # Read in Alice's public key pubkeystr = Ecc.read_key_by_curve("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a", Ecc.CurveName.X25519, ispublic=True) print("Our private key: ", Ecc.query_key(prikeystr, "privateKey")) print("Their public key:", Ecc.query_key(pubkeystr, "publicKey")) # Compute shared secret zz = Ecc.dh_shared_secret(prikeystr, pubkeystr) print("Computed DH shared secret =", Cnv.tohex(zz)) # Compare to expected result print("Expected DH shared secret =", okhex) assert (Cnv.tohex(zz).lower() == okhex.lower()) def test_ecc_dh_shared_secret_x448(): print("\nTEST X448 ECDH DIFFIE-HELLMAN SHARED SECRET...") ''' // Ref: RFC7748 Section 6.2 // https://tools.ietf.org/html/rfc7748#section-6.2 Test vector: Alice's private key, a: 9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d d9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b Alice's public key, X448(a, 5): 9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c 22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0 Bob's private key, b: 1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d 6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d Bob's public key, X448(b, 5): 3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b430 27d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609 Their shared secret, K: 07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282b b60c0b56fd2464c335543936521c24403085d59a449a5037514a879d ''' okhex = "07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d" # Read in Alice's private key prikeystr = Ecc.read_key_by_curve( "9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b", Ecc.CurveName.X448, ispublic=False) # Read in Bob's public key pubkeystr = Ecc.read_key_by_curve( "3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609", Ecc.CurveName.X448, ispublic=True) print("Our private key: ", Ecc.query_key(prikeystr, "privateKey")) print("Their public key:", Ecc.query_key(pubkeystr, "publicKey")) # Compute shared secret zz = Ecc.dh_shared_secret(prikeystr, pubkeystr) print("Computed DH shared secret =", Cnv.tohex(zz)) # Compare to expected result print("Expected DH shared secret =", okhex) assert (Cnv.tohex(zz).lower() == okhex.lower()) # OTHER WAY AROUND # Read in Bobs's private key prikeystr = Ecc.read_key_by_curve( "1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d", Ecc.CurveName.X448, ispublic=False) # Read in Alice's public key pubkeystr = Ecc.read_key_by_curve( "9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0", Ecc.CurveName.X448, ispublic=True) print("Our private key: ", Ecc.query_key(prikeystr, "privateKey")) print("Their public key:", Ecc.query_key(pubkeystr, "publicKey")) # Compute shared secret zz = Ecc.dh_shared_secret(prikeystr, pubkeystr) print("Computed DH shared secret =", Cnv.tohex(zz)) # Compare to expected result print("Expected DH shared secret =", okhex) assert (Cnv.tohex(zz).lower() == okhex.lower()) def test_pbe(): print("\nTESTING PASSWORD-BASED ENCRYPTION (PBE)...") password = 'password' salt = Cnv.fromhex('78 57 8E 5A 5D 63 CB 06') count = 2048 print("password = '" + password + "'") print("salt = 0x" + Cnv.tohex(salt)) print("count =", count) dklen = 24 print("dklen =", dklen) dk = Pbe.kdf2(dklen, password, salt, count) print("dk =", Cnv.tohex(dk)) assert Cnv.tohex(dk) == "BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643" # Same params but derive a longer key (CAUTION: never use the same salt in # practice) dklen = 64 print("dklen =", dklen) dk = Pbe.kdf2(dklen, password, salt, count) print("dk =", Cnv.tohex(dk)) assert Cnv.tohex(dk) == \ "BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643C4B150DEF77511224479994567F2E9B4E3BD0DF7AEDA3022B1F26051D81505C794F8940C04DF1144" # Use different HMAC algorithms dklen = 24 dk = Pbe.kdf2(dklen, password, salt, count, prfalg=Pbe.PrfAlg.HMAC_SHA1) print("dk(HMAC-SHA-1) =", Cnv.tohex(dk)) assert Cnv.tohex(dk) == "BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643" dk = Pbe.kdf2(dklen, password, salt, count, prfalg=Pbe.PrfAlg.HMAC_SHA256) print("dk(HMAC-SHA-256) =", Cnv.tohex(dk)) assert Cnv.tohex(dk) == "97B5A91D35AF542324881315C4F849E327C4707D1BC9D322" dk = Pbe.kdf2(dklen, password, salt, count, prfalg=Pbe.PrfAlg.HMAC_SHA224) print("dk(HMAC-SHA-224) =", Cnv.tohex(dk)) assert Cnv.tohex(dk) == "10CFFEDFB13503519969151E466F587028E0720B387F9AEF" def test_pfx(): print("\nTESTING PFX (PKCS#12) FILE FUNCTIONS...") pfxfile = "bob1.pfx" certlist = "BobRSASignByCarl.cer" prikeyfile = "BobPrivRSAEncrypt.p8e" n = Pfx.make_file(pfxfile, certlist, prikeyfile, 'password', "Bob's ID") assert (0 == n) print("Created new PKCS#12 file:", pfxfile) print("Asn1.Type(" + pfxfile + ") -->", Asn1.type(pfxfile)) print("Check signature is valid against password...") isvalid = Pfx.sig_is_valid(pfxfile, 'password') print("isvalid=", isvalid) assert (isvalid) print("Use the wrong password...") isvalid = Pfx.sig_is_valid(pfxfile, 'passwordXXX') print("isvalid=", isvalid) assert (not isvalid) print("Extract private key file from Pfx...") newp8file = "NewBobPrivRsa.p8e" n = Rsa.get_privatekey_from_pfx(newp8file, pfxfile) assert (n > 0) print("Created new PKCS#8 file:", newp8file) print("Asn1.Type(" + newp8file + ") -->", Asn1.type(newp8file)) def test_pem(): print("\nTESTING PEM/BINARY FILE CONVERSIONS...") binfile = "smallca.cer" pemfile = "smallca.pem" print("Create a PEM-format CERTIFICATE file from binary file...") print("Binary file:", binfile) n = Pem.from_binfile(pemfile, binfile, "CERTIFICATE", Pem.EOL.UNIX) assert (0 == n) print("Created file:", pemfile) print("Check certificate thumbprints...") thumb_bin = X509.cert_thumb(binfile) thumb_pem = X509.cert_thumb(pemfile) print("X509.cert_thumb(" + binfile + ")=" + thumb_bin) print("X509.cert_thumb(" + pemfile + ")=" + thumb_pem) assert (thumb_bin == thumb_pem) print("Convert PEM to binary...") binfile2 = "smallca-copy.bin" n = Pem.to_binfile(binfile2, pemfile) assert (0 == n) print("Created file:", binfile2) print("Binary files should be identical...") hash_bin1 = Hash.hex_from_file(binfile) hash_bin2 = Hash.hex_from_file(binfile2) print("Hash.hex_from_file(" + binfile + ")=\t" + hash_bin1) print("Hash.hex_from_file(" + binfile2 + ")=\t" + hash_bin2) assert (hash_bin1 == hash_bin2) # Note that the *hash* of the PEM file is not the same as the hash of the binary, # but the X509.cert_thumb() is the same for both. def test_cms_envdata(): print("\nTESTING CMS ENV-DATA...") print("Creating an enveloped-data message for Bob and Carl, using file-->file mode") # Create a file mytext = 'This is some sample content.' myfile = "mycontent.txt" write_text_file(myfile, mytext) envdatafile = 'cms2bobandcarl.p7m' certlist = "BobRSASignByCarl.cer;CarlRSASelf.cer" n = Cms.make_envdata(envdatafile, myfile, certlist) print("Cms.make_envdata() returns " + str(n) + " (expected 2 = # of recipients)") assert (n > 0) _dump_and_print_asn1(envdatafile) print("Asn1.type('" + envdatafile + "')-->" + Asn1.type(envdatafile)) # Query this CMS object file fname = envdatafile query = "recipientIssuerName" res = Cms.query_envdata(envdatafile, query) print("Cms.query_envdata(" + fname + ", " + query + "):", res) query = "iv" res = Cms.query_envdata(envdatafile, query) print("Cms.query_envdata(" + fname + ", " + query + "):", res) print("Bob reads the message, outputting to a new file") outputfile = "bobsdata.txt" # Bob reads in his private key to a secure "internal" key string prikeystr = Rsa.read_private_key('BobPrivRSAEncrypt.p8e', 'password') n = Cms.read_envdata_to_file(outputfile, envdatafile, prikeystr) print("Cms.read_envdata_to_file() returns " + str(n) + " (expected 0)") assert (0 == n) _dump_file(outputfile) # Check we got the same as we started assert (read_text_file(outputfile) == mytext) print("\nDo the same but using string-->file mode...") print("DATA:", mytext) envdatafile = 'cms2bobandcarl1.p7m' n = Cms.make_envdata_from_string(envdatafile, mytext, certlist) print("Cms.make_envdata_from_string() returns " + str(n) + " (expected 2 = # of recipients)") assert (n > 0) print("Asn1.type('" + envdatafile + "')-->" + Asn1.type(envdatafile)) s = Cms.read_envdata_to_string(envdatafile, prikeystr) print(s) print("\nDo the same but using bytes-->file mode...") mydata = "Olá mundo".encode() print("DATA:", mydata) envdatafile = 'cms2bobandcarl2.p7m' n = Cms.make_envdata_from_bytes(envdatafile, mydata, certlist) print("Cms.make_envdata_from_string() returns " + str(n) + " (expected 2 = # of recipients)") assert (n > 0) print("Asn1.type('" + envdatafile + "')-->" + Asn1.type(envdatafile)) s = Cms.read_envdata_to_string(envdatafile, prikeystr) print("Cms.read_envdata_to_string=", s) b = Cms.read_envdata_to_bytes(envdatafile, prikeystr) print("Cms.read_envdata_to_bytes=", b) # clean up prikeystr = None def test_smime(): print("\nTESTING S/MIME...") print("First create an enveloped-data message for Bob and Carl...") # Create a file mytext = 'This is some sample content.' myfile = "mycontent.txt" write_text_file(myfile, mytext) envdatafile = 'cms2bobandcarl.p7m' certlist = "BobRSASignByCarl.cer;CarlRSASelf.cer" n = Cms.make_envdata(envdatafile, myfile, certlist) print("Cms.make_envdata() returns " + str(n) + " (expected 2 = # of recipients)") assert (n > 0) print("Asn1.type('" + envdatafile + "')-->" + Asn1.type(envdatafile)) print("Now wrap in S/MIME headers...") smimefile = 'cms2bobandcarl-smime-env.txt' n = Smime.wrap(smimefile, envdatafile) print("Smime.wrap() returns ", n, " (expected +ve)") _dump_file(smimefile) print("Query this S/MIME entity for info...") query = "content-type" r = Smime.query(smimefile, query) print("Smime.query('%s')=[%s]" % (query, r)) query = "smime-type" r = Smime.query(smimefile, query) print("Smime.query('%s')=[%s]" % (query, r)) print("Extract the original CMS env-data object in base64") extractedfile = 'cms2bobandcarl-extracted.txt' n = Smime.extract(extractedfile, smimefile, Smime.Opts.ENCODE_BASE64) print("Smime.extract() returns ", n, " (expected +ve)") assert n > 0 _dump_file(extractedfile) # Read base64 data into a string then analyze s = read_text_file(extractedfile) print("Asn1.type('" + extractedfile + "')-->" + Asn1.type(s)) def test_cms_sigdata(): print("\nTESTING CMS SIG-DATA...") print("Create an signed-data message from Alice, using file-->file mode") # Create a file myfile = "mycontent.txt" mytext = 'This is some sample content.' write_text_file(myfile, mytext) # Alice reads in her private key to a secure "internal" key string prikeystr = Rsa.read_private_key('AlicePrivRSASign.p8e', 'password') certlist = "AliceRSASignByCarl.cer" sigdatafile = 'cms_signedbyalice.p7m' n = Cms.make_sigdata(sigdatafile, myfile, certlist, prikeystr) print("Cms.make_sigdata() returns " + str(n) + " (expected 0)") assert (n == 0) print("Asn1.type('" + sigdatafile + "')-->" + Asn1.type(sigdatafile)) print("\nQuery this CMS object file...") fname = sigdatafile query = "signatureAlgorithm" res = Cms.query_sigdata(sigdatafile, query) print("Cms.query_sigdata(" + fname + ", " + query + "):", res) query = "CountOfSignerInfos" res = Cms.query_sigdata(sigdatafile, query) print("Cms.query_sigdata(" + fname + ", " + query + "):", res) print("\nRead in the content from the signed-data file...") outputfile = "alicesdata.txt" n = Cms.read_sigdata_to_file(outputfile, sigdatafile) print("Cms.read_sigdata_to_file() returns " + str(n) + " (expected 0)") assert (0 == n) _dump_file(outputfile) print("\nVerify the signature in the sigdata file...") isok = Cms.verify_sigdata(sigdatafile) print("Cms.verify_sigdata() returns", isok) assert isok print("\nUse string-->file mode...") print("DATA:", mytext) sigdatafile1 = 'cms_signedbyalice1.p7m' n = Cms.make_sigdata_from_string(sigdatafile1, mytext, certlist, prikeystr) print("Cms.make_sigdata_from_string() returns " + str(n) + " (expected 0)") assert (n == 0) print("Asn1.type('" + sigdatafile1 + "')-->" + Asn1.type(sigdatafile1)) s = Cms.read_sigdata_to_string(sigdatafile) print(s) print("signed-data files should be identical...") print("SHA1('" + sigdatafile + "')=\t" + Hash.hex_from_file(sigdatafile)) print("SHA1('" + sigdatafile1 + "')=\t" + Hash.hex_from_file(sigdatafile1)) assert (Hash.hex_from_file(sigdatafile) == Hash.hex_from_file(sigdatafile1)) print("\nUse bytes-->file mode...") mydata = "Olá mundo".encode() print("DATA:", mydata) sigdatafile1 = 'cms_signedbyalice2.p7m' n = Cms.make_sigdata_from_bytes(sigdatafile1, mydata, certlist, prikeystr) print("Cms.make_sigdata_from_bytes() returns " + str(n) + " (expected 0)") assert (n == 0) print("Asn1.type('" + sigdatafile1 + "')-->" + Asn1.type(sigdatafile1)) b = Cms.read_sigdata_to_bytes(sigdatafile1) print(b) print("\nMake a 'detached signature' signed-data object using the message digest of the content...") hexdigest = Hash.hex_from_string(mytext, Hash.Alg.SHA256) print("SHA256('%s')=%s" % (mytext, hexdigest)) sigdatafile_det = 'cms_signedbyalice_det.p7m' n = Cms.make_detached_sig( sigdatafile_det, hexdigest, certlist, prikeystr, sigalg=Cms.SigAlg.RSA_PSS_SHA256) print("Cms.make_detached_sig() returns " + str(n) + " (expected 0)") assert (n == 0) print("Verify the signature in the detached sigdata file against the digest value...") print("First try verifying against the eContent (which is missing)...") try: isok = Cms.verify_sigdata(sigdatafile_det) except PKIError as e: print("Woops! PKIError:", e) print("Now pass the digest we expect...") isok = Cms.verify_sigdata(sigdatafile_det, hexdigest=hexdigest) print("Cms.verify_sigdata(file,hexdigest) returns", isok) assert isok print("Query the signature and digest algorithms used in our signed-data object (expecting rsaPSS/sha256)") query = "signatureAlgorithm" s = Cms.query_sigdata(sigdatafile_det, query) print(query + "=[" + s + "]") query = "digestAlgorithm" s = Cms.query_sigdata(sigdatafile_det, query) print(query + "=[" + s + "]") print("\nCreate signed-data from a pre-computed signature value...") # Example 4.2 from [SMIME-EX] # Data to be signed 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") data = Cnv.fromhex(datahex) print("DATA:", Cnv.tohex(data)) # Signature value generated by smartcard using rsa-sha1 (our default) 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") sig = Cnv.fromhex(sighex) print("SIG:", Cnv.tohex(sig)) sigdatafile2 = 'cms_signedbyalice2.p7m' n = Cms.make_sigdata_from_sigvalue(sigdatafile2, sig, data, certlist) print("Cms.make_sigdata_from_sigvalue() returns " + str(n) + " (expected 0)") # Compare resulting file to expected `4.2.bin` print("SHA1(outputfile)=", Hash.hex_from_file(sigdatafile2)) print("SHA1('4.2.bin' )=", Hash.hex_from_file('4.2.bin')) assert (Hash.hex_from_file(sigdatafile2) == Hash.hex_from_file('4.2.bin')) def test_cms_comprdata(): print("\nTESTING CMS COMPRESSED-DATA...") print("Creating an compressed-data object...") basefile = "sonnets.txt" compfile = 'sonnets.p7z' print("INPUT:", basefile, os.path.getsize(basefile), "bytes") n = Cms.make_comprdata(compfile, basefile) print("Cms.make_comprdata() returns " + str(n) + " (expected 0)") assert (n == 0) print("COMPR:", compfile, os.path.getsize(compfile), "bytes") print("Asn1.type('" + compfile + "')-->" + Asn1.type(compfile)) print("Reading an compressed-data object...") chkfile = "sonnets-unCompr.txt" n = Cms.read_comprdata(chkfile, compfile) print("Cms.read_comprdata() returns " + str(n) + " (expected +ve)") assert (n > 0) print("UNCPR:", chkfile, os.path.getsize(chkfile), "bytes") # Compare base file to final uncompressed print("SHA1(basefile)=", Hash.hex_from_file(basefile)) print("SHA1(uncmfile)=", Hash.hex_from_file(chkfile)) assert (Hash.hex_from_file(basefile) == Hash.hex_from_file(chkfile)) print("Read with no-inflate option...") chkfile = "sonnets-noinflate.txt" n = Cms.read_comprdata(chkfile, compfile, Cms.ComprDataOpts.NO_INFLATE) assert (n > 0) print("NOINF:", chkfile, os.path.getsize(chkfile), "bytes") def test_sig_rsa(): print("\nTESTING SIG FUNCTIONS USING Rsa...") print("Sign the string 'abc' using Alice's private RSA key...") keyfile = "AlicePrivRSASign.p8e" password = "password" # !!! alg = Sig.Alg.RSA_SHA1 # Sign data data = b"abc" sig = Sig.sign_data(data, keyfile, password, alg) print("sign_data: ", sig) # Sign the digest value of the data digest = Cnv.fromhex("a9993e364706816aba3e25717850c26c9cd0d89d") sig1 = Sig.sign_digest(digest, keyfile, password, alg) print("sign_digest:", sig1) assert (sig1 == sig) # Encode the signature differently print("Different encodings...") sig2 = Sig.sign_data(data, keyfile, password, alg, encoding=Sig.Encoding.BASE64URL) print("sign_data: ", sig2) sig3 = Sig.sign_data(data, keyfile, password, alg, encoding=Sig.Encoding.HEX) print("sign_data: ", sig3) print("Verify the signature over the data") cert = "AliceRSASignByCarl.cer" isok = Sig.data_is_verified(sig, data, cert, alg) print("Sig.data_is_verified() returns", isok) assert (isok) print("Use the wrong cert...") wrongcert = "BobRSASignByCarl.cer" isok = Sig.data_is_verified(sig, data, wrongcert, alg) print("Sig.data_is_verified() returns", isok, "(expected False)") assert (not isok) print("Verify the signature over the message digest value") isok = Sig.digest_is_verified(sig, digest, cert, alg) print("Sig.digest_is_verified() returns", isok) assert (isok) print("Sign a file containing 'abc' using Alice's private RSA key...") datafile = "abc.txt" write_text_file(datafile, 'abc') sig = Sig.sign_file(datafile, keyfile, password, alg) print("sign_file: ", sig) # Verify it isok = Sig.file_is_verified(sig, datafile, cert, alg) print("Sig.file_is_verified() returns", isok) assert (isok) def test_sig_ecc(): print("\nTESTING SIG FUNCTIONS USING Ecc...") # Ref: [RFC6979] "Deterministic Usage of the DSA and ECDSA" # A.2.3. ECDSA, 192 Bits (Prime Field) # Read in private key using (hex,curvename) form keyhex = "6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4" curvename = Ecc.CurveName.P_192 print("KEYHEX:", keyhex) print("CURVE:", curvename) keystr = Ecc.read_key_by_curve(keyhex, curvename) print("NBITS=", Ecc.query_key(keystr, "keyBits")) # Sign data alg = Sig.Alg.ECDSA_SHA1 data = b"test" sig = Sig.sign_data(data, keystr, "", alg, opts=Sig.Opts.DETERMINISTIC, encoding=Sig.Encoding.HEX) print("SIG:", sig) print("Verify the signature over the data...") # Derive the EC public key from the private key pubkeystr = Ecc.publickey_from_private(keystr) # And use it to verify the signature isok = Sig.data_is_verified(sig, data, pubkeystr, alg) print("Sig.data_is_verified() returns", isok) assert (isok) def test_x509_ecc(): print("\nTESTING X509 CERT FUNCTIONS USING Ecc...") # New in v11.3 # Use an EC key we made earlier cakeyfile = 'ecprivkey.p8' # in pkiPythonTestFiles.zip password = 'password' cacert = 'myca_Ecc.cer' dn = "O=My Company;OU=My Org;E=me@org.com;L=Perth;ST=WA;C=AU;CN=Test Example" extns = "serialNumber=#x00F3ED4B1754C18AA5;notBefore=2017-09-19T08:09:06Z;notAfter=2027-09-17T08:09:06Z" # Make a new self-signed certificate... # (just for testing purposes we use the deterministic method for ECDSA, so we always get the same result) print("About to create new certificate:", cacert) r = X509.make_cert_self(cacert, cakeyfile, password, 0, 0, dn, extns, sigalg=X509.SigAlg.ECDSA_SHA256, opts=X509.Opts.VERSION1 | X509.Opts.DETERMINISTIC) assert (0 == r) # Query this new cert certname = cacert print("serialNumber:", X509.query_cert(certname, "serialNumber")) print("issuerName:", X509.query_cert(certname, "issuerName")) print("signatureAlgorithm:", X509.query_cert(certname, "signatureAlgorithm")) print("hashAlgorithm:", X509.query_cert(certname, "hashAlgorithm")) print("subjectPublicKeyAlgorithm:", X509.query_cert(certname, "subjectPublicKeyAlgorithm")) # Dump its details (new fn in v11.3) dump = X509.text_dump_tostring(certname) print("FILE:", certname) print(dump) # Verify this new certificate using itself isok = X509.cert_is_verified(cacert, cacert) print("X509.cert_is_verified({0}, {1}) returns {2}".format(cacert, cacert, isok)) assert (isok) # Read in the EC public key value from the X.509 certificate (new in v11.3) # (just to show we can!) pubkey = Ecc.read_public_key(cacert) assert (len(pubkey) > 0) print("Public key size:", Ecc.query_key(pubkey, "keyBits"), "bits") # Generate a new EC key pair for an end user userprikeyfile = 'myuser_prikey.p8' userpubkeyfile = 'myuser_pubkey.pub' r = Ecc.make_keys(userpubkeyfile, userprikeyfile, Ecc.CurveName.P_224, "password") print("Created new user key pair:", userprikeyfile, "&", userpubkeyfile) assert (0 == r) # Create a new end-user certificate using EC key we just made usercert = 'myuser_Ecc.cer' dn = "CN=Olá mundo;OU=Using ECC_P224" print("About to create new certificate:", usercert) r = X509.make_cert(usercert, cacert, userpubkeyfile, cakeyfile, password, 0x224, 5, dn, sigalg=X509.SigAlg.ECDSA_SHA224, opts=X509.Opts.UTF8) assert (0 == r) # Query this new cert certname = usercert print("serialNumber:", X509.query_cert(certname, "serialNumber")) print("issuerName:", X509.query_cert(certname, "issuerName")) # User name is encoded in UTF-8: default is to display in hex print("subjectName:", X509.query_cert(certname, "subjectName")) # Display as latin-1 string properly in IDE # -- No longer an issue with Python 3!! # print("subjectName:", X509.query_cert(certname, "subjectName", X509.Opts.LATIN1).decode('iso-8859-1')) print("signatureAlgorithm:", X509.query_cert(certname, "signatureAlgorithm")) print("hashAlgorithm:", X509.query_cert(certname, "hashAlgorithm")) print("subjectPublicKeyAlgorithm:", X509.query_cert(certname, "subjectPublicKeyAlgorithm")) # Verify this new certificate using CA's cert isok = X509.cert_is_verified(usercert, cacert) print("X509.cert_is_verified({0}, {1}) returns {2}".format(usercert, cacert, isok)) assert (isok) # Verify the path certlist = usercert + ";" + cacert isok = X509.cert_path_is_valid(certlist) print("X509.cert_path_is_valid({0}) returns {1}".format(certlist, isok)) assert (isok) def test_asn1_dumptostring(): print("\nTESTING ASN.1 TEXT DUMP TO STRING...") # New in v11.3 fname = r"C:\!Data\Crypto\X509\x509cat.Asn1.dat" s = Asn1.text_dump_tostring(fname) # File is large! Just dump the first part print(s[:378]) def test_compress(): print("\nTEST ZLIB COMPRESSION....") message = b"hello, hello, hello. This is a 'hello world' message for the world, repeat, for the world." print("MSG:", message) comprdata = Compr.compress(message) print("Compressed = (0x)" + Cnv.tohex(comprdata)) print("Compressed %d bytes to %d" % (len(message), len(comprdata))) # Now uncompresss (inflate) uncomprdata = Compr.uncompress(comprdata) print("Uncompressed = '" + str(uncomprdata) + "'") assert (uncomprdata == message) def test_aead(): print("\nTEST AES-GCM AUTHENTICATED ENCRYPTION....") # GCM Test Case #03 (AES-128) key = Cnv.fromhex("feffe9928665731c6d6a8f9467308308") iv = Cnv.fromhex("cafebabefacedbaddecaf888") pt = Cnv.fromhex( "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255") okhex = "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f59854d5c2af327cd64a62cf35abd2ba6fab4" print("KY =", Cnv.tohex(key)) print("IV =", Cnv.tohex(iv)) print("PT =", Cnv.tohex(pt)) # Do the business ct = Cipher.encrypt_aead(pt, key, iv, Cipher.AeadAlg.AES_128_GCM) print("CT =", Cnv.tohex(ct)) print("OK =", okhex) assert (okhex.lower() == Cnv.tohex(ct).lower()) # Decrypt, passing IV as an argument dt = Cipher.decrypt_aead(ct, key, iv, Cipher.AeadAlg.AES_128_GCM) print("DT =", Cnv.tohex(dt)) assert (Cnv.tohex(pt) == Cnv.tohex(dt)) print("Repeat but prepend IV to output..") ct = Cipher.encrypt_aead(pt, key, iv, Cipher.AeadAlg.AES_128_GCM, opts=Cipher.Opts.PREFIXIV) print("IV|CT =", Cnv.tohex(ct)) # Decrypt, IV is prepended to ciphertext dt = Cipher.decrypt_aead(ct, key, None, Cipher.AeadAlg.AES_128_GCM, opts=Cipher.Opts.PREFIXIV) print("DT =", Cnv.tohex(dt)) assert (Cnv.tohex(pt) == Cnv.tohex(dt)) def test_aead_chapoly(): print("\nTEST AEAD CHACHA20POLY1305....") print("RFC8439 ChaCha20_Poly1305 Sunscreen test with AAD") key = Cnv.fromhex("808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F") iv = Cnv.fromhex("070000004041424344454647") aad = Cnv.fromhex("50515253C0C1C2C3C4C5C6C7") pt = Cnv.fromhex( "4C616469657320616E642047656E746C656D656E206F662074686520636C617373206F66202739393A204966204920636F756C64206F6666657220796F75206F6E6C79206F6E652074697020666F7220746865206675747572652C2073756E73637265656E20776F756C642062652069742E") okhex = "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd0600691" print("KY =", Cnv.tohex(key)) print("IV =", Cnv.tohex(iv)) print("AD =", Cnv.tohex(aad)) print("PT =", Cnv.tohex(pt)) # Do the business ct = Cipher.encrypt_aead(pt, key, iv, Cipher.AeadAlg.CHACHA20_POLY1305, aad=aad) print("CT =", Cnv.tohex(ct)) print("OK =", okhex) assert (okhex.lower() == Cnv.tohex(ct).lower()) dt = Cipher.decrypt_aead(ct, key, iv, Cipher.AeadAlg.CHACHA20_POLY1305, aad=aad) print("DT =", Cnv.tohex(dt)) print(f"DT ='{dt.decode()}'") assert (Cnv.tohex(pt) == Cnv.tohex(dt)) # Again but prefix the IV to the ciphertext print(f"Prefix the IV ({Cnv.tohex(iv)}) to the CT...") ct = Cipher.encrypt_aead(pt, key, iv, Cipher.AeadAlg.CHACHA20_POLY1305, aad=aad, opts=Cipher.Opts.PREFIXIV) print("CT =", Cnv.tohex(ct)) dt = Cipher.decrypt_aead(ct, key, iv, Cipher.AeadAlg.CHACHA20_POLY1305, aad=aad, opts=Cipher.Opts.PREFIXIV) print("DT =", Cnv.tohex(dt)) assert (Cnv.tohex(pt) == Cnv.tohex(dt)) def test_readcertstring(): print("\nTEST READ CERT STRING FROM P7CHAIN AND Pfx....") # Input is a P7 chain file in PEM format # bob.p7b (contains 2 X.509 certs: BobRSA and CarlRSA) strp7 = """-----BEGIN PKCS7----- MIIERQYJKoZIhvcNAQcCoIIENjCCBDICAQExADALBgkqhkiG9w0BBwGgggQaMIICJzCCAZCgAwIB AgIQRjRrx4AAVrwR024uzV1x0DANBgkqhkiG9w0BAQUFADASMRAwDgYDVQQDEwdDYXJsUlNBMB4X DTk5MDkxOTAxMDkwMloXDTM5MTIzMTIzNTk1OVowETEPMA0GA1UEAxMGQm9iUlNBMIGfMA0GCSqG SIb3DQEBAQUAA4GNADCBiQKBgQCp4WeYPznVX/Kgk0FepnmJhcg1XZqRW/sdAdoZcCYXD72lItA1 hW16mGYUQVzPt7cIOwnJkbgZaTdt+WUee9mpMySjfzu7r0YBhjY0MssHA1lS/IWLMQS4zBgIFEjm Txz7XWDE4FwfU9N/U9hpAfEF+Hpw0b6Dxl84zxwsqmqn6wIDAQABo38wfTAMBgNVHRMBAf8EAjAA MA4GA1UdDwEB/wQEAwIFIDAfBgNVHSMEGDAWgBTp4JAnrHggeprTTPJCN04irp44uzAdBgNVHQ4E FgQU6PS4Z9izlqQq8xGqKdOVWoYWtCQwHQYDVR0RBBYwFIESQm9iUlNBQGV4YW1wbGUuY29tMA0G CSqGSIb3DQEBBQUAA4GBAHuOZsXxED8QIEyIcat7QGshM/pKld6dDltrlCEFwPLhfirNnJOIh/uL t359QWHh5NZt+eIEVWFFvGQnRMChvVl52R1kPCHWRbBdaDOS6qzxV+WBfZjmNZGjOd539OgcOync f1EHl/M28FAK3Zvetl44ESv7V+qJba3JiNiPzyvTMIIB6zCCAVSgAwIBAgIQRjRrx4AAVrwR024u n/JQIDANBgkqhkiG9w0BAQUFADASMRAwDgYDVQQDEwdDYXJsUlNBMB4XDTk5MDgxODA3MDAwMFoX DTM5MTIzMTIzNTk1OVowEjEQMA4GA1UEAxMHQ2FybFJTQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw gYkCgYEA5Ev/GLgkV/R3/25ze5NxXLwzGpKSciPYQUbQzRE6BLOOr4KdvVEeF3rydiwrhjmnvdeN GlPs5ADV6OyiNrHt4lDiMgmKP5+ZJY+4Tqu5fdWWZdoWoMW+Dq5EW+9e9Kcpy4LdrETpqpOUKQ74 GNbIV17ydsTyEWA4uRs8HZfJavECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E BAMCAYYwHQYDVR0OBBYEFOngkCeseCB6mtNM8kI3TiKunji7MA0GCSqGSIb3DQEBBQUAA4GBALee 1ATT7Snk/4mJFS5M2wzwSA8yYe7EBOwSXS3/D2RZfgrD7Rj941ZAN6cHtfA4EmFQ7e/dP+MLuGGl pJs85p6cVJq2ldbabDu1LUU1nUkBdvq5uTH5+WsSU6D1FGCbfco+8lNrsDdvreZ019v6WuoUQWNd zb7IDsHaao1TNBgCMQA= -----END PKCS7-----""" # Get count of certs in P7 chain ncerts = X509.get_cert_count_from_p7(strp7) print("ncerts in P7 chain =", ncerts) for i in range(1, ncerts + 1): certstr = X509.read_cert_string_from_p7chain(strp7, i) print("CER:", certstr[:80], "...", certstr[-10:]) subjectname = X509.query_cert(certstr, "subjectName") print("subjectName:", subjectname) # Input is a PFX file in PEM format # bob.pfx (password="password") strpfx = """-----BEGIN PKCS12----- MIIGhAIBAzCCBkoGCSqGSIb3DQEHAaCCBjsEggY3MIIGMzCCAv8GCSqGSIb3DQEHBqCCAvAwggLsAgEAMIIC5QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIawU AVTFvAiECAggAgIICuNwEuFcRnZamZyMyIn+vH+wC5BVUtZAWNrlIqToezF7cYqt/18+HXB/46nllz+qUD3Dv9rS78MnPeAM47afFRTricHsiOpE+2eXf32lxduoF5+ CLS3S7TAhRUMp2Fh18LlukzK9lY67BGfU9Y3yCukTmwVXqe49dkj8y9JjVJhXnoc2c7eOk3o5RjXHFsAMHwirqdsESHstrDZYLMVGw5HnAamY7zQd8WUpIweAFaEDLJ fyzqY1/LTL/txvZ9VQ/B/36HKyEpoIvuH6iOCBkebpJwWSkkffuVFbUfMLguMztL/sf+jE2NiuljSBJ9pTNsZziZWERb6CxZH0a2xkkBTciXM5Dl5efWL0GmBg+aJSI yh+Gw5W8Q7gmnH6H9myszvW9uYv/epwCbIpHd0dRHPbL3fR4KGhFexq24tAG86tDqPKb6H6n0lSA+Oq46SwZ00xIFpVcFaO/8yVqf6+JRDGoZ55aAZF6OCi7R1GvI+6 pzz37pvP7SWfqVSuXCTNQq9uKw97SH5YftQ9hkELQ4vHCjFh4UJSBUCZgDtqR1uB/+44H5UpP8KvbETaOFJszMxsqXBMqc1uEODSNg+EHEx+yg7Bx1CcNrm+6rtThC4 9+ow18HDMxbn3lAw1ooblANvSzR4YTt68N/4dtwROOdXjwKzyg03qWK2sJaiH5LzbB5MMmrdAChb9dLoRKBN2LREob7KRKEs6v51IW1yq4UCwSmpP+RbchZwIoKVXx/ MYKjVqzGfZAgBRpXEq/KH/8R+ttFPKdab2GAEjd7hIOmetp5einQmK4C7JYE6Uyabf1IImtVhBw2dGU3GiM2zSIGqCx3bmYETZheMTAV9MMVUYe8gQeEpbXM4GAnwX0 wpS0aYapzGeA/62X2nFh21eRHVzUcf0miXVvyOy6a1vj6O6N5F1jVaCV3jCCAywGCSqGSIb3DQEHAaCCAx0EggMZMIIDFTCCAxEGCyqGSIb3DQEMCgECoIICpjCCAqI wHAYKKoZIhvcNAQwBAzAOBAjw/dx4SlLcWwICCAAEggKALm91I8gYuPpRTCSn5pN4OQBLbI6jSW+9FGeNYvOy/+Pt3Oq0i15ZXZZez7dP8rdb0tmTCSZwVPIwtJRKxY UNaTppUTWZhXhnmeTMtSZpFuKmo6UhW8lGUcg45sO5UKUtdH0/UgewaSUfV4L06vp4j7Fugwbp666seJJ/9vQwMAxoqj0blxNNmASAcW7yj/lA2/p4KuGlnGkv4MSW5 ViH7T24VeFXTzyFFR7UR1Nw9Blr5jdr7b2rZSdTj0GeHZ/L3FksFWJocl8PEEL4ZdVscbvO+l7vtbeBz0y9TDr/HUwt2tfqXgjckVVoJhmsczJXrG5Ai+brKnGQ7R5u IpIsqd9O6EpG68VMMGA5iSKsLYtibieqom8mRO00sFiQharxONEdveY+3O98nG6xzHlaBdNbxVo38Y+4LK6Gc81dUWYwss3ajdiJWe0+TYQjMPF72eWctcQAoTxITpd /j6rD7EmvLVyPIR46L4w6Gb/uz5G1T1UiLoh9luM1nRKKICyo2XllZDNO0msaub7DH1xzJzEy2OT9cwChqYfKKeWEE2BWL699fmq5RMCbIQVtE2bJDP8obu9j6HLskC iZcJm6nC7IKS1pQ2BA/JJVKxC8ADuLOAOdicWquDd8MWL5a9HpXd5TtUlfiRecTw8IRozTLaoDVlhaYNGPzwkjL9zZ+Up5Uy6HHXMDb0aD0fgvMqdAspB1+Xlt2RgP6 CnEH2hwQqGFoA8TtijeS+DtdMy8BxJ7g1fiEH0+4UISl1vymjPI1MJCI1VlFLvpjZvKHluwjgp1SHk3tFRJLJ8a/eApvmscKXSlxcYz+5Bv8dxPGdhO/KOLQS7XZ4a8 VSg977WS1jFYMCMGCSqGSIb3DQEJFTEWBBRj8EbS3XBC5R/cJqUR73yB6mItizAxBgkqhkiG9w0BCRQxJB4iAEIAbwBiACcAcwAgAGYAcgBpAGUAbgBkAGwAeQAgAEk ARDAxMCEwCQYFKw4DAhoFAAQUaHSMUJ415FfKGv3cZpwloKDmqgYECAreM3EkHVjCAgIIAA== -----END PKCS12-----""" certstr = X509.read_cert_string_from_pfx(strpfx, "password") print("CER:", certstr[:80], "...", certstr[-10:]) subjectname = X509.query_cert(certstr, "subjectName") print("subjectName:", subjectname) # NEW IN [v12.3] def test_cipher_prefix(): print("\nENCRYPT WITH PREFIXED IV xmlenc#aes128-cbc...") plain = "<encryptme>hello world</encryptme>" key = Cnv.fromhex("6162636465666768696A6B6C6D6E6F70") iv = Rng.bytestring(Cipher.blockbytes(Cipher.Alg.AES128)) print("PT='", plain, "'", sep='') pt = plain.encode() print("HEX(PT)=", Cnv.tohex(pt), sep='') print("KEY=", Cnv.tohex(key), sep='') print("IV=", Cnv.tohex(iv), sep='') # Encrypt and prepend IV before ciphertext ct = Cipher.encrypt(pt, key, iv, "aes128/cbc", opts=Cipher.Opts.PREFIXIV) print("IV|CT=", Cnv.tohex(ct), sep='') # Encode in base64 ciphervalue = Cnv.tobase64(ct) # Output in XML (NB will be different each time) print("<CipherValue>{0}</CipherValue>".format(ciphervalue)) # --------------- # PART 2 - decrypt print("DECRYPTING...") # Decode from base64 ct = Cnv.frombase64(ciphervalue) print("IV|CT=", Cnv.tohex(ct), sep='') # Decrypt. Note that IV is not specified when decrypting with a prefixed IV dt = Cipher.decrypt(ct, key, None, "aes128/cbc", opts=Cipher.Opts.PREFIXIV) # Display plaintext print("DT=", Cnv.tohex(dt), sep='') print("DT='", dt.decode(), "'", sep='') assert (Cnv.tohex(pt) == Cnv.tohex(dt)) def test_x509_makecert_emptydn(): print("\nMAKE CERT WITH EMPTY DN:") certname = "AliceRSA-emptyDN.cer" issuercert = "CarlRSASelf.cer" prikeyfile = "CarlPrivRSASign.p8e" password = "password" subjectpubkeyfile = "AlicePubRsa.pub" dn = "$" # special flag for empty DN extns = "iPAddress=192.168.15.1" # at least one field for subject alt name is required keyusage = X509.KeyUsageFlags.DIGITALSIGNATURE | X509.KeyUsageFlags.NONREPUDIATION # Create a new certificate for Alice signed by Carl valid for 2 years signed using RSA-SHA-256 # Subject's distinguished name will be empty, Subject alternative name will be automatically marked CRITICAL (denoted "[!]" in dump) r = X509.make_cert(certname, issuercert, subjectpubkeyfile, prikeyfile, password, 0x1001, 2, dn, extns=extns, sigalg=X509.SigAlg.RSA_SHA256, keyusage=keyusage) assert (0 == r) print("Created new X509 file '{0}'".format(certname)) _dump_and_print_x509(certname) def test_x509_certrequest_emptydn_extkeyusage(): print("\nMAKE CERTIFICATE SIGNING REQUEST WITH EMPTY DN AND EXTENDED KEY USAGE:") csrfile = "req_emptydn_extkeyusage.p10" subjectprikeyfile = "AlicePrivRSASign.p8e" password = "password" dn = "$" # special flag for empty DN # Use extensions parameter to add alt subject name and extended key usage flags extns = "iPAddress=192.168.15.1;extKeyUsage=serverAuth,clientAuth,emailProtection,critical;" # Create a CSR for Alice # Subject's distinguished name is empty, extKeyUsage is marked CRITICAL (denoted "[!]" in dump) r = X509.cert_request(csrfile, subjectprikeyfile, password, dn, extns=extns, sigalg=X509.SigAlg.RSA_SHA256) assert (0 == r) print("Created certificate request '{0}'".format(csrfile)) _dump_and_print_x509(csrfile) certfile = "certfromcsr_emptydn_extkeyusage.cer" issuercert = "CarlRSASelf.cer" issuerprikeyfile = "CarlPrivRSASign.p8e" issuerpassword = "password" # Now use this PKCS#10 CSR to create an end-user X.509 certificate for Alice signed by Carl valid for 4 years # Pass the csrfile as the subject public key file argument and leave the DN argument empty as a flag to use a CSR instead r = X509.make_cert(certfile, issuercert, csrfile, issuerprikeyfile, issuerpassword, 0x10b, 4, "", sigalg=X509.SigAlg.RSA_SHA256) assert (0 == r) print("Created end-user X.509 certificate '{0}'".format(certfile)) _dump_and_print_x509(certfile) # Query the new certificate query = "subjectName" # empty '' s = X509.query_cert(certfile, query) print("Query {0}='{1}'".format(query, s)) query = "subjectAltName" s = X509.query_cert(certfile, query) print("Query {0}='{1}'".format(query, s)) query = "extKeyUsageString" s = X509.query_cert(certfile, query) print("Query {0}='{1}'".format(query, s)) def test_read_x509_from_pfx_3des(): print("\nREAD IN CERT AS A STRING FROM PFX FILE USING 3DES ENCRYPTION...") # PFX file from draft-dkg-lamps-samples-02 with cert encrypted using "stronger" 3DES # Ref: IETF LAMPS WG https:#gitlab.com/dkg/lamps-samples pfxfile = "bob-lamps.p12" password = 'bob' print("FILE:", pfxfile) certstr = X509.read_cert_string_from_pfx(pfxfile, password) assert (len(certstr) > 0) print(certstr[:30], "...", certstr[-30:]) print("subjectName:", X509.query_cert(certstr, "subjectName")) print("Asn1.type=", Asn1.type(certstr)) def test_pfx_makefile_3des(): print("\nCREATE A NEW PFX FILE USING 3DES TO ENCRYPT THE CERT:") pfxfile = "bob-3des.pfx" prikeyfile = "BobPrivRSAEncrypt.p8e" certfile = "BobRSASignByCarl.cer" password = "password" # Use StrongCert option to encrypt cert using "stronger" 3DES instead of weak default 40-bit RC2. r = Pfx.make_file(pfxfile, certfile, prikeyfile, password, "Old Bob", Pfx.Opts.STRONG_CERT) assert (0 == r) print("Created PKCS#12 key store file '{0}'".format(pfxfile)) # Now dump the ASN.1 # Note that certificate (in encryptedData) is encrypted with "pbeWithSHAAnd3-KeyTripleDES-CBC" # (see line 275 (approx) of output) _dump_and_print_asn1(pfxfile) def test_rng_guid(): print("\nTEST RANDOM GUID STRINGS...") for x in range(0, 5): guid = Rng.guid() print(guid) def test_sig_signdata_ed25519(): print("\nSIGN DATA USING Ed25519...") # Ref: [RFC8032] https://tools.ietf.org/html/rfc8032#section-7.1 # -----TEST SHA(abc) # Read in private key from hex (NB need explicitly to identify as a private key) prikey = Ecc.read_key_by_curve("833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42", Ecc.CurveName.ED25519, Ecc.KeyType.PRIVATE_KEY) print(f"Private key has {Ecc.query_key(prikey, 'keyBits')} bits") print(f"ALGORITHM: {Ecc.query_key(prikey, 'curveName')}") # Message is the 64-byte SHA-512 hash of "abc" message = Cnv.fromhex( "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f") # Compute signature value in hex sig = Sig.sign_data(message, prikey, "", Sig.Alg.ED25519, encoding=Sig.Encoding.HEX) print(f"SIGNATURE:\n{sig}") # Check against known correct result sigok = "dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704" assert (sig == sigok) # Now verify using public key pubkey = Ecc.read_key_by_curve("ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", Ecc.CurveName.ED25519, Ecc.KeyType.PUBLIC_KEY) print(f"Public key has {Ecc.query_key(pubkey, 'keyBits')} bits") ok = Sig.data_is_verified(sig, message, pubkey, Sig.Alg.ED25519) print(f"Sig.data_is_verified() returns {ok}") assert (ok) def test_sig_signdata_ed448(): print("\nSIGN DATA USING Ed448...") # Ref: [RFC8032] https://tools.ietf.org/html/rfc8032 # -----Blank # Read in private key from hex (NB need explicitly to identify as a private key) prikey = Ecc.read_key_by_curve( "6c82a562cb808d10d632be89c8513ebf 6c929f34ddfa8c9f63c9960ef6e348a3 528c8a3fcc2f044e39a3fc5b94492f8f 032e7549a20098f95b", Ecc.CurveName.ED448, Ecc.KeyType.PRIVATE_KEY) print(f"Private key has {Ecc.query_key(prikey, 'keyBits')} bits") print(f"ALGORITHM: {Ecc.query_key(prikey, 'curveName')}") # Message is the empty string message = Cnv.fromhex("") # Compute signature value in hex sig = Sig.sign_data(message, prikey, "", Sig.Alg.ED448, encoding=Sig.Encoding.HEX) print(f"SIGNATURE:\n{sig}") # Check against known correct result sigok = "533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980ff0d2028d4b18a9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4dbb61149f05a7363268c71d95808ff2e652600" assert (sig == sigok) # Now verify using public key pubkey = Ecc.read_key_by_curve( "5fd7449b59b461fd2ce787ec616ad46a 1da1342485a70e1f8a0ea75d80e96778 edf124769b46c7061bd6783df1e50f6c d1fa1abeafe8256180", Ecc.CurveName.ED448, Ecc.KeyType.PUBLIC_KEY) print(f"Public key has {Ecc.query_key(pubkey, 'keyBits')} bits") ok = Sig.data_is_verified(sig, message, pubkey, Sig.Alg.ED448) print(f"Sig.data_is_verified() returns {ok}") assert (ok) def test_cms_makesigdata_ed25519(): print("\nCREATE A CMS SIGNED-DATA OBJECT USING Ed25519...") outfile = "SignedData_Ed25519.p7m" infile = "excontent.txt" certfile = "Ed25519-ietf-selfsigned.cer" # Self-signed cert created using private Ed25519 key in [RFC8410] prikeyfile = "edwards-ietf-ex.p8" # No password, from [RFC8410] # Read in private key to internal key string (no password) prikeystr = Ecc.read_private_key(prikeyfile, "") print(prikeystr) # Create the signed-data object using Ed25519 with signed attributes incl Algorithm Protection opts = Cms.SigDataOpts.INCLUDE_ATTRS or Cms.SigDataOpts.ADD_ALGPROTECT r = Cms.make_sigdata(outfile, infile, certfile, prikeystr, Cms.SigAlg.ED25519, opts=opts) assert 0 == r print(f"Created file '{outfile}'") # Show ASN.1 dump of file print(f"SIGNED-DATA:\n{Asn1.text_dump_tostring(outfile)}") # Query the signed-data object query = "digestAlgorithm" s = Cms.query_sigdata(outfile, query) print(f"Cms.query_sigdata({query})={s}") query = "signatureAlgorithm" s = Cms.query_sigdata(outfile, query) print(f"Cms.query_sigdata({query})={s}") query = "HASsignedAttributes" s = Cms.query_sigdata(outfile, query) print(f"Cms.query_sigdata({query})={s}") query = "DigestOfSignedAttrs" s = Cms.query_sigdata(outfile, query) print(f"Cms.query_sigdata({query})={s}") # Verify the signed-data r = Cms.verify_sigdata(outfile) print(f"Cms.verify_sigdata returns {r} (expecting True)") assert r # Read the signed-data content s = Cms.read_sigdata_to_string(outfile) print(f"signed-data content='{s}'") assert len(s) > 0 def test_cms_makesigdata_ed448(): print("\nCREATE A CMS SIGNED-DATA OBJECT USING Ed448...") outfile = "SignedData_Ed448.p7m" infile = "excontent.txt" certfile = "Ed448-selfsigned.cer" # Self-signed cert using Ed448 prikeyfile = "edkey448.p8" # No password # Read in private key to internal key string (no password) prikeystr = Ecc.read_private_key(prikeyfile, "") print(prikeystr) # Create the signed-data object using EdDSA with signed attributes incl Algorithm Protection opts = Cms.SigDataOpts.INCLUDE_ATTRS | Cms.SigDataOpts.ADD_ALGPROTECT r = Cms.make_sigdata(outfile, infile, certfile, prikeystr, Cms.SigAlg.ED448, opts=opts) assert 0 == r print(f"Created file '{outfile}'") # Show ASN.1 dump of file print(f"SIGNED-DATA:\n{Asn1.text_dump_tostring(outfile)}") # Query the signed-data object query = "digestAlgorithm" s = Cms.query_sigdata(outfile, query) print(f"Cms.query_sigdata({query})={s}") query = "signatureAlgorithm" s = Cms.query_sigdata(outfile, query) print(f"Cms.query_sigdata({query})={s}") query = "HASsignedAttributes" s = Cms.query_sigdata(outfile, query) print(f"Cms.query_sigdata({query})={s}") query = "HASalgorithmProtection" s = Cms.query_sigdata(outfile, query) print(f"Cms.query_sigdata({query})={s}") # Verify the signed-data r = Cms.verify_sigdata(outfile) print(f"Cms.verify_sigdata returns {r} (expecting True)") assert r # Read the signed-data content s = Cms.read_sigdata_to_string(outfile) print(f"signed-data content='{s}'") assert len(s) > 0 def test_x509_makecertself_25519(): print("\nCREATE A SELF-SIGNED X.509 CERTIFICATE USING Ed25519...") # Ref: [RFC8410] https://tools.ietf.org/html/rfc8410 # 1. Create a new self-*signed* certificate using the Ed25519 key in RFC8410 certname = "ietf-Ed25519-self.cer" prikeyfile = "edwards-ietf.p8" # No password dn = "CN=IETF Test Demo" extns = "notBefore=2016-01-01;notAfter=2040-12-31" keyusage = X509.KeyUsageFlags.DIGITALSIGNATURE | X509.KeyUsageFlags.KEYCERTSIGN | X509.KeyUsageFlags.CRLSIGN r = X509.make_cert_self(certname, prikeyfile, "", 0x0ED25519, 0, dn, extns, keyusage, X509.SigAlg.ED25519, X509.Opts.UTF8) print(f"X509.make_cert_self returns {r} (expected 0)") assert 0 == r print(f"FILE: {certname}") print(X509.text_dump_tostring(certname)) # Do a query on the cert query = "signatureAlgorithm" s = X509.query_cert(certname, query) print(f"X509.query_sigdata({query})={s}") assert len(s) > 0 # 2. Now create a self-*issued* cert using Ed25519 to sign an X25519 public key # [RFC8410] 10.2. Example X25519 Certificate # NB This is self-*issued* in that the public key is for an X25519 key intended for ECDH, # but it is signed using an Ed25519 signature with a key also belonging to ones self. # Read in X25519 public key from its hex value # NB we *must* specify that it's a public key pubkeystr = Ecc.read_key_by_curve("8520F0098930A754748B7DDCB43EF75A0DBF3A0D26381AF4EBA4A98EAA9B4E6A", Ecc.CurveName.X25519, Ecc.KeyType.PUBLIC_KEY) assert len(pubkeystr) > 0 # Set cert parameters to closely duplicate the cert given in RFC8410 (almost!) dn = "CN=IETF Test Demo" extns = "notBefore=2016-08-01T12:19:24;notAfter=2040-12-31T23:59:59;keyUsage=noncritical;serialNumber=#x5601474A2A8DC330;" + \ "subjectKeyIdentifier=9B1F5EEDED043385E4F7BC623C5975B90BC8BB3B" keyusage = X509.KeyUsageFlags.KEYAGREEMENT issuercert = certname # Use the self-signed cert we made above to issue this new cert certname = "ietf-X25519-self-issued.cer" r = X509.make_cert(certname, issuercert, pubkeystr, prikeyfile, "", 0, 0, dn, extns, keyusage, X509.SigAlg.ED25519, X509.Opts.UTF8) assert 0 == r print(f"FILE: {certname}") # Dump cert details print(X509.text_dump_tostring(certname)) # Query the public key algorithm query = "subjectPublicKeyAlgorithm" s = X509.query_cert(certname, query) print(f"X509.query_sigdata({query})={s}") assert len(s) > 0 # Verify that this cert was signed by the one above f = X509.cert_is_verified(certname, issuercert) print(f"X509.cert_is_verified returns {f}") assert f, "cert verification failed" def test_cms_pseudo(): print("\nCREATE A SIGNED-DATA CMS OBJECT USING 'PSEUDO' PLACEHOLDER...") # NB signature will be different each time because signingTime is different every time pseudofile = "BasicSignByAlice_pseudo.p7m" opts = Cms.SigDataOpts.PSEUDOSIG | Cms.SigDataOpts.ALT_ALGID | Cms.SigDataOpts.INCLUDE_ATTRS | Cms.SigDataOpts.ADD_SIGNTIME | Cms.SigDataOpts.ADD_SIGNINGCERT # NB privkey not required with PSEUDO option r = Cms.make_sigdata(pseudofile, "excontent.txt", "AliceRSASignByCarl.cer", "", Cms.SigAlg.RSA_SHA256, opts) print(f"Cms.make_sigdata(PSEUDOSIG) returns {r} (expected 0)") assert 0 == r print(f"Created file {pseudofile}") # Expecting bbbbbb... (this is *exactly* the correct length for the final signature) print("signatureValue: " + Cms.query_sigdata(pseudofile, "signatureValue")) # Check signing time (not required, but just to check, out of interest) NB UTC/GMT time print("signingTime: " + Cms.query_sigdata(pseudofile, "signingTime")) # Get digest value in hex - this is the digestValue over which the signature will be created. dighex = Cms.query_sigdata(pseudofile, "DigestOfSignedAttrs") print("DigestOfSignedAttrs: " + dighex) # Convert to base64 digestvalue = Cnv.tobase64(Cnv.fromhex(dighex)) print("digestValue: " + digestvalue) # 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... signaturevalue = Sig.sign_digest(Cnv.frombase64(digestvalue), "AlicePrivRSASign.p8e", "password", Sig.Alg.RSA_SHA256) print("signatureValue: " + signaturevalue) # Now create a new signed-data file from the pseudo file and the received signature Value signedfile = "BasicSignByAlice_signed_from_pseudo.p7m" r = Cms.make_sigdata_from_pseudo(signedfile, pseudofile, Cnv.frombase64(signaturevalue)) print(f"Cms.make_sigdata_from_pseudo returns {r} (expected 0)") assert 0 == r print(f"Created file {signedfile}") # Check the resulting file has a valid signature isok = Cms.verify_sigdata(signedfile) print(f"Cms.verify_sigdata returns {isok}") assert isok def test_rsa_readjwk(): print("\nREAD IN RSA KEY REPRESENTED AS JSON JWK...") # RSA public key as a JSON string # Ref: RFC 7517 JSON Web Key (JWK) Appendix A.1 json = ''' {"kty":"RSA", "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", "e":"AQAB", "alg":"RS256", "kid":"2011-04-29"} ''' print("JSON key=" + json) publickey = Rsa.read_public_key(json) assert len(publickey) > 0 # Display some key properties print("RSA key size =", Rsa.key_bits(publickey)) # Expecting 57F6BA24 keyhashcode = Rsa.key_hashcode(publickey) print("KeyHashCode =", keyhashcode) assert keyhashcode == "57F6BA24" def test_hash_length(): print("\nTEST HASH LENGTH...") print("Hash.length(SHA-1) =", Hash.length(Hash.Alg.SHA1)) print("Hash.length(SHA-256) =", Hash.length(Hash.Alg.SHA256)) print("Hash.length(SHA-512) =", Hash.length(Hash.Alg.SHA512)) print("Hash.length(RMD160) =", Hash.length(Hash.Alg.RMD160)) def test_kdf(): print("\nTEST KEY DERIVATION FUNCTIONS...") # ansx963_2001.rsp CAVS 12.0 'ANS X9.63-2001' information for sample nbytes = 128 // 8 zz = Cnv.fromhex("96c05619d56c328ab95fe84b18264b08725b85e33fd34f08") okhex = "443024c3dae66b95e6f5670601558f71" kek = Kdf.bytes(nbytes, zz, Kdf.KdfAlg.X963, Kdf.HashAlg.SHA256) print("KEK=", Cnv.tohex(kek)) print("OK =", okhex) assert (Cnv.tohex(kek).lower() == okhex) # [RFC 5869] A.1. Test Case 1 Basic test case with SHA-256 nbytes = 42 zz = Cnv.fromhex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b") info = Cnv.fromhex("f0f1f2f3f4f5f6f7f8f9") okhex = "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865" kek = Kdf.bytes(nbytes, zz, Kdf.KdfAlg.HKDF, Kdf.HashAlg.SHA256, info, "salt=000102030405060708090a0b0c") print("KEK=", Cnv.tohex(kek)) print("OK =", okhex) assert (Cnv.tohex(kek).lower() == okhex) # [RFC 5869] A.3. Test with SHA-256 and zero-length salt/info nbytes = 42 zz = Cnv.fromhex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b") # (22 octets) okhex = "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8" kek = Kdf.bytes(nbytes, zz, Kdf.KdfAlg.HKDF, Kdf.HashAlg.SHA256) print("KEK=", Cnv.tohex(kek)) print("OK =", okhex) assert (Cnv.tohex(kek).lower() == okhex) # Test Kdf.for_cms zz = Cnv.fromhex("160E3F5588C6FB4E9CEE8BC3C1C5000AB86396468C3D1CAEC0CB6E21536B5513") okhex = "04d616c654cdf62bb186a5a088b60fb5" kek = Kdf.for_cms(zz, Kdf.KeyWrapAlg.AES128_WRAP, Kdf.KdfAlg.X963, Kdf.HashAlg.SHA1) print("KEK=", Cnv.tohex(kek)) print("OK =", okhex) assert (Cnv.tohex(kek).lower() == okhex) def test_prf(): print("\nTEST PRF FUNCTIONS...") # `KMAC_samples.pdf` "Secure Hashing - KMAC-Samples" 2017-02-27 # Sample #1 # "standard" KMAC output length KMAC128 => 256 bits, no custom string nbytes = 256 // 8 msg = Cnv.fromhex("00010203") key = Cnv.fromhex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F") okhex = "E5780B0D3EA6F7D3A429C5706AA43A00FADBD7D49628839E3187243F456EE14E" kmac = Prf.bytes(nbytes, msg, key, Prf.Alg.KMAC128) print("KMAC=", Cnv.tohex(kmac)) print("OK =", okhex) assert Cnv.tohex(kmac).upper() == okhex, "KMAC failed" # "standard" KMAC output length KMAC256 => 512 bits, no custom string # Sample #6 nbytes = 512 // 8 # Length of data is 1600 bits msg = Cnv.fromhex("""000102030405060708090A0B0C0D0E0F 101112131415161718191A1B1C1D1E1F 202122232425262728292A2B2C2D2E2F 303132333435363738393A3B3C3D3E3F 404142434445464748494A4B4C4D4E4F 505152535455565758595A5B5C5D5E5F 606162636465666768696A6B6C6D6E6F 707172737475767778797A7B7C7D7E7F 808182838485868788898A8B8C8D8E8F 909192939495969798999A9B9C9D9E9F A0A1A2A3A4A5A6A7A8A9AAABACADAEAF B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF C0C1C2C3C4C5C6C7""") key = Cnv.fromhex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F") okhex = "75358CF39E41494E949707927CEE0AF20A3FF553904C86B08F21CC414BCFD691589D27CF5E15369CBBFF8B9A4C2EB17800855D0235FF635DA82533EC6B759B69" kmac = Prf.bytes(nbytes, msg, key, Prf.Alg.KMAC256) print("KMAC=", Cnv.tohex(kmac)) print("OK =", okhex) assert Cnv.tohex(kmac).upper() == okhex, "KMAC failed" # Sample #2 # Same as Sample #1 except with custom string nbytes = 256 // 8 msg = Cnv.fromhex("00010203") key = Cnv.fromhex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F") custom = "My Tagged Application" okhex = "3B1FBA963CD8B0B59E8C1A6D71888B7143651AF8BA0A7070C0979E2811324AA5" kmac = Prf.bytes(nbytes, msg, key, Prf.Alg.KMAC128, custom) print("KMAC=", Cnv.tohex(kmac)) print("OK =", okhex) assert Cnv.tohex(kmac).upper() == okhex, "KMAC failed" # Request a lot of output (> single KECCAK block) nbytes = 1600 // 8 msg = Cnv.fromhex("00010203") key = Cnv.fromhex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F") okhex = """38158A1CAE4E1A25D85F2031246ADE69 7B3292FEF88B0923A59A02D1D53B7046 53EE7242662A10796BA20779D300D52D 7432018741233D587252D31DC48BDB82 33285D4A4ACD65848509B051A448D873 649228B6626E5EF817C7AF2DEDC91F12 0F8CA535A1EE301FAE8186FDEDE5A761 81A472A32CFAD1DDD1391E162F124D4A 7572AD8A20076601BCF81E4B0391F3E9 5AEFFA708C33C1217C96BE6A4F02FBBC 2D3B3B6FFAEB5BFD3BE4A2E02B75993F CC04DA6FAC4BFCB2A9F05792A1A5CC80 CA34186243EFDB31""" okhex = okhex.replace("\n", "") kmac = Prf.bytes(nbytes, msg, key, Prf.Alg.KMAC128) print("KMAC=", Cnv.tohex(kmac)) print("OK =", okhex) assert Cnv.tohex(kmac).upper() == okhex, "KMAC failed" def test_xof(): print("\nTEST XOF FUNCTIONS...") # Ref: "SHA-3 XOF Test Vectors for Byte-Oriented Output" # File `SHAKE256VariableOut.rsp` COUNT = 1244 nbytes = 2000 // 8 msg = Cnv.fromhex("6ae23f058f0f2264a18cd609acc26dd4dbc00f5c3ee9e13ecaea2bb5a2f0bb6b") okhex = """b9b92544fb25cfe4ec6fe437d8da2bbe 00f7bdaface3de97b8775a44d753c3ad ca3f7c6f183cc8647e229070439aa953 9ae1f8f13470c9d3527fffdeef6c94f9 f0520ff0c1ba8b16e16014e1af43ac6d 94cb7929188cce9d7b02f81a2746f52b a16988e5f6d93298d778dfe05ea0ef25 6ae3728643ce3e29c794a0370e9ca6a8 bf3e7a41e86770676ac106f7ae79e670 27ce7b7b38efe27d253a52b5cb54d6eb 4367a87736ed48cb45ef27f42683da14 0ed3295dfc575d3ea38cfc2a3697cc92 864305407369b4abac054e497378dd9f d0c4b352ea3185ce1178b3dc1599df69 db29259d4735320c8e7d33e8226620c9 a1d22761f1d35bdff79a""" okhex = okhex.replace("\n", "") xof = Xof.bytes(nbytes, msg, Xof.Alg.SHAKE256) print("OUT=", Cnv.tohex(xof)) print("OK =", okhex) assert (Cnv.tohex(xof).lower() == okhex) # Using MGF1-SHA-256 # From SPHINCS+ test vectors r.3 nbytes = 34 msg = Cnv.fromhex("3b5c056af3ebba70d4c805380420585562b32410a778f558ff951252407647e3") okhex = "5b7eb772aecf04c74af07d9d9c1c1f8d3a90dcda00d5bab1dc28daecdc86eb87611e" xof = Xof.bytes(nbytes, msg, Xof.Alg.MGF1_SHA256) print("OUT=", Cnv.tohex(xof)) print("OK =", okhex) assert Cnv.tohex(xof).lower() == okhex, "XOF failed" # Test other MGF1's nbytes = 24 msg = Cnv.fromhex("012345ff") okhex = "242fb2e7a338ae07e580047f82b7acff83a41ec5d8ff9bab" xof = Xof.bytes(nbytes, msg, Xof.Alg.MGF1_SHA1) print("OUT=", Cnv.tohex(xof)) print("OK =", okhex) assert Cnv.tohex(xof).lower() == okhex, "XOF failed" nbytes = 32 msg = Cnv.fromhex("012345ff") okhex = "6855a6ab4f421ecb99857d31c4aa836bf3d4916ee8a71a168d3f4665f2d7b74c" xof = Xof.bytes(nbytes, msg, Xof.Alg.MGF1_SHA512) print("OUT=", Cnv.tohex(xof)) print("OK =", okhex) assert Cnv.tohex(xof).lower() == okhex, "XOF failed" def test_hkpe_labeled(): print("\nTESTING HPKE LABELED{EXTRACT|EXPAND}...") print("RFC 9180 Appendix A.1 DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, AES-128-GCM") print('prk = LabeledExtract("", "dkp_prk", ikm)') extracted = Hpke.labeled_extract(None, "dkp_prk", Cnv.fromhex("7268600d403fce431561aef583ee1613527cff655c1343f29812e66706df3234"), Hpke.CurveName.X25519) print("prk:", Cnv.tohex(extracted)) assert Cnv.tohex( extracted).upper() == "7B8BFE1D6F3D0CB45C585E133299C64AC998BF46CAF2DC13BA874F23413EC23A", "labeled_extract failed" print("psk_id_hash = LabeledExtract('', 'psk_id_hash', '')") extracted = Hpke.labeled_extract(None, "psk_id_hash", None, Hpke.CurveName.X25519, Hpke.AeadAlg.AES_128_GCM) print("psk_id_hash:", Cnv.tohex(extracted)) assert Cnv.tohex( extracted).lower() == "725611c9d98c07c03f60095cd32d400d8347d45ed67097bbad50fc56da742d07", "labeled_extract failed" print("key = LabeledExpand(secret, 'key', key_schedule_context, Nk)") nk = 16 key = Hpke.labeled_expand(nk, Cnv.fromhex("12fff91991e93b48de37e7daddb52981084bd8aa64289c3788471d9a9712f397"), "key", Cnv.fromhex( "00725611c9d98c07c03f60095cd32d400d8347d45ed67097bbad50fc56da742d07cb6cffde367bb0565ba28bb02c90744a20f5ef37f30523526106f637abb05449"), Hpke.CurveName.X25519, Hpke.AeadAlg.AES_128_GCM) print("key:", Cnv.tohex(key)) assert Cnv.tohex(key).lower() == "4531685d41d65f03dc48f6b8302c05b0", "labeled_expand failed" def test_hkpe_derive_private_key(): print("\nTESTING HPKE DERIVEPRIVATEKEY...") print("RFC9180 A.1. DHKEM(X25519, HKDF-SHA256)") ikmhex = "7268600d403fce431561aef583ee1613527cff655c1343f29812e66706df3234" skokhex = "52c4a758a802cd8b936eceea314432798d5baf2d7e9235dc084ab1b9cfa2f736" pkokhex = "37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431" # A. Derive private key in hex format print("ikmE:", ikmhex) skhex = Hpke.derive_private_key(Cnv.fromhex(ikmhex), Hpke.CurveName.X25519, Hpke.OutputOpts.KEYASHEX) print("skEm:", skhex) assert skhex.lower() == skokhex, "HPKE derived key does not match test vector" # B. Derive key in ephemeral internal private key format (NB different each time) prikeystr = Hpke.derive_private_key(Cnv.fromhex(ikmhex), Hpke.CurveName.X25519) print("prikeystr:", prikeystr) print("curveName:", Ecc.query_key(prikeystr, "curveName")) # C. Get public key in hex format from internal key string pkhex = Ecc.query_key(prikeystr, "publicKey") print("pkEm:", pkhex) assert pkhex.lower() == pkokhex, "Public key does not match test vector" print("RFC9180 A.6. DHKEM(P-521, HKDF-SHA512)") ikmhex = "7f06ab8215105fc46aceeb2e3dc5028b44364f960426eb0d8e4026c2f8b5d7e7a986688f1591abf5ab753c357a5d6f0440414b4ed4ede71317772ac98d9239f70904" skokhex = "014784c692da35df6ecde98ee43ac425dbdd0969c0c72b42f2e708ab9d535415a8569bdacfcc0a114c85b8e3f26acf4d68115f8c91a66178cdbd03b7bcc5291e374b" pkokhex = "040138b385ca16bb0d5fa0c0665fbbd7e69e3ee29f63991d3e9b5fa740aab8900aaeed46ed73a49055758425a0ce36507c54b29cc5b85a5cee6bae0cf1c21f2731ece2013dc3fb7c8d21654bb161b463962ca19e8c654ff24c94dd2898de12051f1ed0692237fb02b2f8d1dc1c73e9b366b529eb436e98a996ee522aef863dd5739d2f29b0" # A. Derive private key in hex format print("ikmE:", ikmhex) skhex = Hpke.derive_private_key(Cnv.fromhex(ikmhex), Hpke.CurveName.P_521, Hpke.OutputOpts.KEYASHEX) print("skEm:", skhex) assert skhex.lower() == skokhex, "HPKE derived key does not match test vector" # B. Derive key in ephemeral internal private key format (NB different each time) prikeystr = Hpke.derive_private_key(Cnv.fromhex(ikmhex), Hpke.CurveName.P_521) print("prikeystr:", prikeystr) print("curveName:", Ecc.query_key(prikeystr, "curveName")) # C. Get public key in hex format from internal key string pkhex = Ecc.query_key(prikeystr, "publicKey") print("pkEm:", pkhex) assert pkhex.lower() == pkokhex, "Public key does not match test vector" def test_cms_makeenvdata_ecdh(): print("\nMAKE ENVELOPED-DATA OBJECTS USING ECDH KARI...") # Create an enveloped CMS object to Dana (using ecdh) and Alice (using RSA) fname = "dana_alice_all_defaults.p7m" certlist = "lamps-dana.encrypt.crt;lamps-alice.encrypt.crt" msg = "This is some sample content." n = Cms.make_envdata_from_string(fname, msg, certlist, Cipher.Alg.AES128) print("Cms.make_envdata_from_string returns", n, " (expecting 2)") assert n > 0 print("FILE:", fname) # Query the enveloped-data file query = "contentEncryptionAlgorithm" s = Cms.query_envdata(fname, query) print(f"{query}='{s}'") query = "recipientInfoType" s = Cms.query_envdata(fname, query) print(f"{query}='{s}'") query = "recipientInfoType/2" s = Cms.query_envdata(fname, query) print(f"{query}='{s}'") query = "keyEncryptionAlgorithm" s = Cms.query_envdata(fname, query) print(f"{query}='{s}'") query = "keyEncryptionAlgorithm/2" s = Cms.query_envdata(fname, query) print(f"{query}='{s}'") # Read back from CMS enveloped-data object using private keys print("Read CMS using Alice's RSA private key:") prikeyalice = Rsa.read_private_key("lamps-alice.decrypt.p8.pem", "") s = Cms.read_envdata_to_string(fname, prikeyalice, "lamps-alice.encrypt.crt") print("MSG =", s) assert (len(s) > 0) print("Read CMS using Dana's ECC X25519 private key:") prikeydana = Ecc.read_private_key("lamps-dana.decrypt.p8.pem", "") s = Cms.read_envdata_to_string(fname, prikeydana, "lamps-dana.encrypt.crt") print("MSG =", s) assert (len(s) > 0) def test_x509_makecert_internal_x25519(): print("\nCREATE A NEW CERTIFICATE USING INTERNAL KEY STRINGS WITH X25519...") certname = "new-lamps-dana.encrypt.cer" issuercert = "lamps-ca.ed25519.crt" prikeyfile = "lamps-ca.ed25519.p8" pubkeyfile = "lamps-dana.encrypt.crt" dn = "O=IETF;OU=LAMPS WG;CN=Dana Hopper" # sMIMECapabilities = ECDH with HKDF using SHA-256; uses AES-128 key wrap extns = "serialNumber=#x0E4B0A36A9EFBA9C9A3B68248E521DC0DEF3A7;notBefore=2020-12-15T21:35:44;notAfter=2052-12-15T21:35:44;extKeyUsage=emailProtection;" \ + "keyUsage=keyAgreement;sMIMECapabilities=301A060B2A864886F70D0109100313300B0609608648016503040105;" \ + "certificatePolicies=2.16.840.1.101.3.2.1.48.1;rfc822Name=dana@smime.example;subjectKeyIdentifier=9ddf4dd405ef9aec6086bc276d04e9ce5adc8fa4;" # Read in private and public keys to internal key strings prikeystr = Ecc.read_private_key(prikeyfile, "") print("prikeystr=", prikeystr) print("PRI: keyBits=", Ecc.query_key(prikeystr, "keyBits"), ", curveName =", Ecc.query_key(prikeystr, "curveName"), ", keyHashCode=", Ecc.key_hashcode(prikeystr)) assert (len(prikeystr) > 0) pubkeystr = Ecc.read_public_key(pubkeyfile) print("PUB: keyBits=", Ecc.query_key(pubkeystr, "keyBits"), ", curveName =", Ecc.query_key(pubkeystr, "curveName"), ", keyHashCode=", Ecc.key_hashcode(pubkeystr)) assert (len(pubkeystr) > 0) print("dn='" + dn + "'") print("extns='" + extns + "'") # Create the new certificate try: r = X509.make_cert(certname, issuercert, pubkeystr, prikeystr, "", distname=dn, extns=extns, sigalg=X509.SigAlg.ED25519, opts=X509.Opts.AUTHKEYID) print("X509.make_cert returns", r) except PKIError as e: print("Woops! PKIError:", e) # print(Asn1.text_dump_tostring(certname)) print("FILE:", certname) query = "subjectPublicKeyAlgorithm" print(query + "='" + X509.query_cert(certname, query) + "'") query = "signatureAlgorithm" print(query + "='" + X509.query_cert(certname, query) + "'") print(X509.text_dump_tostring(certname)) def test_cms_envdata_auth(): print("\nAUTHENTICATED-ENVELOPED-DATA OBJECTS...") # Create an authenticated-enveloped CMS object to Bob using Bob's X.509 certificate fname = "cms2bob_auth.p7m" fnamecert = "BobRSASignByCarl.cer" s = "This is some sample content." n = Cms.make_envdata_from_string(fname, s, fnamecert, Cipher.Alg.AES128, Cms.KeyEncrAlg.RSA_PKCS1V1_5, 0, Cms.EnvDataOpts.AUTHENTICATED, count=13) print("Cms.make_envdata_from_string returns", n) assert (n == 1) print(Asn1.text_dump_tostring(fname)) def test_cms_make_envdata_auth_chapoly(): print("\nAUTHENTICATED-ENVELOPED-DATA USING CHACHA20POLY1305...") filein = "excontent.txt" cmsfile = "cms2bob_auth_chapoly.p7m" certfile = "lamps-bob.crt" keyfile = "lamps-bob.p8" # Unencrypted n = Cms.make_envdata(cmsfile, filein, certfile, Cms.ContentEncrAlg.CHACHA20_POLY1305) assert n == 1 # Expecting one recipient print("Created file:", cmsfile) print(Asn1.type(cmsfile)) print("contentEncryptionAlgorithm:", Cms.query_envdata(cmsfile, "contentEncryptionAlgorithm")) print("keyEncryptionAlgorithm:", Cms.query_envdata(cmsfile, "keyEncryptionAlgorithm")) # print(Asn1.text_dump_tostring(cmsfile)) # Now try and read it prikeystr = Rsa.read_private_key(keyfile) decstr = Cms.read_envdata_to_string(cmsfile, prikeystr) assert len(decstr) > 0 print(f"DEC: '{decstr}'") # Now use X25519 print("\nUsing X25519...") cmsfile = "cms2carlos_auth_chapoly_X25519.p7m" certfile = "lamps-carlos.encrypt.cer" keyfile = "lamps-carlos.decrypt.p8" # Unencrypted n = Cms.make_envdata(cmsfile, filein, certfile, Cms.ContentEncrAlg.CHACHA20_POLY1305, kdfalg=Kdf.KdfAlg.HKDF) assert n == 1 # Expecting one recipient print("Created file:", cmsfile) print(Asn1.type(cmsfile)) print("contentEncryptionAlgorithm:", Cms.query_envdata(cmsfile, "contentEncryptionAlgorithm")) print("keyEncryptionAlgorithm:", Cms.query_envdata(cmsfile, "keyEncryptionAlgorithm")) print("originatorKeyAlgorithm:", Cms.query_envdata(cmsfile, "originatorKeyAlgorithm")) # print(Asn1.text_dump_tostring(cmsfile)) # Now try and read it prikeystr = Ecc.read_private_key(keyfile) decstr = Cms.read_envdata_to_string(cmsfile, prikeystr) assert len(decstr) > 0 print(f"DEC: '{decstr}'") print("\nUsing X448...") cmsfile = "cms2alice_auth_chapoly_X448.p7m" certfile = "X448-alice-self-issued.cer" keyfile = "X448-alice-self.p8" # Unencrypted n = Cms.make_envdata(cmsfile, filein, certfile, Cms.ContentEncrAlg.CHACHA20_POLY1305, hashalg=Hash.Alg.SHA512, kdfalg=Kdf.KdfAlg.HKDF) assert n == 1 # Expecting one recipient print("Created file:", cmsfile) print(Asn1.type(cmsfile)) print("keyEncryptionAlgorithm:", Cms.query_envdata(cmsfile, "keyEncryptionAlgorithm")) print("originatorKeyAlgorithm:", Cms.query_envdata(cmsfile, "originatorKeyAlgorithm")) # print(Asn1.text_dump_tostring(cmsfile)) # Now try and read it prikeystr = Ecc.read_private_key(keyfile) decstr = Cms.read_envdata_to_string(cmsfile, prikeystr) assert len(decstr) > 0 print(f"DEC: '{decstr}'") def test_cms_envdata_examples(): print("\nENVELOPED-DATA EXAMPLES USED IN DOCS...") print("Create an enveloped CMS object (ktri type) to Bob using Bob's RSA key..") n = Cms.make_envdata("cms2bob_aes128.p7m", "excontent.txt", "BobRSASignByCarl.cer", Cipher.Alg.AES128, Cms.KeyEncrAlg.RSA_OAEP) print("Cms.make_envdata returns", n) assert (n == 1) fname = "cms2bob_aes128.p7m" print("FILE:", fname) query = "recipientInfoType" print("%s=%s" % (query, Cms.query_envdata(fname, query))) query = "contentEncryptionAlgorithm" print("%s=%s" % (query, Cms.query_envdata(fname, query))) print("Same but using authenticated encryption and creating an authEnvelopedData object..") n = Cms.make_envdata("cms2bob_aes128auth.p7m", "excontent.txt", "BobRSASignByCarl.cer", Cms.ContentEncrAlg.AES_128_GCM, Cms.KeyEncrAlg.RSA_OAEP) assert (n == 1) fname = "cms2bob_aes128auth.p7m" print("FILE:", fname) query = "recipientInfoType" print("%s=%s" % (query, Cms.query_envdata(fname, query))) query = "contentEncryptionAlgorithm" print("%s=%s" % (query, Cms.query_envdata(fname, query))) print("Create an enveloped CMS object (kari type) to Dana using Dana's ECC key..") n = Cms.make_envdata("cms2dana_hkdf.p7m", "excontent.txt", "lamps-dana.encrypt.crt", Cipher.Alg.AES256, hashalg=Hash.Alg.SHA256, kdfalg=Kdf.KdfAlg.HKDF, keywrapalg=Kdf.KeyWrapAlg.AES256_WRAP) print("Cms.make_envdata returns", n) assert (n == 1) fname = "cms2dana_hkdf.p7m" print("FILE:", fname) query = "recipientInfoType" print("%s=%s" % (query, Cms.query_envdata(fname, query))) query = "contentEncryptionAlgorithm" print("%s=%s" % (query, Cms.query_envdata(fname, query))) print( "Create an enveloped CMS object (kekri type) using a previously distributed symmetric key-encryption key (KEK)..") n = Cms.make_envdata("cms_envdata_kekri.p7m", "excontent.txt", "type=@kekri,keyid=ourcommonkey", Cipher.Alg.AES256, hashalg=Hash.Alg.SHA256, keywrapalg=Kdf.KeyWrapAlg.AES128_WRAP, keyString="#x0123456789ABCDEFF0E1D2C3B4A59687") print("Cms.make_envdata returns", n) assert (n == 1) fname = "cms_envdata_kekri.p7m" print("FILE:", fname) query = "recipientInfoType" print("%s=%s" % (query, Cms.query_envdata(fname, query))) query = "contentEncryptionAlgorithm" print("%s=%s" % (query, Cms.query_envdata(fname, query))) query = "keyid" print("%s=%s" % (query, Cms.query_envdata(fname, query))) print("Create an enveloped CMS object (pwri type) using password-based key management..") n = Cms.make_envdata("cms_envdata_pwri.p7m", "excontent.txt", "type=@pwri", Cipher.Alg.AES192, keyString="password12345") print("Cms.make_envdata returns", n) assert (n == 1) fname = "cms_envdata_pwri.p7m" print("FILE:", fname) query = "recipientInfoType" print("%s=%s" % (query, Cms.query_envdata(fname, query))) query = "contentEncryptionAlgorithm" print("%s=%s" % (query, Cms.query_envdata(fname, query))) print("\nNow read in the enveloped-data objects we made above...") prikeystr = Rsa.read_private_key("BobPrivRSAEncrypt.p8e", "password") assert (len(prikeystr) > 0) fname = "cms2bob_aes128.p7m" print("FILE:", fname) s = Cms.read_envdata_to_string(fname, prikeystr) print("MSG='%s'" % s) assert (len(s) > 0) fname = "cms2bob_aes128auth.p7m" print("FILE:", fname) s = Cms.read_envdata_to_string(fname, prikeystr) print("MSG='%s'" % s) assert (len(s) > 0) prikeystr = Ecc.read_private_key("lamps-dana.decrypt.p8.pem", "") assert (len(prikeystr) > 0) fname = "cms2dana_hkdf.p7m" print("FILE:", fname) s = Cms.read_envdata_to_string(fname, prikeystr) print("MSG='%s'" % s) assert (len(s) > 0) fname = "cms_envdata_kekri.p7m" print("FILE:", fname) s = Cms.read_envdata_to_string(fname, "#x0123456789ABCDEFF0E1D2C3B4A59687") print("MSG='%s'" % s) assert (len(s) > 0) fname = "cms_envdata_pwri.p7m" print("FILE:", fname) s = Cms.read_envdata_to_string(fname, "password12345") print("MSG='%s'" % s) assert (len(s) > 0) def test_cnv_shortpathname(): print("\nGET SHORT NAME PATH...") shortname = Cnv.shortpathname("你好.txt") print("shortname='%s'" % shortname) exists = os.path.exists(shortname) # Fixed [2023-04-19] print("File '" + shortname + "' " + "EXISTS" if exists else "does not exists") assert (exists) shortname = Cnv.shortpathname("File with a long name and spaces hello there all good yes thanks.txt") print("shortname='%s'" % shortname) exists = os.path.exists(shortname) # Fixed [2023-04-19] print("File '" + shortname + "' " + "EXISTS" if exists else "does not exists") assert (exists) def test_gen_format_error_message(): print("\nTEST FORMAT ERROR MESSAGE...") # Try and read missing file try: s = Asn1.type("missing.file") except PKIError as e: print(e) # ERROR CODE 1: Cannot open input file (OPEN_ERROR): Unable to open file 'missing.file' # Attempt to create signed data but pass name of missing certificate file privkey = Rsa.read_private_key('AlicePrivRSASign.p8e', 'password') try: r = Cms.make_sigdata('sigdata.p7m', 'excontent.txt', 'missing.file', privkey) print("Cms.make_sigdata succeeded returning ", r) # Shouldn't happen except PKIError as e: print(e) # ERROR CODE 21: No match found (NO_MATCH_ERROR): (1) Cannot open input file (OPEN_ERROR): # Private key does not match any certificate in list def test_rsa_read_public_key_csr(): print("\nREAD PUBLIC KEY FROM CSR...") # 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 = X509.cert_request(csrfile, keyfile, "", dn, extns, X509.SigAlg.RSA_SHA256) print(f"X509.cert_request created file '{csrfile}'") # Dump details of CSR we just made... print(X509.text_dump_tostring(csrfile, X509.Opts.LDAP)) # New in [v20.7]: Read in public key from this CSR file to an internal key string keystr = Rsa.read_public_key(csrfile) print("Keysize=" + str(Rsa.key_bits(keystr)) + " bits, HashCode=0x" + Rsa.key_hashcode(keystr)) # Keysize=2048 bits, HashCode=0xCA0B84DA def test_x509_make_cert_448(): print("\nMAKE X.509 CERT USING CURVE448:") # 1. Create a self-signed certificate using the Ed448 key prikeyfile = "edkey448.p8" certfile = "Ed448-self.cer" dn = "CN=example.com" extns = "notBefore=2023-01-01;notAfter=2040-12-31;" # Digital Signature, Certificate Signing, [Off-line CRL Signing], CRL Signing # keyusage = X509.KeyUsageFlags.DIGITALSIGNATURE | X509.KeyUsageFlags.KEYCERTSIGN | X509.KeyUsageFlags.CRLSIGN extns += "keyUsage=digitalSignature,keyCertSign,cRLSign;" r = X509.make_cert_self(certfile, prikeyfile, "", 0x0ED448, 0, dn, extns, sigalg=X509.SigAlg.ED448, opts=X509.Opts.UTF8) print("Created self-signed X.509 certificate:", certfile) print(X509.text_dump_tostring(certfile, X509.Opts.LDAP)) print("issuerName:", X509.query_cert(certfile, "issuerName")) print("subjectName:", X509.query_cert(certfile, "subjectName")) print("subjectPublicKeyAlgorithm:", X509.query_cert(certfile, "subjectPublicKeyAlgorithm")) print("signatureAlgorithm:", X509.query_cert(certfile, "signatureAlgorithm")) # 2. Now create a self-*issued* cert using Ed448 to sign an X448 public key print("\nAbout to create a *self-issued* cert using Ed448 to sign an X448 public key...") # Read in the public key from its hex value pubkeystr = Ecc.read_key_by_curve( "9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0", Ecc.CurveName.X448, ispublic=True) assert len(pubkeystr) > 0 dn = "CN=Alice X448;O=example.com" extns = "notBefore=2023-07-01T12:19:24;notAfter=2040-12-31T23:59:58;keyUsage=noncritical;serialNumber=#x5601474A2A8DC330;" \ + "subjectKeyIdentifier=DEADBEEFCAFEBABE" keyusage = X509.KeyUsageFlags.KEYAGREEMENT issuercert = certfile # Created above certfile = "X448-self-issued.cer" # Create self-issued cert containing Alice's X448 public key but signed by Ed448 cert r = X509.make_cert(certfile, issuercert, pubkeystr, prikeyfile, "", 0, 0, dn, extns, keyusage, X509.SigAlg.ED448, opts=X509.Opts.UTF8) print("Created self-issued X.509 certificate:", certfile) # print(X509.text_dump_tostring(certfile, X509.Opts.LDAP)) print("issuerName:", X509.query_cert(certfile, "issuerName")) print("subjectName:", X509.query_cert(certfile, "subjectName")) print("subjectPublicKeyAlgorithm:", X509.query_cert(certfile, "subjectPublicKeyAlgorithm")) print("signatureAlgorithm:", X509.query_cert(certfile, "signatureAlgorithm")) # Verify that self-issued cert was signed by issuer print("Verify the certificate against its issuer...") isok = X509.cert_is_verified(certfile, issuercert) print("X509.cert_is_verified returns", isok) assert isok def test_scrypt(): print("\nTESTING SCRYPT PASSWORD-BASED KEY DERIVATION FUNCTION...") # Use SCRYPT examples from RFC7914 dk = Pbe.scrypt(64, b'password', b'NaCl', 1024, 8, 16) print("dk(SCRYPT)=", Cnv.tohex(dk)) assert Cnv.tohex(dk) == 'FDBABE1C9D3472007856E7190D01E9FE7C6AD7CBC8237830E77376634B373162' \ + '2EAF30D92E22A3886FF109279D9830DAC727AFB94A83EE6D8360CBDFA2CC0640' # Pass empty string for both password and salt with (N=16, r=1, p=1) dk = Pbe.scrypt(64, b'', b'', 16, 1, 1) print("dk(SCRYPT)=", Cnv.tohex(dk)) assert Cnv.tohex(dk) == '77D6576238657B203B19CA42C18A0497F16B4844E3074AE8DFDFFA3FEDE21442' \ + 'FCD0069DED0948F8326A753A0FC81F17E8D3E0FB2E0D3628CF35E20C38D18906' def test_ecc_make_keys_448(): print("\nTESTING MAKE KEYS FOR ED448 and X448...") pubkeyfile = "myed448.pub" prikeyfile = "myed448.p8e" pwd = "password" n = Ecc.make_keys(pubkeyfile, prikeyfile, Ecc.CurveName.ED448, pwd, pbescheme=Ecc.PbeScheme.PBKDF2_AES256) assert (0 == n) _dump_and_print_asn1(pubkeyfile) print(pubkeyfile + ": " + Asn1.type(pubkeyfile)) print(prikeyfile + ": " + Asn1.type(prikeyfile)) # Read in private key as internal key string skstr = Ecc.read_private_key(prikeyfile, pwd) print("sk curve =", Ecc.query_key(skstr, "curveName"), "keyhashcode =", Ecc.key_hashcode(skstr)) pkstr = Ecc.read_public_key(pubkeyfile) print("pk curve =", Ecc.query_key(pkstr, "curveName"), "keyhashcode =", Ecc.key_hashcode(pkstr)) pubkeyfile = "myX448.pub" prikeyfile = "myX448.p8e" pwd = "password" n = Ecc.make_keys(pubkeyfile, prikeyfile, Ecc.CurveName.X448, pwd, pbescheme=Ecc.PbeScheme.PBKDF2_AES256) assert (0 == n) _dump_and_print_asn1(pubkeyfile) print(pubkeyfile + ": " + Asn1.type(pubkeyfile)) print(prikeyfile + ": " + Asn1.type(prikeyfile)) # Read in private key as internal key string skstr = Ecc.read_private_key(prikeyfile, pwd) print("sk curve =", Ecc.query_key(skstr, "curveName"), "keyhashcode =", Ecc.key_hashcode(skstr)) pkstr = Ecc.read_public_key(pubkeyfile) print("pk curve =", Ecc.query_key(pkstr, "curveName"), "keyhashcode =", Ecc.key_hashcode(pkstr)) def test_ecc_readbycurve_448(): print("\nTESTING READBYCURVE FOR ED448 and X448...") prikeystr = Ecc.read_key_by_curve( "c4eab05d357007c632f3dbb48489924d 552b08fe0c353a0d4a1f00acda2c463a fbea67c5e8d2877c5e3bc397a659949e f8021e954e0a12274e", Ecc.CurveName.ED448, ispublic=False) assert len(prikeystr) > 0 print("CURVE: ", Ecc.query_key(prikeystr, "curveName")) print("KeyHashCode:", Ecc.key_hashcode(prikeystr)) print("SK: ", Ecc.query_key(prikeystr, "privateKey")) print("PK: ", Ecc.query_key(prikeystr, "publicKey")) # Read in corresponding public key pubkeystr = Ecc.read_key_by_curve( "43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480", Ecc.CurveName.ED448, ispublic=True) assert len(pubkeystr) > 0 print("CURVE: ", Ecc.query_key(pubkeystr, "curveName")) print("KeyHashCode:", Ecc.key_hashcode(pubkeystr)) # Again for ECDH X448 key prikeystr = Ecc.read_key_by_curve( "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", Ecc.CurveName.X448, ispublic=False) assert len(prikeystr) > 0 print("CURVE: ", Ecc.query_key(prikeystr, "curveName")) print("KeyHashCode:", Ecc.key_hashcode(prikeystr)) print("SK: ", Ecc.query_key(prikeystr, "privateKey")) print("PK: ", Ecc.query_key(prikeystr, "publicKey")) # Read in corresponding public key pubkeystr = Ecc.read_key_by_curve( "172837c1ef0bf5d890af8dcee6bda1ad1970c167e893dd46054795693a11397580fe732f2b50bd9fc1d7596c62fd5c4d5df403e94ad8c507", Ecc.CurveName.X448, ispublic=True) assert len(pubkeystr) > 0 print("CURVE: ", Ecc.query_key(pubkeystr, "curveName")) print("KeyHashCode:", Ecc.key_hashcode(pubkeystr)) def test_ecc_savekeys_448(): print("\nTESTING SAVEKEYS FOR CURVE448...") # Read in private key from hex representation prikeystr = Ecc.read_key_by_curve( "9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b", Ecc.CurveName.X448, ispublic=False) assert len(prikeystr) > 0 print("CURVE: ", Ecc.query_key(prikeystr, "curveName")) print("KeyHashCode:", Ecc.key_hashcode(prikeystr)) print("SK: ", Ecc.query_key(prikeystr, "privateKey")) print("PK: ", Ecc.query_key(prikeystr, "publicKey")) # Save to unencrypted PKCS8 v2 file (OneAsymmetricKey) # [v22.0] by default now includes the public key as well fname = "keypri_X448.p8" r = Ecc.save_key(fname, prikeystr) print(f"Created key file '{fname}'") print(Asn1.type(fname)) print(Asn1.text_dump_tostring(fname)) # Check we can read the private key prikeystr = Ecc.read_private_key(fname) assert len(prikeystr) > 0 print("KeyHashCode:", Ecc.key_hashcode(prikeystr)) print("Is a Private Key" if Ecc.query_key(prikeystr, "isPrivate") else "Is a Public Key") # Check we can read the public key pubkeystr = Ecc.read_public_key(fname) assert len(pubkeystr) > 0 print("KeyHashCode:", Ecc.key_hashcode(pubkeystr)) print("Is a Private Key" if Ecc.query_key(pubkeystr, "isPrivate") else "Is a Public Key") # Now save in legacy v1 PKCS8 without public key fname = "keypri_X448_legacy.p8" r = Ecc.save_key(fname, prikeystr, keytype=Ecc.KeyType.LEGACY) print(f"Created key file '{fname}'") print(Asn1.type(fname)) print(Asn1.text_dump_tostring(fname)) def test_pfx_makefile_double(): print("\nTESTING MAKE PFX LIKE PARAGUAY CA'S DO...") epkfile = "sifen-emisor.p8e" certfile = "sifen-emisor.cer" pwd = "12345678a" pfxfile = "sifen-emisor.p12" # Make a PFX like Paraguay SET CA's (unencrypted cert + double-encrypted key) n = Pfx.make_file(pfxfile, certfile, epkfile, pwd, "EMPRESA DE AUTOBUSES", Pfx.Opts.DOUBLE_ENCRYPT | Pfx.Opts.PLAIN_CERT) assert (0 == n) print("Created new PKCS#12 file:", pfxfile) print("Asn1.Type(" + pfxfile + ") -->", Asn1.type(pfxfile)) # print(Asn1.text_dump_tostring(pfxfile)) # Make sure we can read both the private key and certificate prikeystr = Rsa.read_private_key(pfxfile, pwd) assert len(prikeystr) > 0 print("Key size =", Rsa.key_bits(prikeystr)) certstr = X509.read_cert_string_from_pfx(pfxfile, pwd) assert len(certstr) > 0 print("Cert subject =", X509.query_cert(certstr, "subjectName", opts=X509.Opts.LDAP)) # Explicity call this function to test the Pwd dialog class # Note this does not begin with `test_` because we don't want it firing in py.test def do_pwd(): print("\nTESTING PWD DIALOG...") pwd = Pwd.prompt() print("[" + pwd + "]") pwd = Pwd.prompt("Demo of Pwd.prompt()", "Type secret phrase:") print("[" + pwd + "]") def quick_version(): print("\nDETAILS OF CORE DLL...") print("DLL Version=" + str(Gen.version()) + " [" + Gen.core_platform() + "] Lic=" + Gen.licence_type() + " Compiled=[" + Gen.compile_time() + "]") print("[" + Gen.module_name() + "]" + " (" + Gen.module_info() + ")") def main(): do_all = True for arg in sys.argv: global delete_tmp_dir if (arg == 'nodelete'): delete_tmp_dir = False elif (arg == 'some'): do_all = False setup_temp_dir() # DO THE TESTS - EITHER SOME OR ALL if (do_all): test_version() test_error_lookup() test_cnv() test_cnv_utf8() test_cipher() test_cipher_block() test_cipher_file() test_cipher_keywrap() test_cipher_pad() test_rsa_makekeys() test_rsa_errors() test_rsa_savekeys() test_rsa_sign() test_rsa_encrypt() test_rng() test_hash() test_hmac() test_x509_generate() test_x509_analyze() test_x509_validate() test_x509_extract() test_wipe() test_asn1() test_ocsp() test_ecc() test_pbe() test_pfx() test_pem() test_cms_envdata() test_cms_sigdata() test_cms_comprdata() test_smime() test_sig_rsa() test_sig_ecc() test_x509_ecc() test_asn1_dumptostring() test_compress() test_aead() test_readcertstring() test_cipher_prefix() test_x509_makecert_emptydn() test_x509_certrequest_emptydn_extkeyusage() test_read_x509_from_pfx_3des() test_pfx_makefile_3des() test_rng_guid() test_ecc_dh_shared_secret() test_ecc_dh_shared_secret_x25519() test_cipher_hex() test_sig_signdata_ed25519() test_cms_makesigdata_ed25519() test_x509_makecertself_25519() test_cms_pseudo() test_rsa_readjwk() test_ecc_brainpool() test_hash_length() test_kdf() test_cms_makeenvdata_ecdh() test_x509_makecert_internal_x25519() test_cms_envdata_auth() test_cms_envdata_examples() test_cnv_shortpathname() test_gen_format_error_message() test_cipher_gcm() test_rsa_read_public_key_csr() test_hash_sha3() test_hmac_sha3() test_prf() test_xof() test_scrypt() test_pfx_makefile_double() test_ecc_make_keys_448() test_ecc_readbycurve_448() test_ecc_savekeys_448() test_ecc_dh_shared_secret_x448() test_x509_make_cert_448() test_sig_signdata_ed448() test_cms_makesigdata_ed448() test_hkpe_labeled() test_hkpe_derive_private_key() test_aead_chapoly() test_cms_make_envdata_auth_chapoly() else: # just do some tests: comment out as necessary test_version() # test_error_lookup() # test_cnv() # test_cnv_utf8() # test_cipher() # test_cipher_block() # test_cipher_file() # test_cipher_keywrap() # test_cipher_pad() # test_rsa_makekeys() # test_rsa_errors() # test_rsa_savekeys() # test_rsa_sign() # test_rsa_encrypt() # test_rng() # test_hash() # test_hmac() # test_x509_generate() # test_x509_analyze() # test_x509_validate() # test_x509_extract() # test_wipe() # test_asn1() # test_ocsp() # test_ecc() # test_pbe() # test_pfx() # test_pem() # test_cms_envdata() # test_cms_sigdata() # test_cms_comprdata() # test_smime() # test_sig_rsa() # test_sig_ecc() # test_x509_ecc() # test_asn1_dumptostring() # test_compress() # test_aead() # test_readcertstring() # test_cipher_prefix() # test_x509_makecert_emptydn() # test_x509_certrequest_emptydn_extkeyusage() # test_read_x509_from_pfx_3des() # test_pfx_makefile_3des() # test_rng_guid() # test_ecc_dh_shared_secret() # test_ecc_dh_shared_secret_x25519() # test_cipher_hex() # test_sig_signdata_ed25519() # test_cms_makesigdata_ed25519() # test_x509_makecertself_25519() # test_cms_pseudo() # test_rsa_readjwk() # test_ecc_brainpool() # New in [v20.5] # test_hash_length() # test_kdf() # test_cms_makeenvdata_ecdh() # test_x509_makecert_internal_x25519() # # New in [v20.6] # test_cms_envdata_auth() # test_cms_envdata_examples() # # New in [v21.0] # test_cnv_shortpathname() # test_gen_format_error_message() # test_cipher_gcm() # test_rsa_read_public_key_csr() # test_x509_make_cert_ex() # test_hash_sha3() # test_hmac_sha3() # test_prf() # test_xof() # # New in [v22.0] # test_scrypt() # test_pfx_makefile_double() # test_ecc_make_keys_448() # test_ecc_readbycurve_448() # test_ecc_savekeys_448() # test_ecc_dh_shared_secret_x448() # test_x509_make_cert_448() # test_sig_signdata_ed448() # test_cms_makesigdata_ed448() # test_hkpe_labeled() # test_hkpe_derive_private_key() # test_aead_chapoly() test_cms_make_envdata_auth_chapoly() # Uncomment the next line to test the Pwd dialog procedure # Do not do in py.test (unless you want to interact!) # ## do_pwd() reset_start_dir() quick_version() print("__version__=", pki.__version__) print("ALL DONE.") if __name__ == "__main__": main()