#! python3
# -*- coding: utf-8 -*-

"""Some tests for ``cryptosyspki`` the Python interface to CryptoSys PKI"""

# test_pki.py: version 23.2.0
# $Date: 2026-09-01 08:13:00 $

# ************************** LICENSE *****************************************
# Copyright (C) 2016-26 David Ireland, DI Management Services Pty Limited
# t/a CryptoSys. <https://di-mgt.com.au> <https://cryptosys.net>
# The code in this module is licensed under the terms of the MIT license.
# SPDX-License-Identifier: 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 = 230200

# 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)


def textwrap(text, width=64):
    """Simple textwrap to display string."""
    return "\n".join([text[i:i + width] for i in range(0, len(text) - 1, width)])


#############
# 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(f"Rng.initialize() returns {n}.")
    sd = read_binary_file(seedfile)
    # print(textwrap(Cnv.tohex(sd)))
    assert (len(sd) == Rng.SEED_BYTES)

    print("5 random byte arrays")
    for i in (16,24,32,48,64):
        b = Rng.bytestring(i)
        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 <= 1000000)

    print("10 random octet values")
    s = ""  # form string to do in one line
    for i in range(10):
        r = Rng.octet()
        assert (0 <= r <= 255)
        s += str(r) + " "
    print(s)

    # Update seedfile
    n = Rng.update_seedfile(seedfile)
    assert (0 == n)
    print(f"Rng.update_seedfile() returns {n}. Contents of seed file:")
    sd = read_binary_file(seedfile)
    print(textwrap(Cnv.tohex(sd)))
    assert (len(sd) == Rng.SEED_BYTES)

    # Carry out DRBGVS test
    # Ref: drbgtestvectors/drbgvectors_pr_false/HMAC_DRBG.txt (line 22654)
    # CAVS 14.3 DRBG800-90A information for "drbg_pr" COUNT = 0
    s = Rng.test_drbgvs(2048, "da740cbc36057a8e282ae717fe7dfbb245e9e5d49908a0119c5dbcf0a1f2d5ab", "46561ff612217ba3ff91baa06d4b5440",
        "fc227293523ecb5b1e28c87863626627d958acc558a672b148ce19e2abd2dde4", "b7998998eaf9e5d34e64ff7f03de765b31f407899d20535573e670c1b402c26a",
        "1d61d4d8a41c3254b92104fd555adae0569d1835bb52657ec7fbba0fe03579c5", "b9ed8e35ad018a375b61189c8d365b00507cb1b4510d21cac212356b5bbaa8b2",
        "2089d49d63e0c4df58879d0cb1ba998e5b3d1a7786b785e7cf13ca5ea5e33cfd")
    ok = "5b70f3e4da95264233efbab155b828d4e231b67cc92757feca407cc9615a6608" + \
        "71cb07ad1a2e9a99412feda8ee34dc9c57fa08d3f8225b30d29887d20907d123" + \
        "30fffd14d1697ba0756d37491b0a8814106e46c8677d49d9157109c402ad0c24" + \
        "7a2f50cd5d99e538c850b906937a05dbb8888d984bc77f6ca00b0e3bc97b16d6" + \
        "d25814a54aa12143afddd8b2263690565d545f4137e593bb3ca88a37b0aadf79" + \
        "726b95c61906257e6dc47acd5b6b7e4b534243b13c16ad5a0a1163c0099fce43" + \
        "f428cd27c3e6463cf5e9a9621f4b3d0b3d4654316f4707675df39278d5783823" + \
        "049477dcce8c57fdbd576711c91301e9bd6bb0d3e72dc46d480ed8f61fd63811"

    print("Rng.test_drbgvs returns:")
    print(textwrap(s))
    print("Expected:\n", ok[:64], '\n... ', ok[-32:], sep='')
    assert(s == ok)


def test_rng_initialize_ex():
    print("\nTESTING RNG_INITIALIZE_EX...")
    n = Rng.initialize_ex()
    print(f"Rng.initialize_ex returns {n} (if >0 then Intel(R) DRNG is supported)")
    # Explicitly turn off support for rest of session
    n = Rng.initialize_ex(Rng.Opts.NO_INTEL_DRNG)
    print(f"Rng.initialize_ex(NO_INTEL_DRNG) returns {n} (expected -214)")
    # Check again, should now be off
    n = Rng.initialize_ex()
    print(f"Rng.initialize_ex returns {n} (expected -214)")



# Explicity call this function to test the random-number generator prompts
# This does not begin with "test_" so as not to fire in py.test
def do_rng_prompt():
    # FUNCS THAT OPEN A DIALOG BOX FOR KEYBOARD PROMPTS...
    n = Rng.make_seedfile('newseed.dat', strength=Rng.Strength.BITS_128)
    print("crsysapi.Rng.make_seedfile returns", n)

    b = Rng.bytes_with_prompt(32, Rng.Strength.BITS_192, "Type random keys until done")
    print("crsysapi.Rng.bytes_with_prompt:", Cnv.tohex(b).lower())


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_cms_envdata_oaep():
    print("\nTESTING CMS ENV-DATA...")
    print("Creating an enveloped-data message for Bob using RSA-OAEP-SHA-256")
    # Create a file
    mytext = 'This is some sample content.'
    myfile = "mycontent.txt"
    write_text_file(myfile, mytext)
    envdatafile = 'cms2boboaep.p7m'
    certlist = "lamps-bob.encrypt.crt"
    n = Cms.make_envdata(envdatafile, myfile, certlist, cipheralg=Cms.ContentEncrAlg.AES128,
                         keyencralg=Cms.KeyEncrAlg.RSA_OAEP, hashalg=Hash.Alg.SHA256)
    print("Cms.make_envdata() returns " + str(n) + " (expected 1 = # 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)
    query = "SizeOfEncryptedContent"
    res = Cms.query_envdata(envdatafile, query)
    print("Cms.query_envdata(" + fname + ", " + query + "):", res)
    query = "keyEncryptionAlgorithm"
    res = Cms.query_envdata(envdatafile, query)
    print("Cms.query_envdata(" + fname + ", " + query + "):", res)

    print("Bob reads the message directly into a byte array")
    # Bob reads in his private key to a secure "internal" key string
    prikeystr = Rsa.read_private_key('lamps-bob.decrypt.p8.pem')

    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 RFC9216 with cert encrypted using "stronger" 3DES
    pfxfile = "lamps-bob.p12.pem"
    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."
    # Changed in [v23.0] `Cms.EnvDataOpts.AUTHENTICATED` removed. Use explicit `Cms.ContentEncrAlg`
    # n = Cms.make_envdata_from_string(fname, s, fnamecert, Cipher.Alg.AES128, Cms.KeyEncrAlg.RSA_PKCS1V1_5,
    #                                  0, Cms.EnvDataOpts.AUTHENTICATED, count=13)
    n = Cms.make_envdata_from_string(fname, s, fnamecert, Cms.ContentEncrAlg.AES_128_GCM, Cms.KeyEncrAlg.RSA_PKCS1V1_5,
                                     0, 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))


def test_kdf2_3():
    print("\nTESTING KDF2 AND KDF3 from ANSI-X9.44...")
    zhex = """9c126102a5c1c0354672a3c2f19fc9ddea988f815e1da812c7bd4f8eb082bdd1
       4f85a7f7c2f1af11d5333e0d6bcb375bf855f208da72ba27e6fb0655f2825aa6
       2b93b1f9bbd3491fed58f0380fa0de36430e3a144d569600bd362609be5b9481
       0875990b614e406fa6dff500043cbca95968faba61f795096a7fb3687a51078c
       4ca2cb663366b0bea0cd9cccac72a25f3f4ed03deb68b4453bba44b943f4367b
       67d6cd10c8ace53f545aac50968fc3c6ecc80f3224b64e37038504e2d2c0e2b2
       9d45e46c62826d96331360e4c17ea3ef89a9efc5fac99eda830e81450b6534dc
       0bdf042b8f3b706649c631fe51fc2445cc8d447203ec2f41f79cdfea16de1ce6
       abdfdc1e2ef2e5d5d8a65e645f397240ef5a26f5e4ff715de782e30ecf477293
       e89e13171405909a8e04dd31d21d0c57935fc1ceea8e1033e31e1bc8c56da0f3
       d79510f3f380ff58e5a61d361f2f18e99fbae5663172e8cd1f21deaddc5bbbea
       060d55f1842b93d1a9c888d0bf85d0af9947fe51acf940c7e7577eb79cabecb3
    """
    ss = Kdf.bytes(16, Cnv.fromhex(zhex), Kdf.KdfAlg.KDF3, Hash.Alg.SHA256)
    print("ss =", Cnv.tohex(ss))
    print("OK =", "3cf82ec41b54ed4d37402bbd8f805a52")

    # KDF2 Test vector #4, ANSI X9.63
    zhex = "7e335afa4b31d772c0635c7b0e06f26fcd781df947d2990a"
    kdp = "d65a4812733f8cdbcdfb4b2f4c191d87"
    ss = Kdf.bytes(1024//8, Cnv.fromhex(zhex), Kdf.KdfAlg.KDF2, Hash.Alg.SHA256, Cnv.fromhex(kdp))
    print("ss =", Cnv.tohex(ss))
    print("OK =", "c0bd9e38a8f9de14c2acd35b2f3410c6988cf02400543631e0d6a4c1d030365acbf398115e51aaddebdc9590664210f9aa9fed770d4c57edeafa0b8c14f93300865251218c262d63dadc47dfa0e0284826793985137e0a544ec80abf2fdf5ab90bdaea66204012efe34971dc431d625cd9a329b8217cc8fd0d9f02b13f2f6b0b")


def query_sigdata(fname, query, quiet=False):
    s = Cms.query_sigdata(fname, query)
    if not quiet:
        print(f"{query}={s}")
    return s


def query_envdata(fname, query, quiet=False):
    s = Cms.query_envdata(fname, query)
    if not quiet:
        print(f"{query}={s}")
    return s


def test_cms_rsa_kem():
    print("\nTESTING CMS ENV DATA USING RSA-KEM...")
    certfile = "rfc5990bis-bob.crt"  # A made-up self-signed certificate using Bob's public key from RFC5990bis
    keyfile = "rfc5990bis-bob.p8.pem"  # Bob's 3072-bit RSA key from RFC5990bis, no password
    inputdata = "Hello, world!"

    # 1. Use defaults for RSA-KEM: aes128-CBC, kdf3/sha-128/aes128-wrap. Recipientidentifier is default CHOICE issuerAndSerialNumber
    edfile = "rfc5990bis.p7m"  # Output file
    r = Cms.make_envdata_from_string(edfile, inputdata, certfile, keyencralg=Cms.KeyEncrAlg.RSA_KEM)
    print(f"Created file {edfile}")
    # Read enveloped data
    prikeystr = Rsa.read_private_key(keyfile)
    s = Cms.read_envdata_to_string(edfile, prikeystr)
    print(f"Plaintext is '{s}'")
    assert s == inputdata

    # 2. As for (1) but use subjectKeyIdentifier instead of issuerAndSerialNumber
    edfile = "rfc5990bis-ski.p7m"  # Output file
    r = Cms.make_envdata_from_string(edfile, inputdata, certfile, keyencralg=Cms.KeyEncrAlg.RSA_KEM, opts=Cms.EnvDataOpts.USE_SKI)
    print(f"Created file {edfile}")
    # Read enveloped data
    prikeystr = Rsa.read_private_key(keyfile)
    s = Cms.read_envdata_to_string(edfile, prikeystr)
    print(f"Plaintext is '{s}'")
    assert s == inputdata

    # 3. As for (2) but use HKDF instead of default KDF3
    edfile = "rfc5990bis-hkdf.p7m"  # Output file
    r = Cms.make_envdata_from_string(edfile, inputdata, certfile, keyencralg=Cms.KeyEncrAlg.RSA_KEM, opts=Cms.EnvDataOpts.USE_SKI, kdfalg=Kdf.KdfAlg.HKDF)
    print(f"Created file {edfile}")
    # Read enveloped data
    prikeystr = Rsa.read_private_key(keyfile)
    s = Cms.read_envdata_to_string(edfile, prikeystr)
    print(f"Plaintext is '{s}'")
    assert s == inputdata

    # 4. As for (2) but use AES-128-GCM from RFC 5116 in an authEnvelopedData object
    edfile = "rfc5990bis-gcm.p7m"  # Output file
    r = Cms.make_envdata_from_string(edfile, inputdata, certfile, keyencralg=Cms.KeyEncrAlg.RSA_KEM, opts=Cms.EnvDataOpts.USE_SKI, cipheralg=Cms.ContentEncrAlg.AES_128_GCM)
    print(f"Created file {edfile}")
    # Read enveloped data
    prikeystr = Rsa.read_private_key(keyfile)
    s = Cms.read_envdata_to_string(edfile, prikeystr)
    print(f"Plaintext is '{s}'")
    assert s == inputdata

    # 5. As for (4) but use AEAD_CHACHA20_POLY1305 authenticated encryption algorithm from RFC 8439
    edfile = "rfc5990bis-chachapoly.p7m"  # Output file
    r = Cms.make_envdata_from_string(edfile, inputdata, certfile, keyencralg=Cms.KeyEncrAlg.RSA_KEM,
                                     opts=Cms.EnvDataOpts.USE_SKI,  cipheralg=Cms.ContentEncrAlg.CHACHA20_POLY1305)
    print(f"Created file {edfile}")
    # Read enveloped data
    prikeystr = Rsa.read_private_key(keyfile)
    s = Cms.read_envdata_to_string(edfile, prikeystr)
    print(f"Plaintext is '{s}'")
    assert s == inputdata

    # 6. As for (2) but add user keying material (ukm)
    edfile = "rfc5990bis-ukm.p7m"  # Output file
    r = Cms.make_envdata_from_string(edfile, inputdata, certfile, keyencralg=Cms.KeyEncrAlg.RSA_KEM,
                                     opts=Cms.EnvDataOpts.USE_SKI, keyString='some user key info')
    print(f"Created file {edfile}")
    # Read enveloped data
    prikeystr = Rsa.read_private_key(keyfile)
    s = Cms.read_envdata_to_string(edfile, prikeystr)
    print(f"Plaintext is '{s}'")
    assert s == inputdata


def test_cms_makesigdata_v23():
    print("\nTESTING CMS MAKE SIGDATA WITH IMPROVEMENTS IN v23.0...")
    excontent = "This is some sample content."

    # 1. Basic Signed Content, RSA-v1_5 with SHA-256
    keyFile = "lamps-alice.p8"
    certFile = "lamps-alice.crt"
    cmsFile = "signedByAliceRSA.p7m"
    priKeyStr = Rsa.read_private_key(keyFile)
    assert len(priKeyStr) > 0
    r = Cms.make_sigdata_from_string(cmsFile, excontent, certFile, priKeyStr, Cms.SigAlg.RSA_SHA256)
    print(f"Created file '{cmsFile}'")
    # Check we can read
    sd = Cms.read_sigdata_to_string(cmsFile)
    print(f"signeddata='{sd}'")
    assert sd == excontent
    # and verify
    isok = Cms.verify_sigdata(cmsFile)
    assert(isok)

    # Query this signed-data object
    query_sigdata(cmsFile, "version")
    query_sigdata(cmsFile, "signerInfoVersion")
    query_sigdata(cmsFile, "digestAlgorithm")
    query_sigdata(cmsFile, "signatureAlgorithm")
    query_sigdata(cmsFile, "HASsubjectKeyIdentifier")
    query_sigdata(cmsFile, "signerIdentifier")

    # 2. Signed by Carlos, Ed25519, detached content, using subject key identifier
    keyFile = "lamps-carlos.p8.pem"
    certFile = "lamps-carlos.crt"
    cmsFile = "signedByCarlosDetached.p7m"
    priKeyStr = Ecc.read_private_key(keyFile)
    assert(len(priKeyStr) > 0)
    r = Cms.make_sigdata_from_string(cmsFile, excontent, certFile, priKeyStr,
                                     Cms.SigAlg.ED25519,
                                     Cms.SigDataOpts.EXCLUDE_DATA | Cms.SigDataOpts.USE_SKI)
    print(f"Created file '{cmsFile}'")

    # NB we cannot read a signed-data object with detached content (obviously)
    # and we cannot verify using a digest for ED25519 (because we need the full message)

    query_sigdata(cmsFile, "version")
    query_sigdata(cmsFile, "signerInfoVersion")
    query_sigdata(cmsFile, "digestAlgorithm")
    query_sigdata(cmsFile, "signatureAlgorithm")
    query_sigdata(cmsFile, "HASsubjectKeyIdentifier")
    query_sigdata(cmsFile, "signerIdentifier")

    # 3. Signed by both Alice and Carlos, Carlos uses SKI
    # -- use Cms.SigDataOpts.ADD_SIGNER to add to an existing signed-data object file
    sdFile = "signedByAliceRSA.p7m"
    cmsFile = "signedByAliceAndCarlosNoCerts.p7m"
    r = Cms.make_sigdata(cmsFile, sdFile, certFile, priKeyStr, Cms.SigAlg.ED25519,
                         Cms.SigDataOpts.ADD_SIGNER | Cms.SigDataOpts.USE_SKI)
    print(f"Created file '{cmsFile}'")

    # Check we can read
    sd = Cms.read_sigdata_to_string(cmsFile)
    print(f"signeddata='{sd}'")
    assert sd == excontent
    # and verify - we need to specify the certificate for each signerInfo case
    isok = Cms.verify_sigdata(cmsFile, "lamps-carlos.crt")
    assert isok
    isok = Cms.verify_sigdata(cmsFile, "lamps-alice.crt")
    assert isok

    # Query this signed-data object
    query_sigdata(cmsFile, "version")
    query_sigdata(cmsFile, "CountOfSignerInfos")
    query_sigdata(cmsFile, "CountOfDigestAlgs")
    query_sigdata(cmsFile, "CountOfCertificates")
    query_sigdata(cmsFile, "signerInfoVersion/1")
    query_sigdata(cmsFile, "digestAlgorithm/1")
    query_sigdata(cmsFile, "signatureAlgorithm/1")
    query_sigdata(cmsFile, "HASsubjectKeyIdentifier/1")
    query_sigdata(cmsFile, "signerInfoVersion/2")
    query_sigdata(cmsFile, "digestAlgorithm/2")
    query_sigdata(cmsFile, "signatureAlgorithm/2")
    query_sigdata(cmsFile, "HASsubjectKeyIdentifier/2")

    # Get both certificates in the signed-data object and show their issuerName
    certStr = query_sigdata(cmsFile, "certificate/1", quiet=True)
    print("certificate/1.issuerName:", X509.query_cert(certStr, "issuerName"))
    certStr = query_sigdata(cmsFile, "certificate/2", quiet=True)
    print("certificate/2.issuerName:", X509.query_cert(certStr, "issuerName"))


def test_cms_makeenvdata_v23():
    print("\nTESTING CMS MAKE ENVDATA WITH IMPROVEMENTS IN v23.0...")
    excontent = "This is some sample content."

    # 1. Basic encrypted content, 2 signers, RSA-OAEP and X25519
    cmsFile = "encryptedForBobAndDana"
    certFile = "lamps-bob.encrypt.crt;lamps-dana.encrypt.crt"
    r = Cms.make_envdata_from_string(cmsFile, excontent, certFile,
                                     keyencralg=Cms.KeyEncrAlg.RSA_OAEP, hashalg=Hash.Alg.SHA256)
    print(f"Created file '{cmsFile}'")

    # Make sure we can decrypt - use each private key in turn and pass matching certficiate
    certFile = "lamps-bob.encrypt.crt"
    priKeyStr = Rsa.read_private_key("lamps-bob.decrypt.p8.pem")
    assert (len(priKeyStr) > 0)
    print(f"Decrypting for matching certificate '{certFile}'")
    s = Cms.read_envdata_to_string(cmsFile, priKeyStr, certFile)
    print(f"Decrypted: {s}")
    certFile = "lamps-dana.encrypt.crt"
    # NB Dana's key is ECC not RSA
    priKeyStr = Ecc.read_private_key("lamps-dana.decrypt.p8.pem")
    assert (len(priKeyStr) > 0)
    print(f"Decrypting for matching certificate '{certFile}'")
    s = Cms.read_envdata_to_string(cmsFile, priKeyStr, certFile)
    print(f"Decrypted: {s}")
    assert len(s) > 0

    #     Examine the enveloped-data file
    query_envdata(cmsFile, "version")
    query_envdata(cmsFile, "CountOfRecipientInfos")
    query_envdata(cmsFile, "recipientInfoType/1")
    query_envdata(cmsFile, "keyEncryptionAlgorithm/1")
    query_envdata(cmsFile, "oaepParams/1")
    query_envdata(cmsFile, "keyWrapAlgorithm/1")
    query_envdata(cmsFile, "recipientInfoType/2")
    query_envdata(cmsFile, "keyEncryptionAlgorithm/2")
    query_envdata(cmsFile, "oaepParams/2")
    query_envdata(cmsFile, "keyWrapAlgorithm/2")
    query_envdata(cmsFile, "HASsubjectKeyIdentifier")

    # 2. authEnveloped for Dana using ECDH with HKDF using SHA-512, AES-256-wrap,
    # recipientIdentifier is the CHOICE subjectKeyIdentifier, user key material added
    cmsFile = "authEnvelopedForDanaX25519HKDF_ski_ukm"
    certFile = "lamps-dana.encrypt.crt"
    r = Cms.make_envdata_from_string(cmsFile, excontent, certFile,
                                     cipheralg=Cms.ContentEncrAlg.AES_128_GCM,
                                     hashalg=Hash.Alg.SHA512,
                                     kdfalg=Kdf.KdfAlg.HKDF, keywrapalg=Kdf.KeyWrapAlg.AES256_WRAP,
                                     opts=Cms.EnvDataOpts.USE_SKI,
                                     keyString="some user key info")
    print(f"Created file '{cmsFile}'")

    # Examine the enveloped-data file
    query_envdata(cmsFile, "version")
    query_envdata(cmsFile, "CountOfRecipientInfos")
    query_envdata(cmsFile, "recipientInfoType")
    query_envdata(cmsFile, "keyEncryptionAlgorithm")
    query_envdata(cmsFile, "originatorKeyAlgorithm")
    query_envdata(cmsFile, "HASsubjectKeyIdentifier")
    query_envdata(cmsFile, "recipientIdentifier")
    query_envdata(cmsFile, "ukm")

    # Read content
    priKeyStr = Ecc.read_private_key("lamps-dana.decrypt.p8.pem")
    assert len(priKeyStr) > 0
    s = Cms.read_envdata_to_string(cmsFile, priKeyStr)
    print(f"Decrypted: {s}")
    assert len(s) > 0


def test_ecc_writekey():
    print("\nTESTING ECC WRITE KEY...")
    # Read in keys using hex values
    intprikey = Ecc.read_key_by_curve("DC66B3415456D649429B53223DF7532B942D6B0E0842C30BCA4C0ACF91547BB2", Ecc.CurveName.SECP256R1)
    intpubkey = Ecc.read_key_by_curve("02AE4CDB01F614DEFC7121285FDC7F5C6D1D42C95647F061BA0080DF678867845E", Ecc.CurveName.SECP256R1)
    # Output private key in PEM form
    keypem = Ecc.write_key(intprikey)
    print("PRI KEY default:")
    print(keypem)
    # Show ASN.1 structure
    print("ASN.1 structure...")
    print(Asn1.text_dump_tostring(keypem));
    # Output public key in ECDSA compressed form
    keypem = Ecc.write_key(intpubkey, Ecc.KeyType.COMPRESSKEY)
    print("PUB KEY compressed:")
    print(keypem)
    # Show ASN.1 structure
    print("ASN.1 structure...")
    print(Asn1.text_dump_tostring(keypem));


def test_ecc_keymatch():
    print("\nTESTING ECC KEY MATCH...")
    # Read in keys using hex values
    intprikey = Ecc.read_key_by_curve("DC66B3415456D649429B53223DF7532B942D6B0E0842C30BCA4C0ACF91547BB2", Ecc.CurveName.SECP256R1)
    intpubkey = Ecc.read_key_by_curve("02AE4CDB01F614DEFC7121285FDC7F5C6D1D42C95647F061BA0080DF678867845E", Ecc.CurveName.SECP256R1)
    # Show something to indicate progress...
    print(f"PRI KEY: {Ecc.query_key(intprikey, "curveName")} {Ecc.query_key(intprikey, "keyBits")} bits {"Private" if Ecc.query_key(intprikey, "isPrivate") else "Public"}")
    print(f"PUB KEY: {Ecc.query_key(intpubkey, "curveName")} {Ecc.query_key(intprikey, "keyBits")} bits {"Private" if Ecc.query_key(intpubkey, "isPrivate") else "Public"}")
    # Check that keys match
    ismatch = Ecc.key_match(intprikey, intpubkey)
    print(f"Ecc.key_match returns {ismatch}")
    assert ismatch, "Ecc.key_match failed"

    # Again for SAFE curve Ed25519
    # Note to read SAFE curve keys we must differentiate private from public keys as they are the same length
    intprikey = Ecc.read_key_by_curve("d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842", Ecc.CurveName.ED25519, Ecc.KeyType.PRIVATE_KEY)
    intpubkey = Ecc.read_key_by_curve("19bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1", Ecc.CurveName.ED25519, Ecc.KeyType.PUBLIC_KEY)
    print(f"PRI KEY: {Ecc.query_key(intprikey, "curveName")} {Ecc.query_key(intprikey, "keyBits")} bits {"Private" if Ecc.query_key(intprikey, "isPrivate") else "Public"}")
    # Check that keys match
    ismatch = Ecc.key_match(intprikey, intpubkey)
    print(f"Ecc.key_match returns {ismatch}")
    assert ismatch, "Ecc.key_match failed"


def test_x509_makecert_ecc_self_rsa_alice():
    # Create a self-signed ECDSA cert then use it to sign Alice's RSA public key
    """
    Demonstrates: Creating a self-signed X.509 certificate using X509.make_cert_self() with an ECC private key.
    Reading in an ECC key from its hex representation with Ecc.read_key_by_curve().
    Using X509.text_dump_tostring() to display the details of an X.509 certificate.
    Passing an RSA public key as a PEM string.
    Creating an end user X.509 certificate using X509.make_cert() over the RSA public key signing this using
    the self-signed ECC certificate we created above.
    Shows that different algorithms (RSA vs ECC) can be used for the subject public key and the signature.
    Note that the subject public key in 'ecdsa_self_signed.cer' is in compressed form (begins 02).
    """
    print("\nTESTING X509 MAKECERT ECC SELF THEN SIGN RSA ALICE...")
    ecdsacert = "ecdsa_self_signed.cer"
    alicecert = "cert_rsa_signed_by_ecdsa256.cer"
    # Read in private key to internal key string
    # Ref: https://www.ietf.org/archive/id/draft-ietf-cose-cbor-encoded-cert-20.html#appendix-A.1.4
    intprikey = Ecc.read_key_by_curve("DC66B3415456D649429B53223DF7532B942D6B0E0842C30BCA4C0ACF91547BB2", Ecc.CurveName.SECP256R1)
    # Write private key file to output string, so no intermediate files required
    prikeystr = Ecc.write_key(intprikey)
    print(f"PRI KEY:\n{prikeystr}")
    # Make a self-signed certificate with subject public key compressed (new in [v23.2])
    r = X509.make_cert_self(ecdsacert, prikeystr, "", 0x2020, 10, "CN=RFC Test CA", "keyUsage=digitalSignature,keyCertSign,cRLSign;notBefore=2022-12-01",
                            0, X509.SigAlg.ECDSA_SHA256, X509.Opts.UTF8 | X509.Opts.DETERMINISTIC | X509.Opts.COMPRESSKEY)
    assert r == 0, "X509.make_cert_self failed"
    print(f"Created self-signed X.509 certificate '{ecdsacert}'")
    print(X509.text_dump_tostring(ecdsacert))
    expected_thumb = "ca1e59f33c7b2b2e207324e9497d62946da18e839b28ee00b17eda1183379f95"
    print(f"Expected SHA-256 thumb: {expected_thumb}")
    assert expected_thumb == X509.cert_thumb(ecdsacert, X509.HashAlg.SHA256).lower()

    # Make a certificate for Alice's RSA public key signed by ECC key above
    alice_pubkey_str = """-----BEGIN RSA PUBLIC KEY-----
        MIIBCgKCAQEAtPSJ6Fg4Fj5Nmn9PkrYo0jTkfCv4TfA/pdO/KLpZbJOAEr0sI7Aj
        aO7B1GuMUFJeSTulamNfCwDcDkY63PQWl+DILs7GxVwXurhYdZlaV5hcUqVAckPv
        edDBc/3rz4D/esFfs+E7QMFtmd+K04s+A8TCNO12DRVBDpbP4JFD9hsc8prDtpGm
        Fk7rd0q8gqnhxBW2RZAeLqzJOMayCQtws1q7ktkNBR2wZX5ICjecF1YJFhX4jrnH
        wp/iELGqqaNXd3/Y0pG7QFecN7836IPPdfTMSiPR+peCrhJZwLSewbWXLJe3VMvb
        vQjoBMpEYlaJBUIKkO1zQ1Pq90njlsJLOwIDAQAB
        -----END RSA PUBLIC KEY-----"""
    r = X509.make_cert(alicecert, ecdsacert, alice_pubkey_str, prikeystr, "", 0x3005, 5, "CN=Alice RSA signed by ecdsa-256",
                       "notBefore=2025-12-31", 0, X509.SigAlg.ECDSA_SHA256, X509.Opts.UTF8 | X509.Opts.DETERMINISTIC)
    assert r == 0, "X509.make_cert failed"
    print(f"Created end-user X.509 certificate '{alicecert}'")
    print(X509.text_dump_tostring(alicecert))
    expected_thumb = "1b6b1b9034ef0698b5697c5e2ff5880f9d54762056453765d7871320432cfa8e"
    print(f"Expected SHA-256 thumb: {expected_thumb}")
    assert expected_thumb == X509.cert_thumb(alicecert, X509.HashAlg.SHA256).lower()


# 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_oaep()
        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()
        test_rng_initialize_ex()
        test_kdf2_3
        test_cms_makesigdata_v23
        test_cms_makeenvdata_v23
        test_ecc_writekey()
        test_ecc_keymatch()
        test_x509_makecert_ecc_self_rsa_alice()

    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_oaep()
        # 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()
        # # New in [v22.1]
        # test_rng_initialize_ex()
        # New in [v23.0]
        # test_kdf2_3()
        # test_cms_rsa_kem()
        # test_cms_makesigdata_v23()
        # test_cms_makeenvdata_v23()
        # New in [v23.2]
        # test_ecc_writekey()
        # test_ecc_keymatch()
        test_x509_makecert_ecc_self_rsa_alice()

        # Uncomment the next line to test the Pwd dialog procedure
        # Do not do in py.test (unless you want to interact!)
        # ## do_pwd()
        # Uncomment the next line to test the Rng dialog procedure
        # Do not do in py.test (unless you want to interact!)
        # ##do_rng_prompt()
    reset_start_dir()
    quick_version()
    print("__version__=", pki.__version__)
    print("ALL DONE.")


if __name__ == "__main__":
    main()