From 514474b98ccdfa6c0f76576c0ebfbec2af637d81 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Tue, 20 Oct 2015 16:19:11 -0400 Subject: Fix botan.py for Python3 Remove any need for callers to do version checks or encode values specially to handle Python2 vs Python3 ctypes differences. API users shouldn't have to care about that - encapsulate the differences in a few functions for handling the conversions. Add botan_cipher_query_keylen to ffi --- src/python/botan.py | 500 +++++++++++++++++++++++++++------------------------- 1 file changed, 255 insertions(+), 245 deletions(-) (limited to 'src/python') diff --git a/src/python/botan.py b/src/python/botan.py index 04e574746..f15d892c6 100755 --- a/src/python/botan.py +++ b/src/python/botan.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python """ Python wrapper of the botan crypto library @@ -14,7 +14,6 @@ import sys from ctypes import * from binascii import hexlify, unhexlify - """ Module initialization """ @@ -23,14 +22,14 @@ if sys.platform == 'darwin': else: botan = CDLL('libbotan-1.11.so') -expected_api_rev = 20150210 +expected_api_rev = 20151015 botan_api_rev = botan.botan_ffi_api_version() if botan_api_rev != expected_api_rev: raise Exception("Bad botan API rev got %d expected %d" % (botan_api_rev, expected_api_rev)) # Internal utilities -def _call_fn_returning_string(guess, fn): +def _call_fn_returning_vec(guess, fn): buf = create_string_buffer(guess) buf_len = c_size_t(len(buf)) @@ -39,29 +38,49 @@ def _call_fn_returning_string(guess, fn): if rc < 0: if buf_len.value > len(buf): #print("Calling again with %d" % (buf_len.value)) - return _call_fn_returning_string(buf_len.value, fn) + return _call_fn_returning_vec(buf_len.value, fn) else: raise Exception("Call failed: %d" % (rc)) assert buf_len.value <= len(buf) - return str(buf.raw[0:buf_len.value]) - -def _call_fn_returning_vec(guess, fn): + return buf.raw[0:buf_len.value] - buf = create_string_buffer(guess) - buf_len = c_size_t(len(buf)) +def _call_fn_returning_string(guess, fn): + # Assumes that anything called with this is returning plain ASCII strings + # (base64 data, algorithm names, etc) + v = _call_fn_returning_vec(guess, fn) + return v.decode('ascii')[:-1] + +def _ctype_str(s): + assert(type(s) == type("")) + if sys.version_info[0] < 3: + return s + else: + return s.encode('utf-8') - rc = fn(buf, byref(buf_len)) - if rc < 0: - if buf_len.value > len(buf): - #print("Calling again with %d" % (buf_len.value)) - return _call_fn_returning_vec(buf_len.value, fn) +def _ctype_bits(s): + # TODO typecheck for bytes in python3? + if sys.version_info[0] < 3: + return s + else: + if isinstance(s, bytes): + return s + elif isinstance(s, str): + return s.encode('utf-8') # FIXME else: - raise Exception("Call failed: %d" % (rc)) + assert False - assert buf_len.value <= len(buf) - return buf.raw[0:buf_len.value] +def _ctype_bufout(buf): + if sys.version_info[0] < 3: + return str(buf.raw) + else: + return buf.raw + +def hex_encode(buf): + return hexlify(buf).decode('ascii') +def hex_decode(buf): + return unhexlify(buf) """ Versions @@ -77,7 +96,7 @@ def version_patch(): def version_string(): botan.botan_version_string.restype = c_char_p - return botan.botan_version_string() + return botan.botan_version_string().decode('ascii') """ RNG @@ -87,11 +106,7 @@ class rng(object): def __init__(self, rng_type = 'system'): botan.botan_rng_init.argtypes = [c_void_p, c_char_p] self.rng = c_void_p(0) - if sys.version_info[0] < 3: - rc = botan.botan_rng_init(byref(self.rng), rng_type) - else: - rc = botan.botan_rng_init(byref(self.rng), rng_type.encode('ascii')) - + rc = botan.botan_rng_init(byref(self.rng), _ctype_str(rng_type)) if rc != 0 or self.rng is None: raise Exception("No rng " + algo + " for you!") @@ -108,10 +123,7 @@ class rng(object): out = create_string_buffer(length) l = c_size_t(length) rc = botan.botan_rng_get(self.rng, out, l) - if sys.version_info[0] < 3: - return str(out.raw) - else: - return out.raw + return _ctype_bufout(out) """ Hash function @@ -121,10 +133,7 @@ class hash_function(object): botan.botan_hash_init.argtypes = [c_void_p, c_char_p, c_uint32] flags = c_uint32(0) # always zero in this API version self.hash = c_void_p(0) - if sys.version_info[0] < 3: - rc = botan.botan_hash_init(byref(self.hash), algo, flags) - else: - rc = botan.botan_hash_init(byref(self.hash), algo.encode('utf-8'), flags) + rc = botan.botan_hash_init(byref(self.hash), _ctype_str(algo), flags) if rc != 0 or self.hash is None: raise Exception("No hash " + algo + " for you!") @@ -144,16 +153,13 @@ class hash_function(object): def update(self, x): botan.botan_hash_update.argtypes = [c_void_p, POINTER(c_char), c_size_t] - botan.botan_hash_update(self.hash, x, len(x)) + botan.botan_hash_update(self.hash, _ctype_bits(x), len(x)) def final(self): botan.botan_hash_final.argtypes = [c_void_p, POINTER(c_char)] out = create_string_buffer(self.output_length()) botan.botan_hash_final(self.hash, out) - if sys.version_info[0] < 3: - return str(out.raw) - else: - return out.raw + return _ctype_bufout(out) """ Message authentication codes @@ -163,7 +169,7 @@ class message_authentication_code(object): botan.botan_mac_init.argtypes = [c_void_p, c_char_p, c_uint32] flags = c_uint32(0) # always zero in this API version self.mac = c_void_p(0) - rc = botan.botan_mac_init(byref(self.mac), algo, flags) + rc = botan.botan_mac_init(byref(self.mac), _ctype_str(algo), flags) if rc != 0 or self.mac is None: raise Exception("No mac " + algo + " for you!") @@ -193,20 +199,14 @@ class message_authentication_code(object): botan.botan_mac_final.argtypes = [c_void_p, POINTER(c_char)] out = create_string_buffer(self.output_length()) botan.botan_mac_final(self.mac, out) - if sys.version_info[0] < 3: - return str(out.raw) - else: - return out.raw + return _ctype_bufout(out) class cipher(object): def __init__(self, algo, encrypt = True): botan.botan_cipher_init.argtypes = [c_void_p,c_char_p, c_uint32] flags = 0 if encrypt else 1 self.cipher = c_void_p(0) - if sys.version_info[0] < 3: - rc = botan.botan_cipher_init(byref(self.cipher), algo, flags) - else: - rc = botan.botan_cipher_init(byref(self.cipher), algo.encode('utf-8'), flags) + rc = botan.botan_cipher_init(byref(self.cipher), _ctype_str(algo), flags) if rc != 0 or self.cipher is None: raise Exception("No cipher " + algo + " for you!") @@ -226,6 +226,12 @@ class cipher(object): botan.botan_cipher_get_update_granularity(self.cipher, byref(l)) return l.value + def key_length(self): + kmin = c_size_t(0) + kmax = c_size_t(0) + botan.botan_cipher_query_keylen(self.cipher, byref(kmin), byref(kmax)) + return kmin.value, kmax.value + def tag_length(self): botan.botan_cipher_get_tag_length.argtypes = [c_void_p, POINTER(c_size_t)] l = c_size_t(0) @@ -265,8 +271,6 @@ class cipher(object): inp = txt if txt else '' inp_sz = c_size_t(len(inp)) - if sys.version_info[0] >= 3: - inp = cast(inp, c_char_p) inp_consumed = c_size_t(0) out = create_string_buffer(inp_sz.value + (self.tag_length() if final else 0)) out_sz = c_size_t(len(out)) @@ -275,7 +279,7 @@ class cipher(object): botan.botan_cipher_update(self.cipher, flags, out, out_sz, byref(out_written), - inp, inp_sz, byref(inp_consumed)) + cast(inp, c_char_p), inp_sz, byref(inp_consumed)) # buffering not supported yet assert inp_consumed.value == inp_sz.value @@ -315,7 +319,7 @@ PBKDF def pbkdf(algo, password, out_len, iterations = 10000, salt = rng().get(12)): botan.botan_pbkdf.argtypes = [c_char_p, POINTER(c_char), c_size_t, c_char_p, c_void_p, c_size_t, c_size_t] out_buf = create_string_buffer(out_len) - botan.botan_pbkdf(algo, out_buf, out_len, password, salt, len(salt), iterations) + botan.botan_pbkdf(_ctype_str(algo), out_buf, out_len, _ctype_str(password), salt, len(salt), iterations) return (salt,iterations,out_buf.raw) def pbkdf_timed(algo, password, out_len, ms_to_run = 300, salt = rng().get(12)): @@ -323,7 +327,7 @@ def pbkdf_timed(algo, password, out_len, ms_to_run = 300, salt = rng().get(12)): c_void_p, c_size_t, c_size_t, POINTER(c_size_t)] out_buf = create_string_buffer(out_len) iterations = c_size_t(0) - botan.botan_pbkdf_timed(algo, out_buf, out_len, password, salt, len(salt), ms_to_run, byref(iterations)) + botan.botan_pbkdf_timed(_ctype_str(algo), out_buf, out_len, _ctype_str(password), salt, len(salt), ms_to_run, byref(iterations)) return (salt,iterations.value,out_buf.raw) """ @@ -333,7 +337,7 @@ def kdf(algo, secret, out_len, salt): botan.botan_kdf.argtypes = [c_char_p, POINTER(c_char), c_size_t, POINTER(c_char), c_size_t, POINTER(c_char), c_size_t] out_buf = create_string_buffer(out_len) out_sz = c_size_t(out_len) - botan.botan_kdf(algo, out_buf, out_sz, secret, len(secret), salt, len(salt)) + botan.botan_kdf(_ctype_str(algo), out_buf, out_sz, secret, len(secret), salt, len(salt)) return out_buf.raw[0:out_sz.value] """ @@ -359,13 +363,8 @@ class public_key(object): def encoding(self, pem = False): botan.botan_pubkey_export.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t), c_uint32] - flag = 1 if pem else 0 - - if pem: - return _call_fn_returning_string(0, lambda b,bl: botan.botan_pubkey_export(self.pubkey, b, bl, 1)) - else: - return _call_fn_returning_string(0, lambda b,bl: botan.botan_pubkey_export(self.pubkey, b, bl, 0)) + return _call_fn_returning_vec(0, lambda b,bl: botan.botan_pubkey_export(self.pubkey, b, bl, flag)) def fingerprint(self, hash = 'SHA-256'): botan.botan_pubkey_fingerprint.argtypes = [c_void_p, c_char_p, @@ -374,11 +373,9 @@ class public_key(object): n = hash_function(hash).output_length() buf = create_string_buffer(n) buf_len = c_size_t(n) - if sys.version_info[0] > 2: - hash = hash.encode('utf-8') - botan.botan_pubkey_fingerprint(self.pubkey, hash, buf, byref(buf_len)) - return hexlify(buf[0:buf_len.value]) + botan.botan_pubkey_fingerprint(self.pubkey, _ctype_str(hash), buf, byref(buf_len)) + return hex_encode(buf[0:buf_len.value]) class private_key(object): def __init__(self, alg, param, rng): @@ -388,12 +385,13 @@ class private_key(object): botan.botan_privkey_create_mceliece.argtypes = [c_void_p, c_void_p, c_size_t, c_size_t] self.privkey = c_void_p(0) + if alg == 'rsa': botan.botan_privkey_create_rsa(byref(self.privkey), rng.rng, param) elif alg == 'ecdsa': - botan.botan_privkey_create_ecdsa(byref(self.privkey), rng.rng, param) + botan.botan_privkey_create_ecdsa(byref(self.privkey), rng.rng, _ctype_str(param)) elif alg == 'ecdh': - botan.botan_privkey_create_ecdh(byref(self.privkey), rng.rng, param) + botan.botan_privkey_create_ecdh(byref(self.privkey), rng.rng, _ctype_str(param)) elif alg in ['mce', 'mceliece']: botan.botan_privkey_create_mceliece(byref(self.privkey), rng.rng, param[0], param[1]) else: @@ -431,9 +429,8 @@ class pk_op_encrypt(object): botan.botan_pk_op_encrypt_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32] self.op = c_void_p(0) flags = c_uint32(0) # always zero in this ABI - if sys.version_info[0] > 2: - padding = cast(padding, c_char_p) - botan.botan_pk_op_encrypt_create(byref(self.op), key.pubkey, padding, flags) + print("Padding is ", padding) + botan.botan_pk_op_encrypt_create(byref(self.op), key.pubkey, _ctype_str(padding), flags) if not self.op: raise Exception("No pk op for you") @@ -450,9 +447,9 @@ class pk_op_encrypt(object): outbuf = create_string_buffer(outbuf_sz.value) ll = len(msg) #print("encrypt: len=%d" % ll) - if sys.version_info[0] > 2: - msg = cast(msg, c_char_p) - ll = c_size_t(ll) + #if sys.version_info[0] > 2: + # msg = cast(msg, c_char_p) + # ll = c_size_t(ll) botan.botan_pk_op_encrypt(self.op, rng.rng, outbuf, byref(outbuf_sz), msg, ll) #print("encrypt: outbuf_sz.value=%d" % outbuf_sz.value) return outbuf.raw[0:outbuf_sz.value] @@ -463,9 +460,7 @@ class pk_op_decrypt(object): botan.botan_pk_op_decrypt_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32] self.op = c_void_p(0) flags = c_uint32(0) # always zero in this ABI - if sys.version_info[0] > 2: - padding = cast(padding, c_char_p) - botan.botan_pk_op_decrypt_create(byref(self.op), key.privkey, padding, flags) + botan.botan_pk_op_decrypt_create(byref(self.op), key.privkey, _ctype_str(padding), flags) if not self.op: raise Exception("No pk op for you") @@ -481,10 +476,7 @@ class pk_op_decrypt(object): outbuf_sz = c_size_t(4096) #?!?! outbuf = create_string_buffer(outbuf_sz.value) ll = len(msg) - if sys.version_info[0] > 2: - msg = cast(msg, c_char_p) - ll = c_size_t(ll) - botan.botan_pk_op_decrypt(self.op, outbuf, byref(outbuf_sz), msg, ll) + botan.botan_pk_op_decrypt(self.op, outbuf, byref(outbuf_sz), _ctype_bits(msg), ll) return outbuf.raw[0:outbuf_sz.value] class pk_op_sign(object): @@ -492,9 +484,7 @@ class pk_op_sign(object): botan.botan_pk_op_sign_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32] self.op = c_void_p(0) flags = c_uint32(0) # always zero in this ABI - if sys.version_info[0] > 2: - padding = cast(padding, c_char_p) - botan.botan_pk_op_sign_create(byref(self.op), key.privkey, padding, flags) + botan.botan_pk_op_sign_create(byref(self.op), key.privkey, _ctype_str(padding), flags) if not self.op: raise Exception("No pk op for you") @@ -504,7 +494,7 @@ class pk_op_sign(object): def update(self, msg): botan.botan_pk_op_sign_update.argtypes = [c_void_p, POINTER(c_char), c_size_t] - botan.botan_pk_op_sign_update(self.op, msg, len(msg)) + botan.botan_pk_op_sign_update(self.op, _ctype_str(msg), len(msg)) def finish(self, rng): botan.botan_pk_op_sign_finish.argtypes = [c_void_p, c_void_p, POINTER(c_char), POINTER(c_size_t)] @@ -518,7 +508,7 @@ class pk_op_verify(object): botan.botan_pk_op_verify_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32] self.op = c_void_p(0) flags = c_uint32(0) # always zero in this ABI - botan.botan_pk_op_verify_create(byref(self.op), key.pubkey, padding, flags) + botan.botan_pk_op_verify_create(byref(self.op), key.pubkey, _ctype_str(padding), flags) if not self.op: raise Exception("No pk op for you") @@ -528,11 +518,11 @@ class pk_op_verify(object): def update(self, msg): botan.botan_pk_op_verify_update.argtypes = [c_void_p, POINTER(c_char), c_size_t] - botan.botan_pk_op_verify_update(self.op, msg, len(msg)) + botan.botan_pk_op_verify_update(self.op, _ctype_bits(msg), len(msg)) def check_signature(self, signature): botan.botan_pk_op_verify_finish.argtypes = [c_void_p, POINTER(c_char), c_size_t] - rc = botan.botan_pk_op_verify_finish(self.op, signature, len(signature)) + rc = botan.botan_pk_op_verify_finish(self.op, _ctype_bits(signature), len(signature)) if rc == 0: return True return False @@ -545,15 +535,31 @@ def mceies_encrypt(mce, rng, aead, pt, ad): botan.botan_mceies_encrypt.argtypes = [c_void_p, c_void_p, c_char_p, POINTER(c_char), c_size_t, POINTER(c_char), c_size_t, POINTER(c_char), POINTER(c_size_t)] - return _call_fn_returning_string(0, lambda b,bl: - botan.botan_mceies_encrypt(mce.pubkey, rng.rng, aead, pt, len(pt), ad, len(ad), b, bl)) + return _call_fn_returning_vec(0, lambda b,bl: + botan.botan_mceies_encrypt(mce.pubkey, + rng.rng, + _ctype_str(aead), + _ctype_bits(pt), + len(pt), + _ctype_bits(ad), + len(ad), + b, bl)) def mceies_decrypt(mce, aead, pt, ad): botan.botan_mceies_decrypt.argtypes = [c_void_p, c_char_p, POINTER(c_char), c_size_t, POINTER(c_char), c_size_t, POINTER(c_char), POINTER(c_size_t)] - return _call_fn_returning_string(0, lambda b,bl: - botan.botan_mceies_decrypt(mce.privkey, aead, pt, len(pt), ad, len(ad), b, bl)) + #msg = cast(msg, c_char_p) + #ll = c_size_t(ll) + + return _call_fn_returning_vec(0, lambda b,bl: + botan.botan_mceies_decrypt(mce.privkey, + _ctype_str(aead), + _ctype_bits(pt), + len(pt), + _ctype_bits(ad), + len(ad), + b, bl)) class pk_op_key_agreement(object): def __init__(self, key, kdf): @@ -565,7 +571,7 @@ class pk_op_key_agreement(object): if not self.op: raise Exception("No key agreement for you") - self.m_public_value = _call_fn_returning_string(0, lambda b, bl: botan.botan_pk_op_key_agreement_export_public(key.privkey, b, bl)) + self.m_public_value = _call_fn_returning_vec(0, lambda b, bl: botan.botan_pk_op_key_agreement_export_public(key.privkey, b, bl)) def __del__(self): botan.botan_pk_op_key_agreement_destroy.argtypes = [c_void_p] @@ -578,18 +584,16 @@ class pk_op_key_agreement(object): botan.botan_pk_op_key_agreement.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t), POINTER(c_char), c_size_t, POINTER(c_char), c_size_t] - return _call_fn_returning_string(key_len, - lambda b,bl: botan.botan_pk_op_key_agreement(self.op, b, bl, - other, len(other), - salt, len(salt))) + return _call_fn_returning_vec(key_len, + lambda b,bl: botan.botan_pk_op_key_agreement(self.op, b, bl, + other, len(other), + salt, len(salt))) class x509_cert(object): def __init__(self, filename): botan.botan_x509_cert_load_file.argtypes = [POINTER(c_void_p), c_char_p] self.x509_cert = c_void_p(0) - if sys.version_info[0] > 2: - filename = cast(filename, c_char_p) - botan.botan_x509_cert_load_file(byref(self.x509_cert), filename) + botan.botan_x509_cert_load_file(byref(self.x509_cert), _ctype_str(filename)) def __del__(self): botan.botan_x509_cert_destroy.argtypes = [c_void_p] @@ -613,10 +617,7 @@ class x509_cert(object): POINTER(c_char), POINTER(c_size_t)] n = hash_function(hash_algo).output_length() * 3 - if sys.version_info[0] > 2: - hash_algo = hash_algo.encode('utf-8') - - return _call_fn_returning_string(n, lambda b,bl: botan.botan_x509_cert_get_fingerprint(self.x509_cert, hash_algo, b, bl)) + return _call_fn_returning_string(n, lambda b,bl: botan.botan_x509_cert_get_fingerprint(self.x509_cert, _ctype_str(hash_algo), b, bl)) def serial_number(self): botan.botan_x509_cert_get_serial_number.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)] @@ -640,199 +641,208 @@ Tests and examples """ def test(): - r = rng("user") + def test_version(): + print("\n%s" % version_string()) + print("v%d.%d.%d\n" % (version_major(), version_minor(), version_patch())) - print("\n%s" % version_string().decode('utf-8')) - print("v%d.%d.%d\n" % (version_major(), version_minor(), version_patch())) + def test_kdf(): + print("KDF2(SHA-1) %s" % + hex_encode(kdf('KDF2(SHA-1)', hex_decode('701F3480DFE95F57941F804B1B2413EF'), 7, + hex_decode('55A4E9DD5F4CA2EF82')))) - print("KDF2(SHA-1) %s" % - hexlify(kdf('KDF2(SHA-1)'.encode('ascii'), unhexlify('701F3480DFE95F57941F804B1B2413EF'), 7, - unhexlify('55A4E9DD5F4CA2EF82')) - ).decode('ascii') - ) + def test_pbkdf(): + print("PBKDF2(SHA-1) %s" % + hex_encode(pbkdf('PBKDF2(SHA-1)', '', 32, 10000, hex_decode('0001020304050607'))[2])) + print("good output %s\n" % + '59B2B1143B4CB1059EC58D9722FB1C72471E0D85C6F7543BA5228526375B0127') - print("PBKDF2(SHA-1) %s" % - hexlify(pbkdf('PBKDF2(SHA-1)'.encode('ascii'), ''.encode('ascii'), 32, 10000, - unhexlify('0001020304050607')) - [2] - ).upper().decode('ascii')) - print("good output %s\n" % - '59B2B1143B4CB1059EC58D9722FB1C72471E0D85C6F7543BA5228526375B0127') + (salt,iterations,psk) = pbkdf_timed('PBKDF2(SHA-256)', 'xyz', 32, 200) - (salt,iterations,psk) = pbkdf_timed('PBKDF2(SHA-256)'.encode('ascii'), - 'xyz'.encode('utf-8'), 32, 200) + print("PBKDF2(SHA-256) x=timed, y=iterated; salt = %s (len=%d) #iterations = %d\n" % + (hex_encode(salt), len(salt), iterations)) - print("PBKDF2(SHA-256) x=timed, y=iterated; salt = %s (len=%d) #iterations = %d\n" % - (hexlify(salt).decode('ascii'), len(salt), iterations)) + print('x %s' % hex_encode(psk)) + print('y %s\n' % (hex_encode(pbkdf('PBKDF2(SHA-256)', 'xyz', 32, iterations, salt)[2]))) - print('x %s' % hexlify(psk).decode('utf-8')) - print('y %s\n' % - (hexlify(pbkdf('PBKDF2(SHA-256)'.encode('utf-8'), - 'xyz'.encode('ascii'), 32, iterations, salt)[2]).decode('utf-8'))) + def test_hmac(): - hmac = message_authentication_code('HMAC(SHA-256)'.encode('ascii')) - hmac.set_key(unhexlify('0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20')) - hmac.update(unhexlify('616263')) + hmac = message_authentication_code('HMAC(SHA-256)') + hmac.set_key(hex_decode('0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20')) + hmac.update(hex_decode('616263')) - hmac_output = hmac.final() + hmac_vec = hex_decode('A21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181') + hmac_output = hmac.final() - if hmac_output != unhexlify('A21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181'): - print("Bad HMAC:\t%s" % hexlify(bytes(hmac_output, 'utf-8')).decode('utf-8')) - print("vs good: \tA21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181"); - else: - print("HMAC output (good): %s\n" % hexlify(hmac_output).decode('utf-8')) + if hmac_output != hmac_vec: + print("Bad HMAC:\t%s" % hex_encode(hmac_output)) + print("vs good: \t%s" % hex_encode(hmac_vec)) + else: + print("HMAC output correct: %s\n" % hex_encode(hmac_output)) + + def test_rng(): + user_rng = rng("user") - print("rng output:\n\t%s\n\t%s\n\t%s\n" % - (hexlify(r.get(42)).decode('utf-8'), - hexlify(r.get(13)).decode('utf-8'), - hexlify(r.get(9)).decode('utf-8') - ) - ) + print("rng output:\n\t%s\n\t%s\n\t%s\n" % + (hex_encode(user_rng.get(42)), + hex_encode(user_rng.get(13)), + hex_encode(user_rng.get(9)))) - h = hash_function('MD5') - assert h.output_length() == 16 - h.update('h'.encode('utf-8')) - h.update('i'.encode('utf-8')) - print("md5 hash: %s\n" % (hexlify(h.final())).decode('utf-8')) + def test_hash(): + md5 = hash_function('MD5') + assert md5.output_length() == 16 + md5.update('h') + md5.update('i') + h1 = md5.final() + print("md5 hash: %s (%s)\n" % (hex_encode(h1), '49f68a5c8493ec2c0bf489821c21fc3b')) + md5.update(hex_decode('f468025b')) + h2 = md5.final() + print("md5 hash: %s (%s)\n" % (hex_encode(h2), '47efd2be302a937775e93dea281b6751')) - gcm = cipher('AES-128/GCM') - print("AES-128/GCM: default nonce=%d update_size=%d" % - (gcm.default_nonce_length(), gcm.update_granularity())) - gcm_dec = cipher('AES-128/GCM', encrypt=False) + def test_cipher(): + for mode in ['AES-128/CTR-BE', 'Serpent/GCM', 'ChaCha20Poly1305']: + enc = cipher(mode, encrypt=True) - iv = r.get(12) - key = r.get(16) - pt = r.get(21) - gcm.set_key(key) - gcm.start(iv) - assert len(gcm.update('')) == 0 - ct = gcm.finish(pt) - print("GCM ct %s" % hexlify(ct).decode('utf-8')) + (kmin,kmax) = enc.key_length() + print("%s: default nonce=%d update_size=%d key_min=%d key_max=%d" % + (mode, enc.default_nonce_length(), enc.update_granularity(), kmin, kmax)) + iv = rng().get(enc.default_nonce_length()) + key = rng().get(kmax) + pt = rng().get(21) - gcm_dec.set_key(key) - gcm_dec.start(iv) - dec = gcm_dec.finish(ct) - print("GCM pt %s %d" % (hexlify(pt).decode('utf-8'), len(pt))) - print("GCM de %s %d\n" % (hexlify(dec).decode('utf-8'), len(dec))) + print(" plaintext %s %d" % (hex_encode(pt), len(pt))) - ocb = cipher('AES-128/OCB') - print("AES-128/OCB: default nonce=%d update_size=%d" % - (ocb.default_nonce_length(), ocb.update_granularity())) - ocb_dec = cipher('AES-128/OCB', encrypt=False) + enc.set_key(key) + enc.start(iv) + assert len(enc.update('')) == 0 + ct = enc.finish(pt) + print(" ciphertext %s %d" % (hex_encode(ct), len(ct))) - iv = r.get(12) - key = r.get(16) - pt = r.get(21) - ocb.set_key(key) - ocb.start(iv) - assert len(ocb.update('')) == 0 - ct = ocb.finish(pt) - print("OCB ct %s" % hexlify(ct).decode('utf-8')) + dec = cipher(mode, encrypt=False) + dec.set_key(key) + dec.start(iv) + decrypted = dec.finish(ct) - ocb_dec.set_key(key) - ocb_dec.start(iv) - dec = ocb_dec.finish(ct) - print("OCB pt %s %d" % (hexlify(pt).decode('utf-8'), len(pt))) - print("OCB de %s %d\n" % (hexlify(dec).decode('utf-8'), len(dec))) + print(" decrypted %s %d\n" % (hex_encode(decrypted), len(decrypted))) - mce_priv = private_key('mce', [2960,57], r) - mce_pub = mce_priv.get_public_key() + def test_mceliece(): + mce_priv = private_key('mce', [2960,57], rng()) + mce_pub = mce_priv.get_public_key() - mce_plaintext = 'mce plaintext' - mce_ad = 'mce AD' - mce_ciphertext = mceies_encrypt(mce_pub, r, 'ChaCha20Poly1305', mce_plaintext, mce_ad) + mce_plaintext = 'mce plaintext' + mce_ad = 'mce AD' + mce_ciphertext = mceies_encrypt(mce_pub, rng(), 'ChaCha20Poly1305', mce_plaintext, mce_ad) - print "mce", len(mce_plaintext), len(mce_ciphertext) + print("mce", len(mce_plaintext), len(mce_ciphertext)) - mce_decrypt = mceies_decrypt(mce_priv, 'ChaCha20Poly1305', mce_ciphertext, mce_ad) + mce_decrypt = mceies_decrypt(mce_priv, 'ChaCha20Poly1305', mce_ciphertext, mce_ad) - print("mce_pub %s/SHA-1 fingerprint: %s (estimated strength %s) (len %d)" % - (mce_pub.algo_name().decode('utf-8'), mce_pub.fingerprint("SHA-1").decode('utf-8'), - mce_pub.estimated_strength(), len(mce_pub.encoding()) - ) - ) + print("mce_pub %s/SHA-1 fingerprint: %s (estimated strength %s) (len %d)" % + (mce_pub.algo_name(), mce_pub.fingerprint("SHA-1"), + mce_pub.estimated_strength(), len(mce_pub.encoding()) + ) + ) - rsapriv = private_key('rsa', 1536, r) + def test_rsa(): + rsapriv = private_key('rsa', 1536, rng()) - rsapub = rsapriv.get_public_key() + rsapub = rsapriv.get_public_key() - print("rsapub %s SHA-1 fingerprint: %s estimated strength %d len %d" % - (rsapub.algo_name().decode('utf-8'), rsapub.fingerprint("SHA-1").decode('utf-8'), - rsapub.estimated_strength(), len(rsapub.encoding()) - ) - ) + print("rsapub %s SHA-1 fingerprint: %s estimated strength %d len %d" % + (rsapub.algo_name(), rsapub.fingerprint("SHA-1"), + rsapub.estimated_strength(), len(rsapub.encoding()) + ) + ) - dec = pk_op_decrypt(rsapriv, "EME1(SHA-256)".encode('utf-8')) - enc = pk_op_encrypt(rsapub, "EME1(SHA-256)".encode('utf-8')) + dec = pk_op_decrypt(rsapriv, "EME1(SHA-256)") + enc = pk_op_encrypt(rsapub, "EME1(SHA-256)") - ctext = enc.encrypt('foof'.encode('utf-8'), r) - print("ptext \'%s\'" % 'foof') - print("ctext \'%s\'" % hexlify(ctext).decode('utf-8')) - print("decrypt \'%s\'\n" % dec.decrypt(ctext).decode('utf-8')) + sys_rng = rng() + symkey = sys_rng.get(32) + ctext = enc.encrypt(symkey, sys_rng) + print("ptext \'%s\'" % hex_encode(symkey)) + print("ctext \'%s\'" % hex_encode(ctext)) + print("decrypt \'%s\'\n" % hex_encode(dec.decrypt(ctext))) - signer = pk_op_sign(rsapriv, 'EMSA4(SHA-384)'.encode('utf-8')) + signer = pk_op_sign(rsapriv, 'EMSA4(SHA-384)') - signer.update('messa'.encode('utf-8')) - signer.update('ge'.encode('utf-8')) - sig = signer.finish(r) + signer.update('messa') + signer.update('ge') + sig = signer.finish(rng()) - r.reseed(200) - print("EMSA4(SHA-384) signature: %s" % hexlify(sig).decode('utf-8')) + print("EMSA4(SHA-384) signature: %s" % hex_encode(sig)) + verify = pk_op_verify(rsapub, 'EMSA4(SHA-384)') - verify = pk_op_verify(rsapub, 'EMSA4(SHA-384)'.encode('utf-8')) + verify.update('mess') + verify.update('age') + print("good sig accepted? %s" % verify.check_signature(sig)) - verify.update('mess'.encode('utf-8')) - verify.update('age'.encode('utf-8')) - print("good sig accepted? %s" % verify.check_signature(sig)) + verify.update('mess of things') + verify.update('age') + print("bad sig accepted? %s" % verify.check_signature(sig)) + + verify.update('message') + print("good sig accepted? %s\n" % verify.check_signature(sig)) - verify.update('mess of things'.encode('utf-8')) - verify.update('age'.encode('utf-8')) - print("bad sig accepted? %s" % verify.check_signature(sig)) + def test_dh(): + a_rng = rng('user') + b_rng = rng('user') - verify.update('message'.encode('utf-8')) - print("good sig accepted? %s\n" % verify.check_signature(sig)) + for dh_grp in ['secp256r1', 'curve25519']: + dh_kdf = 'KDF2(SHA-384)'.encode('utf-8') + a_dh_priv = private_key('ecdh', dh_grp, rng()) + a_dh_pub = a_dh_priv.get_public_key() - for dh_grps in ['secp256r1', 'curve25519']: - dh_grp = dh_grps.encode('utf-8') - dh_kdf = 'KDF2(SHA-384)'.encode('utf-8') - a_dh_priv = private_key('ecdh', dh_grp, r) - a_dh_pub = a_dh_priv.get_public_key() + b_dh_priv = private_key('ecdh', dh_grp, rng()) + b_dh_pub = b_dh_priv.get_public_key() - b_dh_priv = private_key('ecdh', dh_grp, r) - b_dh_pub = b_dh_priv.get_public_key() + a_dh = pk_op_key_agreement(a_dh_priv, dh_kdf) + b_dh = pk_op_key_agreement(b_dh_priv, dh_kdf) - a_dh = pk_op_key_agreement(a_dh_priv, dh_kdf) - b_dh = pk_op_key_agreement(b_dh_priv, dh_kdf) + a_salt = a_rng.get(8) + b_salt = b_rng.get(8) - print("ecdh %s pubs:\n %s\n %s\n" % - (dh_grps, - hexlify(a_dh.public_value()).decode('utf-8'), - hexlify(b_dh.public_value()).decode('utf-8'))) + print("ecdh %s pubs:\n %s (salt %s)\n %s (salt %s)\n" % + (dh_grp, + hex_encode(a_dh.public_value()), + hex_encode(a_salt), + hex_encode(b_dh.public_value()), + hex_encode(b_salt))) - a_key = a_dh.agree(b_dh.public_value(), 20, 'salt'.encode('utf-8')) - b_key = b_dh.agree(a_dh.public_value(), 20, 'salt'.encode('utf-8')) + a_key = a_dh.agree(b_dh.public_value(), 32, a_salt + b_salt) + b_key = b_dh.agree(a_dh.public_value(), 32, a_salt + b_salt) - print("ecdh %s shared:\n %s\n %s\n" % - (dh_grps, hexlify(a_key).decode('utf-8'), hexlify(b_key).decode('utf-8'))) + print("ecdh %s shared:\n %s\n %s\n" % + (dh_grp, hex_encode(a_key), hex_encode(b_key))) - cert = x509_cert("src/tests/data/ecc/CSCA.CSCA.csca-germany.1.crt") - print(cert.fingerprint("SHA-1")) - print("32:42:1C:C3:EC:54:D7:E9:43:EC:51:F0:19:23:BD:85:1D:F2:1B:B9") + def test_certs(): + cert = x509_cert("src/tests/data/ecc/CSCA.CSCA.csca-germany.1.crt") + print(cert.fingerprint("SHA-1")) + print("32:42:1C:C3:EC:54:D7:E9:43:EC:51:F0:19:23:BD:85:1D:F2:1B:B9") - print(cert.time_starts()) - print(cert.time_expires()) + print(cert.time_starts()) + print(cert.time_expires()) - print(hexlify(cert.serial_number())) - print(hexlify(cert.authority_key_id())) - print(hexlify(cert.subject_key_id())) - print(hexlify(cert.subject_public_key_bits())) + print(hex_encode(cert.serial_number())) + print(hex_encode(cert.authority_key_id())) + print(hex_encode(cert.subject_key_id())) + print(hex_encode(cert.subject_public_key_bits())) - print(cert.to_string()) + print(cert.to_string()) - return + test_version() + test_kdf() + test_pbkdf() + test_hmac() + test_rng() + test_hash() + test_cipher() + test_mceliece() + test_rsa() + test_dh() + test_certs() def main(args = None): -- cgit v1.2.3