aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2018-08-11 19:02:32 -0400
committerJack Lloyd <[email protected]>2018-08-11 19:02:32 -0400
commitc6358e15568b0ce43006cd987354e92e44efa6b2 (patch)
treec32e9c16277325ae78b774ba6b10a9c79faa28cb
parent2c10ec625d799d513a56ec93f8117999143be244 (diff)
parent24963a27f6f5e2235f00ef7463f039bceac548b3 (diff)
Merge GH #1643 Refresh of Python module
-rw-r--r--src/lib/ffi/ffi.h46
-rwxr-xr-xsrc/python/botan2.py651
-rw-r--r--src/scripts/test_python.py286
3 files changed, 588 insertions, 395 deletions
diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h
index 080c71cef..fe847e94a 100644
--- a/src/lib/ffi/ffi.h
+++ b/src/lib/ffi/ffi.h
@@ -88,7 +88,7 @@ enum BOTAN_FFI_ERROR {
* Convert an error code into a string. Returns "Unknown error"
* if the error code is not a known one.
*/
-const char* botan_error_description(int err);
+BOTAN_PUBLIC_API(2,8) const char* botan_error_description(int err);
/**
* Return the version of the currently supported FFI API. This is
@@ -532,25 +532,6 @@ BOTAN_PUBLIC_API(2,0) int botan_kdf(const char* kdf_algo,
const uint8_t salt[], size_t salt_len,
const uint8_t label[], size_t label_len);
-/**
-* Create a password hash using Bcrypt
-* @param out buffer holding the password hash, should be of length 64 bytes
-* @param out_len the desired output length in bytes
-* @param password the password
-* @param rng a random number generator
-* @param work_factor how much work to do to slow down guessing attacks
-* @param flags should be 0 in current API revision, all other uses are reserved
-* and return BOTAN_FFI_ERROR_BAD_FLAG
-* @return 0 on success, a negative value on failure
-
-* Output is formatted bcrypt $2a$...
-*/
-BOTAN_PUBLIC_API(2,0) int botan_bcrypt_generate(uint8_t* out, size_t* out_len,
- const char* password,
- botan_rng_t rng,
- size_t work_factor,
- uint32_t flags);
-
/*
* Raw Block Cipher (PRP) interface
*/
@@ -736,6 +717,25 @@ BOTAN_PUBLIC_API(2,1) int botan_mp_clear_bit(botan_mp_t n, size_t bit);
/* Bcrypt password hashing */
/**
+* Create a password hash using Bcrypt
+* @param out buffer holding the password hash, should be of length 64 bytes
+* @param out_len the desired output length in bytes
+* @param password the password
+* @param rng a random number generator
+* @param work_factor how much work to do to slow down guessing attacks
+* @param flags should be 0 in current API revision, all other uses are reserved
+* and return BOTAN_FFI_ERROR_BAD_FLAG
+* @return 0 on success, a negative value on failure
+
+* Output is formatted bcrypt $2a$...
+*/
+BOTAN_PUBLIC_API(2,0) int botan_bcrypt_generate(uint8_t* out, size_t* out_len,
+ const char* password,
+ botan_rng_t rng,
+ size_t work_factor,
+ uint32_t flags);
+
+/**
* Check a previously created password hash
* @param pass the password to check against
* @param hash the stored hash to check against
@@ -751,9 +751,9 @@ BOTAN_PUBLIC_API(2,0) int botan_bcrypt_is_valid(const char* pass, const char* ha
typedef struct botan_privkey_struct* botan_privkey_t;
BOTAN_PUBLIC_API(2,0) int botan_privkey_create(botan_privkey_t* key,
- const char* algo_name,
- const char* algo_params,
- botan_rng_t rng);
+ const char* algo_name,
+ const char* algo_params,
+ botan_rng_t rng);
#define BOTAN_CHECK_KEY_EXPENSIVE_TESTS 1
diff --git a/src/python/botan2.py b/src/python/botan2.py
index 18125a4b6..209805a66 100755
--- a/src/python/botan2.py
+++ b/src/python/botan2.py
@@ -1,10 +1,10 @@
-#!/usr/bin/env python
+#!/usr/bin/python
"""
Python wrapper of the botan crypto library
https://botan.randombit.net
-(C) 2015,2017 Jack Lloyd
+(C) 2015,2017,2018 Jack Lloyd
(C) 2015 Uri Blumenthal (extensions and patches)
Botan is released under the Simplified BSD License (see license.txt)
@@ -12,23 +12,35 @@ Botan is released under the Simplified BSD License (see license.txt)
This module uses the ctypes module and is usable by programs running
under at least CPython 2.7, CPython 3.4 and 3.5, or PyPy.
-It uses botan's ffi module, which exposes a C API. It suppports all
-versions of Botan >= 2.0
+It uses botan's ffi module, which exposes a C API.
+
+This version of the module requires FFI API version 20180713, which was
+introduced in Botan 2.8
+
"""
-import sys
from ctypes import CDLL, POINTER, byref, c_void_p, c_size_t, c_uint32, c_int, c_char, c_char_p, create_string_buffer
+
+from sys import version_info
+from time import strptime, mktime
from binascii import hexlify
from datetime import datetime
-import time
+BOTAN_FFI_VERSION = 20180713
#
# Base exception for all exceptions raised from this module
#
class BotanException(Exception):
- pass
+ def __init__(self, message, rc=0):
+
+ if rc == 0:
+ super(BotanException, self).__init__(message)
+ else:
+ descr = botan.botan_error_description(rc).decode('ascii')
+ super(BotanException, self).__init__("%s: %d (%s)" % (message, rc, descr))
+ self.rc = rc
#
# Module initialization
@@ -37,7 +49,7 @@ class BotanException(Exception):
def load_botan_dll(expected_version):
possible_dll_names = ['libbotan-2.dylib', 'libbotan-2.so'] + \
- ['libbotan-2.so.%d' % (v) for v in reversed(range(0, 16))]
+ ['libbotan-2.so.%d' % (v) for v in reversed(range(8, 16))]
for dll_name in possible_dll_names:
try:
@@ -51,12 +63,311 @@ def load_botan_dll(expected_version):
return None
-botan = load_botan_dll(20150515) # pylint: disable=invalid-name
+botan = load_botan_dll(BOTAN_FFI_VERSION) # pylint: disable=invalid-name
if botan is None:
raise BotanException("Could not find a usable Botan shared object library")
#
+# ctypes function prototypes
+#
+def errcheck_for(fn_name):
+ def errcheck(rc, _func, _args):
+ # No idea what to do if return value isn't an int, just return it
+ if not isinstance(rc, int):
+ return rc
+
+ if rc >= 0:
+ return rc
+ if rc == -10: # insufficient buffer space, pass up to caller
+ return rc
+ raise BotanException('%s failed' % (fn_name), rc)
+ return errcheck
+
+botan.botan_version_string.argtypes = []
+botan.botan_version_string.restype = c_char_p
+
+botan.botan_error_description.argtypes = [c_int]
+botan.botan_error_description.restype = c_char_p
+
+# RNG
+botan.botan_rng_init.argtypes = [c_void_p, c_char_p]
+botan.botan_rng_init.errcheck = errcheck_for('botan_rng_init')
+
+botan.botan_rng_destroy.argtypes = [c_void_p]
+botan.botan_rng_destroy.errcheck = errcheck_for('botan_rng_destroy')
+
+botan.botan_rng_reseed.argtypes = [c_void_p, c_size_t]
+botan.botan_rng_reseed.errcheck = errcheck_for('botan_rng_reseed')
+
+botan.botan_rng_get.argtypes = [c_void_p, POINTER(c_char), c_size_t]
+botan.botan_rng_get.errcheck = errcheck_for('botan_rng_get')
+
+# Hash function
+botan.botan_hash_init.argtypes = [c_void_p, c_char_p, c_uint32]
+botan.botan_hash_init.errcheck = errcheck_for('botan_hash_init')
+
+botan.botan_hash_destroy.argtypes = [c_void_p]
+botan.botan_hash_destroy.errcheck = errcheck_for('botan_hash_destroy')
+
+botan.botan_hash_name.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_hash_name.errcheck = errcheck_for('botan_hash_name')
+
+botan.botan_hash_clear.argtypes = [c_void_p]
+botan.botan_hash_clear.errcheck = errcheck_for('botan_hash_clear')
+
+botan.botan_hash_output_length.argtypes = [c_void_p, POINTER(c_size_t)]
+botan.botan_hash_output_length.errcheck = errcheck_for('botan_hash_output_length')
+
+botan.botan_hash_update.argtypes = [c_void_p, POINTER(c_char), c_size_t]
+botan.botan_hash_update.errcheck = errcheck_for('botan_hash_update')
+
+botan.botan_hash_final.argtypes = [c_void_p, POINTER(c_char)]
+botan.botan_hash_final.errcheck = errcheck_for('botan_hash_final')
+
+# MAC
+botan.botan_mac_init.argtypes = [c_void_p, c_char_p, c_uint32]
+botan.botan_mac_init.errcheck = errcheck_for('botan_mac_init')
+
+botan.botan_mac_destroy.argtypes = [c_void_p]
+botan.botan_mac_destroy.errcheck = errcheck_for('botan_mac_destroy')
+
+botan.botan_mac_name.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_mac_name.errcheck = errcheck_for('botan_mac_name')
+
+botan.botan_mac_clear.argtypes = [c_void_p]
+botan.botan_mac_clear.errcheck = errcheck_for('botan_mac_clear')
+
+botan.botan_mac_output_length.argtypes = [c_void_p, POINTER(c_size_t)]
+botan.botan_mac_output_length.errcheck = errcheck_for('botan_mac_output_length')
+
+botan.botan_mac_set_key.argtypes = [c_void_p, POINTER(c_char), c_size_t]
+botan.botan_mac_set_key.errcheck = errcheck_for('botan_mac_set_key')
+
+botan.botan_mac_update.argtypes = [c_void_p, POINTER(c_char), c_size_t]
+botan.botan_mac_update.errcheck = errcheck_for('botan_mac_update')
+
+botan.botan_mac_final.argtypes = [c_void_p, POINTER(c_char)]
+botan.botan_mac_final.errcheck = errcheck_for('botan_mac_final')
+
+# Cipher
+botan.botan_cipher_init.argtypes = [c_void_p, c_char_p, c_uint32]
+botan.botan_cipher_init.errcheck = errcheck_for('botan_cipher_init')
+
+botan.botan_cipher_destroy.argtypes = [c_void_p]
+botan.botan_cipher_destroy.errcheck = errcheck_for('botan_cipher_destroy')
+
+botan.botan_cipher_name.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_cipher_name.errcheck = errcheck_for('botan_cipher_name')
+
+botan.botan_cipher_get_default_nonce_length.argtypes = [c_void_p, POINTER(c_size_t)]
+botan.botan_cipher_get_default_nonce_length.errcheck = errcheck_for('botan_cipher_get_default_nonce_length')
+
+botan.botan_cipher_get_update_granularity.argtypes = [c_void_p, POINTER(c_size_t)]
+botan.botan_cipher_get_update_granularity.errcheck = errcheck_for('botan_cipher_get_update_granularity')
+
+botan.botan_cipher_get_tag_length.argtypes = [c_void_p, POINTER(c_size_t)]
+botan.botan_cipher_get_tag_length.errcheck = errcheck_for('botan_cipher_get_tag_length')
+
+botan.botan_cipher_valid_nonce_length.argtypes = [c_void_p, c_size_t]
+botan.botan_cipher_valid_nonce_length.errcheck = errcheck_for('botan_cipher_valid_nonce_length')
+
+botan.botan_cipher_clear.argtypes = [c_void_p]
+botan.botan_cipher_clear.errcheck = errcheck_for('botan_cipher_clear')
+
+botan.botan_cipher_set_key.argtypes = [c_void_p, POINTER(c_char), c_size_t]
+botan.botan_cipher_set_key.errcheck = errcheck_for('botan_cipher_set_key')
+
+botan.botan_cipher_set_associated_data.argtypes = [c_void_p, POINTER(c_char), c_size_t]
+botan.botan_cipher_set_associated_data.errcheck = errcheck_for('botan_cipher_set_associated_data')
+
+botan.botan_cipher_start.argtypes = [c_void_p, POINTER(c_char), c_size_t]
+botan.botan_cipher_start.errcheck = errcheck_for('botan_cipher_start')
+
+botan.botan_cipher_update.argtypes = [c_void_p, c_uint32,
+ POINTER(c_char), c_size_t, POINTER(c_size_t),
+ POINTER(c_char), c_size_t, POINTER(c_size_t)]
+botan.botan_cipher_update.errcheck = errcheck_for('botan_cipher_update')
+
+# Bcrypt
+botan.botan_bcrypt_generate.argtypes = [POINTER(c_char), POINTER(c_size_t),
+ c_char_p, c_void_p, c_size_t, c_uint32]
+botan.botan_bcrypt_generate.errcheck = errcheck_for('botan_bcrypt_generate')
+
+botan.botan_bcrypt_is_valid.argtypes = [c_char_p, c_char_p]
+botan.botan_bcrypt_is_valid.errcheck = errcheck_for('botan_bcrypt_is_valid')
+
+# PBKDF
+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]
+botan.botan_pbkdf.errcheck = errcheck_for('botan_pbkdf')
+
+botan.botan_pbkdf_timed.argtypes = [c_char_p, POINTER(c_char), c_size_t, c_char_p,
+ c_void_p, c_size_t, c_size_t, POINTER(c_size_t)]
+botan.botan_pbkdf_timed.errcheck = errcheck_for('botan_pbkdf_timed')
+
+# Scrypt
+botan.botan_scrypt.argtypes = [POINTER(c_char), c_size_t, c_char_p, POINTER(c_char), c_size_t,
+ c_size_t, c_size_t, c_size_t]
+botan.botan_scrypt.errcheck = errcheck_for('botan_scrypt')
+
+# KDF
+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, POINTER(c_char), c_size_t]
+botan.botan_kdf.errcheck = errcheck_for('botan_kdf')
+
+# Public key
+botan.botan_pubkey_destroy.argtypes = [c_void_p]
+botan.botan_pubkey_destroy.errcheck = errcheck_for('botan_pubkey_destroy')
+
+botan.botan_pubkey_estimated_strength.argtypes = [c_void_p, POINTER(c_size_t)]
+botan.botan_pubkey_estimated_strength.errcheck = errcheck_for('botan_pubkey_estimated_strength')
+
+botan.botan_pubkey_algo_name.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_pubkey_algo_name.errcheck = errcheck_for('botan_pubkey_algo_name')
+
+botan.botan_pubkey_export.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t), c_uint32]
+botan.botan_pubkey_export.errcheck = errcheck_for('botan_pubkey_export')
+
+botan.botan_pubkey_fingerprint.argtypes = [c_void_p, c_char_p,
+ POINTER(c_char), POINTER(c_size_t)]
+botan.botan_pubkey_fingerprint.errcheck = errcheck_for('botan_pubkey_fingerprint')
+
+botan.botan_privkey_create.argtypes = [c_void_p, c_char_p, c_char_p, c_void_p]
+botan.botan_privkey_create.errcheck = errcheck_for('botan_privkey_create')
+
+botan.botan_privkey_algo_name.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_privkey_algo_name.errcheck = errcheck_for('botan_privkey_algo_name')
+
+botan.botan_privkey_export_pubkey.argtypes = [c_void_p, c_void_p]
+botan.botan_privkey_export_pubkey.errcheck = errcheck_for('botan_privkey_export_pubkey')
+
+botan.botan_privkey_destroy.argtypes = [c_void_p]
+botan.botan_privkey_destroy.errcheck = errcheck_for('botan_privkey_destroy')
+
+botan.botan_privkey_export.argtypes = [c_void_p, POINTER(c_char), c_void_p]
+botan.botan_privkey_export.errcheck = errcheck_for('botan_privkey_export')
+
+# PK Encryption
+botan.botan_pk_op_encrypt_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32]
+botan.botan_pk_op_encrypt_create.errcheck = errcheck_for('botan_pk_op_encrypt_create')
+
+botan.botan_pk_op_encrypt_output_length.argtypes = [c_void_p, c_size_t, POINTER(c_size_t)]
+botan.botan_pk_op_encrypt_output_length.errcheck = errcheck_for('botan_pk_op_encrypt_output_length')
+
+botan.botan_pk_op_encrypt_destroy.argtypes = [c_void_p]
+botan.botan_pk_op_encrypt_destroy.errcheck = errcheck_for('botan_pk_op_encrypt_destroy')
+
+botan.botan_pk_op_encrypt.argtypes = [c_void_p, c_void_p,
+ POINTER(c_char), POINTER(c_size_t),
+ POINTER(c_char), c_size_t]
+botan.botan_pk_op_encrypt.errcheck = errcheck_for('botan_pk_op_encrypt')
+
+# PK Decryption
+botan.botan_pk_op_decrypt_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32]
+botan.botan_pk_op_decrypt_create.errcheck = errcheck_for('botan_pk_op_decrypt_create')
+
+botan.botan_pk_op_decrypt_output_length.argtypes = [c_void_p, c_size_t, POINTER(c_size_t)]
+botan.botan_pk_op_decrypt_output_length.errcheck = errcheck_for('botan_pk_op_decrypt_output_length')
+
+botan.botan_pk_op_decrypt_destroy.argtypes = [c_void_p]
+botan.botan_pk_op_decrypt_destroy.errcheck = errcheck_for('botan_pk_op_decrypt_destroy')
+
+botan.botan_pk_op_decrypt.argtypes = [c_void_p,
+ POINTER(c_char), POINTER(c_size_t),
+ POINTER(c_char), c_size_t]
+botan.botan_pk_op_decrypt.errcheck = errcheck_for('botan_pk_op_encrypt')
+
+# PK Signatures
+botan.botan_pk_op_sign_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32]
+botan.botan_pk_op_sign_create.errcheck = errcheck_for('botan_pk_op_sign_create')
+
+botan.botan_pk_op_sign_destroy.argtypes = [c_void_p]
+botan.botan_pk_op_sign_destroy.errcheck = errcheck_for('botan_pk_op_sign_destroy')
+
+botan.botan_pk_op_sign_update.argtypes = [c_void_p, POINTER(c_char), c_size_t]
+botan.botan_pk_op_sign_update.errcheck = errcheck_for('botan_pk_op_sign_update')
+
+botan.botan_pk_op_sign_finish.argtypes = [c_void_p, c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_pk_op_sign_finish.errcheck = errcheck_for('botan_pk_op_sign_finish')
+
+# PK Verification
+botan.botan_pk_op_verify_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32]
+botan.botan_pk_op_verify_create.errcheck = errcheck_for('botan_pk_op_verify_create')
+
+botan.botan_pk_op_verify_destroy.argtypes = [c_void_p]
+botan.botan_pk_op_verify_destroy.errcheck = errcheck_for('botan_pk_op_verify_destroy')
+
+botan.botan_pk_op_verify_update.argtypes = [c_void_p, POINTER(c_char), c_size_t]
+botan.botan_pk_op_verify_update.errcheck = errcheck_for('botan_pk_op_verify_update')
+
+botan.botan_pk_op_verify_finish.argtypes = [c_void_p, POINTER(c_char), c_size_t]
+botan.botan_pk_op_verify_finish.errcheck = errcheck_for('botan_pk_op_verify_finish')
+
+# MCEIES
+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)]
+botan.botan_mceies_encrypt.errcheck = errcheck_for('botan_mceies_encrypt')
+
+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)]
+botan.botan_mceies_decrypt.errcheck = errcheck_for('botan_mceies_decrypt')
+
+# Key Agreement
+botan.botan_pk_op_key_agreement_export_public.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_pk_op_key_agreement_export_public.errcheck = errcheck_for('botan_pk_op_key_agreement_export_public')
+
+botan.botan_pk_op_key_agreement_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32]
+botan.botan_pk_op_key_agreement_create.errcheck = errcheck_for('botan_pk_op_key_agreement_create')
+
+botan.botan_pk_op_key_agreement_destroy.argtypes = [c_void_p]
+botan.botan_pk_op_key_agreement_destroy.errcheck = errcheck_for('botan_pk_op_key_agreement_destroy')
+
+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]
+botan.botan_pk_op_key_agreement.errcheck = errcheck_for('botan_pk_op_key_agreement')
+
+# X509 certs
+botan.botan_x509_cert_load_file.argtypes = [POINTER(c_void_p), c_char_p]
+botan.botan_x509_cert_load_file.errcheck = errcheck_for('botan_x509_cert_load_file')
+
+botan.botan_x509_cert_load.argtypes = [POINTER(c_void_p), POINTER(c_char), c_size_t]
+botan.botan_x509_cert_load.errcheck = errcheck_for('botan_x509_cert_load')
+
+botan.botan_x509_cert_destroy.argtypes = [c_void_p]
+botan.botan_x509_cert_destroy.errcheck = errcheck_for('botan_x509_cert_destroy')
+
+botan.botan_x509_cert_get_time_starts.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_x509_cert_get_time_starts.errcheck = errcheck_for('botan_x509_cert_get_time_starts')
+
+botan.botan_x509_cert_get_time_expires.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_x509_cert_get_time_expires.errcheck = errcheck_for('botan_x509_cert_get_time_expires')
+
+botan.botan_x509_cert_to_string.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_x509_cert_to_string.errcheck = errcheck_for('botan_x509_cert_to_string')
+
+botan.botan_x509_cert_get_fingerprint.argtypes = [c_void_p, c_char_p,
+ POINTER(c_char), POINTER(c_size_t)]
+botan.botan_x509_cert_get_fingerprint.errcheck = errcheck_for('botan_x509_cert_get_fingerprint')
+
+botan.botan_x509_cert_get_serial_number.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_x509_cert_get_serial_number.errcheck = errcheck_for('botan_x509_cert_get_serial_number')
+
+botan.botan_x509_cert_get_authority_key_id.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_x509_cert_get_authority_key_id.errcheck = errcheck_for('botan_x509_cert_get_authority_key_id')
+
+botan.botan_x509_cert_get_subject_key_id.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_x509_cert_get_subject_key_id.errcheck = errcheck_for('botan_x509_cert_get_subject_key_id')
+
+botan.botan_x509_cert_get_public_key_bits.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_x509_cert_get_public_key_bits.errcheck = errcheck_for('botan_x509_cert_get_public_key_bits')
+
+botan.botan_x509_cert_get_public_key.argtypes = [c_void_p, c_void_p]
+botan.botan_x509_cert_get_public_key.errcheck = errcheck_for('botan_x509_cert_get_public_key')
+
+botan.botan_x509_cert_get_subject_dn.argtypes = [c_void_p, c_char_p, c_size_t, POINTER(c_char), POINTER(c_size_t)]
+botan.botan_x509_cert_get_subject_dn.errcheck = errcheck_for('botan_x509_cert_get_subject_dn')
+
+#
# Internal utilities
#
def _call_fn_returning_vec(guess, fn):
@@ -65,12 +376,8 @@ def _call_fn_returning_vec(guess, fn):
buf_len = c_size_t(len(buf))
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)
- else:
- raise BotanException("Call failed: %d" % (rc))
+ if rc == -10 and buf_len.value > len(buf):
+ return _call_fn_returning_vec(buf_len.value, fn)
assert buf_len.value <= len(buf)
return buf.raw[0:int(buf_len.value)]
@@ -83,27 +390,33 @@ def _call_fn_returning_string(guess, fn):
def _ctype_str(s):
assert isinstance(s, str)
- if sys.version_info[0] < 3:
+ if version_info[0] < 3:
return s
else:
return s.encode('utf-8')
+def _ctype_to_str(s):
+ if version_info[0] < 3:
+ return s.encode('utf-8')
+ else:
+ return s.decode('utf-8')
+
def _ctype_bits(s):
- if sys.version_info[0] < 3:
+ if version_info[0] < 3:
if isinstance(s, str):
return s
else:
- raise Exception("Internal error - unexpected type provided to _ctype_bits")
+ raise Exception("Internal error - unexpected type %s provided to _ctype_bits" % (type(s).__name__))
else:
if isinstance(s, bytes):
return s
elif isinstance(s, str):
return s.encode('utf-8')
else:
- raise Exception("Internal error - unexpected type provided to _ctype_bits")
+ raise Exception("Internal error - unexpected type %s provided to _ctype_bits" % (type(s).__name__))
def _ctype_bufout(buf):
- if sys.version_info[0] < 3:
+ if version_info[0] < 3:
return str(buf.raw)
else:
return buf.raw
@@ -124,7 +437,6 @@ def version_patch():
return botan.botan_version_patch()
def version_string():
- botan.botan_version_string.restype = c_char_p
return botan.botan_version_string().decode('ascii')
#
@@ -133,64 +445,48 @@ def version_string():
class rng(object): # pylint: disable=invalid-name
# Can also use type "system"
def __init__(self, rng_type='system'):
- botan.botan_rng_init.argtypes = [c_void_p, c_char_p]
self.rng = c_void_p(0)
- rc = botan.botan_rng_init(byref(self.rng), _ctype_str(rng_type))
- if rc != 0 or self.rng is None:
- raise BotanException("No rng " + rng_type + " available")
+ botan.botan_rng_init(byref(self.rng), _ctype_str(rng_type))
def __del__(self):
- botan.botan_rng_destroy.argtypes = [c_void_p]
botan.botan_rng_destroy(self.rng)
def reseed(self, bits=256):
- botan.botan_rng_reseed.argtypes = [c_void_p, c_size_t]
botan.botan_rng_reseed(self.rng, bits)
def get(self, length):
- botan.botan_rng_get.argtypes = [c_void_p, POINTER(c_char), c_size_t]
out = create_string_buffer(length)
l = c_size_t(length)
- rc = botan.botan_rng_get(self.rng, out, l)
- if rc == 0:
- return _ctype_bufout(out)
- else:
- return None
+ botan.botan_rng_get(self.rng, out, l)
+ return _ctype_bufout(out)
#
# Hash function
#
class hash_function(object): # pylint: disable=invalid-name
def __init__(self, algo):
- 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)
- rc = botan.botan_hash_init(byref(self.hash), _ctype_str(algo), flags)
- if rc != 0 or self.hash is None:
- raise BotanException("No hash " + algo + " for you!")
+ botan.botan_hash_init(byref(self.hash), _ctype_str(algo), flags)
def __del__(self):
- botan.botan_hash_destroy.argtypes = [c_void_p]
botan.botan_hash_destroy(self.hash)
+ def algo_name(self):
+ return _call_fn_returning_string(32, lambda b, bl: botan.botan_hash_name(self.hash, b, bl))
+
def clear(self):
- botan.botan_hash_clear.argtypes = [c_void_p]
- return botan.botan_hash_clear(self.hash)
+ botan.botan_hash_clear(self.hash)
def output_length(self):
- botan.botan_hash_output_length.argtypes = [c_void_p, POINTER(c_size_t)]
l = c_size_t(0)
- rc = botan.botan_hash_output_length(self.hash, byref(l))
- if rc == 0:
- return l.value
- raise BotanException("botan_hash_output_length failed")
+ botan.botan_hash_output_length(self.hash, byref(l))
+ return l.value
def update(self, x):
- botan.botan_hash_update.argtypes = [c_void_p, POINTER(c_char), c_size_t]
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)
return _ctype_bufout(out)
@@ -200,64 +496,53 @@ class hash_function(object): # pylint: disable=invalid-name
#
class message_authentication_code(object): # pylint: disable=invalid-name
def __init__(self, algo):
- 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), _ctype_str(algo), flags)
- if rc != 0 or self.mac is None:
- raise BotanException("No mac " + algo + " for you!")
+ botan.botan_mac_init(byref(self.mac), _ctype_str(algo), flags)
def __del__(self):
- botan.botan_mac_destroy.argtypes = [c_void_p]
botan.botan_mac_destroy(self.mac)
def clear(self):
- botan.botan_mac_clear.argtypes = [c_void_p]
- return botan.botan_mac_clear(self.mac)
+ botan.botan_mac_clear(self.mac)
+
+ def algo_name(self):
+ return _call_fn_returning_string(32, lambda b, bl: botan.botan_mac_name(self.mac, b, bl))
def output_length(self):
- botan.botan_mac_output_length.argtypes = [c_void_p, POINTER(c_size_t)]
l = c_size_t(0)
- rc = botan.botan_mac_output_length(self.mac, byref(l))
- if rc == 0:
- return l.value
- raise BotanException("botan_mac_output_length failed")
+ botan.botan_mac_output_length(self.mac, byref(l))
+ return l.value
def set_key(self, key):
- botan.botan_mac_set_key.argtypes = [c_void_p, POINTER(c_char), c_size_t]
- return botan.botan_mac_set_key(self.mac, key, len(key))
+ botan.botan_mac_set_key(self.mac, key, len(key))
def update(self, x):
- botan.botan_mac_update.argtypes = [c_void_p, POINTER(c_char), c_size_t]
botan.botan_mac_update(self.mac, x, len(x))
def final(self):
- 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)
return _ctype_bufout(out)
class cipher(object): # pylint: disable=invalid-name
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)
- rc = botan.botan_cipher_init(byref(self.cipher), _ctype_str(algo), flags)
- if rc != 0 or self.cipher is None:
- raise BotanException("No cipher " + algo + " for you!")
+ botan.botan_cipher_init(byref(self.cipher), _ctype_str(algo), flags)
def __del__(self):
- botan.botan_cipher_destroy.argtypes = [c_void_p]
botan.botan_cipher_destroy(self.cipher)
+ def algo_name(self):
+ return _call_fn_returning_string(32, lambda b, bl: botan.botan_cipher_name(self.cipher, b, bl))
+
def default_nonce_length(self):
- botan.botan_cipher_get_default_nonce_length.argtypes = [c_void_p, POINTER(c_size_t)]
l = c_size_t(0)
botan.botan_cipher_get_default_nonce_length(self.cipher, byref(l))
return l.value
def update_granularity(self):
- botan.botan_cipher_get_update_granularity.argtypes = [c_void_p, POINTER(c_size_t)]
l = c_size_t(0)
botan.botan_cipher_get_update_granularity(self.cipher, byref(l))
return l.value
@@ -269,7 +554,6 @@ class cipher(object): # pylint: disable=invalid-name
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)
botan.botan_cipher_get_tag_length(self.cipher, byref(l))
return l.value
@@ -278,32 +562,22 @@ class cipher(object): # pylint: disable=invalid-name
return self.tag_length() > 0
def valid_nonce_length(self, nonce_len):
- botan.botan_cipher_valid_nonce_length.argtypes = [c_void_p, c_size_t]
rc = botan.botan_cipher_valid_nonce_length(self.cipher, nonce_len)
- if rc < 0:
- raise BotanException('Error calling valid_nonce_length')
return True if rc == 1 else False
def clear(self):
- botan.botan_cipher_clear.argtypes = [c_void_p]
botan.botan_cipher_clear(self.cipher)
def set_key(self, key):
- botan.botan_cipher_set_key.argtypes = [c_void_p, POINTER(c_char), c_size_t]
botan.botan_cipher_set_key(self.cipher, key, len(key))
def set_assoc_data(self, ad):
- botan.botan_cipher_set_associated_data.argtypes = [c_void_p, POINTER(c_char), c_size_t]
botan.botan_cipher_set_associated_data(self.cipher, ad, len(ad))
def start(self, nonce):
- botan.botan_cipher_start.argtypes = [c_void_p, POINTER(c_char), c_size_t]
botan.botan_cipher_start(self.cipher, nonce, len(nonce))
def _update(self, txt, final):
- botan.botan_cipher_update.argtypes = [c_void_p, c_uint32,
- POINTER(c_char), c_size_t, POINTER(c_size_t),
- POINTER(c_char), c_size_t, POINTER(c_size_t)]
inp = txt if txt else ''
inp_sz = c_size_t(len(inp))
@@ -332,53 +606,60 @@ def bcrypt(passwd, rng_instance, work_factor=10):
"""
Bcrypt password hashing
"""
- botan.botan_bcrypt_generate.argtypes = [POINTER(c_char), POINTER(c_size_t),
- c_char_p, c_void_p, c_size_t, c_uint32]
out_len = c_size_t(64)
out = create_string_buffer(out_len.value)
flags = c_uint32(0)
- rc = botan.botan_bcrypt_generate(out, byref(out_len), _ctype_str(passwd),
- rng_instance.rng, c_size_t(work_factor), flags)
- if rc != 0:
- raise BotanException('botan bcrypt failed, error %s' % (rc))
+ botan.botan_bcrypt_generate(out, byref(out_len), _ctype_str(passwd),
+ rng_instance.rng, c_size_t(work_factor), flags)
b = out.raw[0:int(out_len.value)-1]
if b[-1] == '\x00':
b = b[:-1]
- return b
+ return _ctype_to_str(b)
def check_bcrypt(passwd, passwd_hash):
- rc = botan.botan_bcrypt_is_valid(_ctype_str(passwd), passwd_hash)
+ rc = botan.botan_bcrypt_is_valid(_ctype_str(passwd), _ctype_str(passwd_hash))
return rc == 0
#
# 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]
+def pbkdf(algo, password, out_len, iterations=10000, salt=None):
+ if salt is None:
+ salt = rng().get(12)
out_buf = create_string_buffer(out_len)
- botan.botan_pbkdf(_ctype_str(algo), out_buf, out_len, _ctype_str(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)):
- botan.botan_pbkdf_timed.argtypes = [c_char_p, POINTER(c_char), c_size_t, c_char_p,
- c_void_p, c_size_t, c_size_t, POINTER(c_size_t)]
+def pbkdf_timed(algo, password, out_len, ms_to_run=300, salt=None):
+ if salt is None:
+ salt = rng().get(12)
out_buf = create_string_buffer(out_len)
iterations = c_size_t(0)
- botan.botan_pbkdf_timed(
- _ctype_str(algo), out_buf, out_len, _ctype_str(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)
#
+# Scrypt
+#
+def scrypt(out_len, password, salt, n=1024, r=8, p=8):
+ out_buf = create_string_buffer(out_len)
+ botan.botan_scrypt(out_buf, out_len, _ctype_str(password),
+ _ctype_bits(salt), len(salt), n, r, p)
+
+ return out_buf.raw
+
+#
# KDF
#
def kdf(algo, secret, out_len, salt, label):
- 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, POINTER(c_char), c_size_t]
out_buf = create_string_buffer(out_len)
out_sz = c_size_t(out_len)
- botan.botan_kdf(_ctype_str(algo), out_buf, out_sz, secret, len(secret),
- salt, len(salt), label, len(label))
+ botan.botan_kdf(_ctype_str(algo), out_buf, out_sz,
+ secret, len(secret),
+ salt, len(salt),
+ label, len(label))
return out_buf.raw[0:int(out_sz.value)]
#
@@ -389,27 +670,21 @@ class public_key(object): # pylint: disable=invalid-name
self.pubkey = obj
def __del__(self):
- botan.botan_pubkey_destroy.argtypes = [c_void_p]
botan.botan_pubkey_destroy(self.pubkey)
def estimated_strength(self):
- botan.botan_pubkey_estimated_strength.argtypes = [c_void_p, POINTER(c_size_t)]
r = c_size_t(0)
botan.botan_pubkey_estimated_strength(self.pubkey, byref(r))
return r.value
def algo_name(self):
- botan.botan_pubkey_algo_name.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
return _call_fn_returning_string(32, lambda b, bl: botan.botan_pubkey_algo_name(self.pubkey, b, bl))
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
- return _call_fn_returning_vec(0, lambda b, bl: botan.botan_pubkey_export(self.pubkey, b, bl, flag))
+ return _call_fn_returning_vec(4096, lambda b, bl: botan.botan_pubkey_export(self.pubkey, b, bl, flag))
def fingerprint(self, hash_algorithm='SHA-256'):
- botan.botan_pubkey_fingerprint.argtypes = [c_void_p, c_char_p,
- POINTER(c_char), POINTER(c_size_t)]
n = hash_function(hash_algorithm).output_length()
buf = create_string_buffer(n)
@@ -419,41 +694,43 @@ class public_key(object): # pylint: disable=invalid-name
return _hex_encode(buf[0:int(buf_len.value)])
class private_key(object): # pylint: disable=invalid-name
- def __init__(self, alg, param, rng_instance):
- botan.botan_privkey_create_rsa.argtypes = [c_void_p, c_void_p, c_size_t]
- botan.botan_privkey_create_ecdsa.argtypes = [c_void_p, c_void_p, c_char_p]
- botan.botan_privkey_create_ecdh.argtypes = [c_void_p, c_void_p, c_char_p]
- botan.botan_privkey_create_mceliece.argtypes = [c_void_p, c_void_p, c_size_t, c_size_t]
+ def __init__(self, algo, params, rng_instance):
self.privkey = c_void_p(0)
- if alg == 'rsa':
- botan.botan_privkey_create_rsa(byref(self.privkey), rng_instance.rng, param)
- elif alg == 'ecdsa':
- botan.botan_privkey_create_ecdsa(byref(self.privkey), rng_instance.rng, _ctype_str(param))
- elif alg == 'ecdh':
- botan.botan_privkey_create_ecdh(byref(self.privkey), rng_instance.rng, _ctype_str(param))
- elif alg in ['mce', 'mceliece']:
- botan.botan_privkey_create_mceliece(byref(self.privkey), rng_instance.rng, param[0], param[1])
- else:
- raise BotanException('Unknown public key algo ' + alg)
+ if algo == 'rsa':
+ algo = 'RSA'
+ params = "%d" % (params)
+ elif algo == 'ecdsa':
+ algo = 'ECDSA'
+ elif algo == 'ecdh':
+
+ if params == 'curve25519':
+ algo = 'Curve25519'
+ params = ''
+ else:
+ algo = 'ECDH'
- if self.privkey is None:
- raise BotanException('Error creating ' + alg + ' key')
+ elif algo in ['mce', 'mceliece']:
+ algo = 'McEliece'
+ params = "%d,%d" % (params[0], params[1])
+
+ botan.botan_privkey_create(byref(self.privkey),
+ _ctype_str(algo), _ctype_str(params), rng_instance.rng)
def __del__(self):
- botan.botan_privkey_destroy.argtypes = [c_void_p]
botan.botan_privkey_destroy(self.privkey)
+ def algo_name(self):
+ return _call_fn_returning_string(32, lambda b, bl: botan.botan_privkey_algo_name(self.privkey, b, bl))
+
def get_public_key(self):
- botan.botan_privkey_export_pubkey.argtypes = [c_void_p, c_void_p]
pub = c_void_p(0)
botan.botan_privkey_export_pubkey(byref(pub), self.privkey)
return public_key(pub)
def export(self):
- botan.botan_privkey_export.argtypes = [c_void_p, POINTER(c_char), c_void_p]
n = 4096
buf = create_string_buffer(n)
@@ -467,102 +744,69 @@ class private_key(object): # pylint: disable=invalid-name
class pk_op_encrypt(object): # pylint: disable=invalid-name
def __init__(self, key, padding):
- 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
- print("Padding is ", padding)
botan.botan_pk_op_encrypt_create(byref(self.op), key.pubkey, _ctype_str(padding), flags)
- if not self.op:
- raise BotanException("No pk op for you")
def __del__(self):
- botan.botan_pk_op_encrypt_destroy.argtypes = [c_void_p]
botan.botan_pk_op_encrypt_destroy(self.op)
def encrypt(self, msg, rng_instance):
- botan.botan_pk_op_encrypt.argtypes = [c_void_p, c_void_p,
- POINTER(c_char), POINTER(c_size_t),
- POINTER(c_char), c_size_t]
-
- outbuf_sz = c_size_t(4096) #?!?!
+ outbuf_sz = c_size_t(0)
+ botan.botan_pk_op_encrypt_output_length(self.op, len(msg), byref(outbuf_sz))
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)
- botan.botan_pk_op_encrypt(self.op, rng_instance.rng, outbuf, byref(outbuf_sz), msg, ll)
- #print("encrypt: outbuf_sz.value=%d" % outbuf_sz.value)
+ botan.botan_pk_op_encrypt(self.op, rng_instance.rng, outbuf, byref(outbuf_sz), msg, len(msg))
return outbuf.raw[0:int(outbuf_sz.value)]
class pk_op_decrypt(object): # pylint: disable=invalid-name
def __init__(self, key, padding):
- 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
botan.botan_pk_op_decrypt_create(byref(self.op), key.privkey, _ctype_str(padding), flags)
- if not self.op:
- raise BotanException("No pk op for you")
def __del__(self):
- botan.botan_pk_op_decrypt_destroy.argtypes = [c_void_p]
botan.botan_pk_op_decrypt_destroy(self.op)
def decrypt(self, msg):
- botan.botan_pk_op_decrypt.argtypes = [c_void_p,
- POINTER(c_char), POINTER(c_size_t),
- POINTER(c_char), c_size_t]
-
- outbuf_sz = c_size_t(4096) #?!?!
+ outbuf_sz = c_size_t(0)
+ botan.botan_pk_op_decrypt_output_length(self.op, len(msg), byref(outbuf_sz))
outbuf = create_string_buffer(outbuf_sz.value)
- ll = len(msg)
- botan.botan_pk_op_decrypt(self.op, outbuf, byref(outbuf_sz), _ctype_bits(msg), ll)
+ botan.botan_pk_op_decrypt(self.op, outbuf, byref(outbuf_sz), _ctype_bits(msg), len(msg))
return outbuf.raw[0:int(outbuf_sz.value)]
class pk_op_sign(object): # pylint: disable=invalid-name
def __init__(self, key, padding):
- 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
botan.botan_pk_op_sign_create(byref(self.op), key.privkey, _ctype_str(padding), flags)
- if not self.op:
- raise BotanException("No pk op for you")
def __del__(self):
- botan.botan_pk_op_sign_destroy.argtypes = [c_void_p]
botan.botan_pk_op_sign_destroy(self.op)
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, _ctype_str(msg), len(msg))
def finish(self, rng_instance):
- botan.botan_pk_op_sign_finish.argtypes = [c_void_p, c_void_p, POINTER(c_char), POINTER(c_size_t)]
- outbuf_sz = c_size_t(4096) #?!?!
+ outbuf_sz = c_size_t(0)
+ botan.botan_pk_op_sign_output_length(self.op, byref(outbuf_sz))
outbuf = create_string_buffer(outbuf_sz.value)
botan.botan_pk_op_sign_finish(self.op, rng_instance.rng, outbuf, byref(outbuf_sz))
return outbuf.raw[0:int(outbuf_sz.value)]
class pk_op_verify(object): # pylint: disable=invalid-name
def __init__(self, key, padding):
- 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, _ctype_str(padding), flags)
- if not self.op:
- raise BotanException("No pk op for you")
def __del__(self):
- botan.botan_pk_op_verify_destroy.argtypes = [c_void_p]
botan.botan_pk_op_verify_destroy(self.op)
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, _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, _ctype_bits(signature), len(signature))
if rc == 0:
return True
@@ -574,10 +818,7 @@ class pk_op_verify(object): # pylint: disable=invalid-name
# Must be used with McEliece keys
#
def mceies_encrypt(mce, rng_instance, 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_vec(0, lambda b, bl:
+ return _call_fn_returning_vec(len(pt) + 1024, lambda b, bl:
botan.botan_mceies_encrypt(mce.pubkey,
rng_instance.rng,
_ctype_str(aead),
@@ -587,46 +828,36 @@ def mceies_encrypt(mce, rng_instance, aead, pt, 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)]
+def mceies_decrypt(mce, aead, ct, ad):
#msg = cast(msg, c_char_p)
#ll = c_size_t(ll)
- return _call_fn_returning_vec(0, lambda b, bl:
+ return _call_fn_returning_vec(len(ct), lambda b, bl:
botan.botan_mceies_decrypt(mce.privkey,
_ctype_str(aead),
- _ctype_bits(pt),
- len(pt),
+ _ctype_bits(ct),
+ len(ct),
_ctype_bits(ad),
len(ad),
b, bl))
class pk_op_key_agreement(object): # pylint: disable=invalid-name
def __init__(self, key, kdf_name):
- botan.botan_pk_op_key_agreement_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32]
- botan.botan_pk_op_key_agreement_export_public.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
self.op = c_void_p(0)
flags = c_uint32(0) # always zero in this ABI
botan.botan_pk_op_key_agreement_create(byref(self.op), key.privkey, kdf_name, flags)
- if not self.op:
- raise BotanException("No key agreement for you")
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]
botan.botan_pk_op_key_agreement_destroy(self.op)
def public_value(self):
return self.m_public_value
def agree(self, other, key_len, salt):
- 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_vec(key_len, lambda b, bl:
botan.botan_pk_op_key_agreement(self.op, b, bl,
other, len(other),
@@ -642,89 +873,73 @@ class x509_cert(object): # pylint: disable=invalid-name
if filename is not None and buf is not None:
raise BotanException("Both filename and buf given")
elif filename is not None:
- botan.botan_x509_cert_load_file.argtypes = [POINTER(c_void_p), c_char_p]
self.x509_cert = c_void_p(0)
botan.botan_x509_cert_load_file(byref(self.x509_cert), _ctype_str(filename))
elif buf is not None:
- botan.botan_x509_cert_load.argtypes = [POINTER(c_void_p), POINTER(c_char), c_size_t]
self.x509_cert = c_void_p(0)
botan.botan_x509_cert_load(byref(self.x509_cert), _ctype_bits(buf), len(buf))
def __del__(self):
- botan.botan_x509_cert_destroy.argtypes = [c_void_p]
botan.botan_x509_cert_destroy(self.x509_cert)
def time_starts(self):
- botan.botan_x509_cert_get_time_starts.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
starts = _call_fn_returning_string(
16, lambda b, bl: botan.botan_x509_cert_get_time_starts(self.x509_cert, b, bl))
if len(starts) == 13:
# UTC time
- struct_time = time.strptime(starts, "%y%m%d%H%M%SZ")
+ struct_time = strptime(starts, "%y%m%d%H%M%SZ")
elif len(starts) == 15:
# Generalized time
- struct_time = time.strptime(starts, "%Y%m%d%H%M%SZ")
+ struct_time = strptime(starts, "%Y%m%d%H%M%SZ")
else:
- raise BotanException("Wrong date/time format")
+ raise BotanException("Unexpected date/time format for x509 start time")
- return datetime.fromtimestamp(time.mktime(struct_time))
+ return datetime.fromtimestamp(mktime(struct_time))
def time_expires(self):
- botan.botan_x509_cert_get_time_expires.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
expires = _call_fn_returning_string(
16, lambda b, bl: botan.botan_x509_cert_get_time_expires(self.x509_cert, b, bl))
if len(expires) == 13:
# UTC time
- struct_time = time.strptime(expires, "%y%m%d%H%M%SZ")
+ struct_time = strptime(expires, "%y%m%d%H%M%SZ")
elif len(expires) == 15:
# Generalized time
- struct_time = time.strptime(expires, "%Y%m%d%H%M%SZ")
+ struct_time = strptime(expires, "%Y%m%d%H%M%SZ")
else:
- raise BotanException("Wrong date/time format")
- return datetime.fromtimestamp(time.mktime(struct_time))
+ raise BotanException("Unexpected date/time format for x509 expire time")
+
+ return datetime.fromtimestamp(mktime(struct_time))
def to_string(self):
- botan.botan_x509_cert_to_string.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
return _call_fn_returning_string(
- 0, lambda b, bl: botan.botan_x509_cert_to_string(self.x509_cert, b, bl))
+ 4096, lambda b, bl: botan.botan_x509_cert_to_string(self.x509_cert, b, bl))
def fingerprint(self, hash_algo='SHA-256'):
- botan.botan_x509_cert_get_fingerprint.argtypes = [c_void_p, c_char_p,
- POINTER(c_char), POINTER(c_size_t)]
-
n = hash_function(hash_algo).output_length() * 3
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)]
return _call_fn_returning_vec(
- 0, lambda b, bl: botan.botan_x509_cert_get_serial_number(self.x509_cert, b, bl))
+ 32, lambda b, bl: botan.botan_x509_cert_get_serial_number(self.x509_cert, b, bl))
def authority_key_id(self):
- botan.botan_x509_cert_get_authority_key_id.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
return _call_fn_returning_vec(
- 0, lambda b, bl: botan.botan_x509_cert_get_authority_key_id(self.x509_cert, b, bl))
+ 32, lambda b, bl: botan.botan_x509_cert_get_authority_key_id(self.x509_cert, b, bl))
def subject_key_id(self):
- botan.botan_x509_cert_get_subject_key_id.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
return _call_fn_returning_vec(
- 0, lambda b, bl: botan.botan_x509_cert_get_subject_key_id(self.x509_cert, b, bl))
+ 32, lambda b, bl: botan.botan_x509_cert_get_subject_key_id(self.x509_cert, b, bl))
def subject_public_key_bits(self):
- botan.botan_x509_cert_get_public_key_bits.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
return _call_fn_returning_vec(
- 0, lambda b, bl: botan.botan_x509_cert_get_public_key_bits(self.x509_cert, b, bl))
+ 512, lambda b, bl: botan.botan_x509_cert_get_public_key_bits(self.x509_cert, b, bl))
def subject_public_key(self):
- botan.botan_x509_cert_get_public_key.argtypes = [c_void_p, c_void_p]
pub = c_void_p(0)
botan.botan_x509_cert_get_public_key(self.x509_cert, byref(pub))
-
return public_key(pub)
def subject_dn(self, key, index):
- botan.botan_x509_cert_get_subject_dn.argtypes = [
- c_void_p, c_char_p, c_size_t, POINTER(c_char), POINTER(c_size_t)]
return _call_fn_returning_string(
0, lambda b, bl: botan.botan_x509_cert_get_subject_dn(self.x509_cert, _ctype_str(key), index, b, bl))
diff --git a/src/scripts/test_python.py b/src/scripts/test_python.py
index fa3f49655..4bd0085ab 100644
--- a/src/scripts/test_python.py
+++ b/src/scripts/test_python.py
@@ -6,7 +6,7 @@
Botan is released under the Simplified BSD License (see license.txt)
"""
-import sys
+import unittest
import binascii
import botan2
@@ -16,93 +16,118 @@ def hex_encode(buf):
def hex_decode(buf):
return binascii.unhexlify(buf.encode('ascii'))
-def test():
- # pylint: disable=too-many-statements
+class BotanPythonTests(unittest.TestCase):
- def test_version():
+ def test_version(self):
+ version_str = botan2.version_string()
+ self.assertTrue(version_str.startswith('Botan '))
- print("\n%s" % botan2.version_string())
- print("v%d.%d.%d\n" % (botan2.version_major(), botan2.version_minor(), botan2.version_patch()))
- print("\nPython %s\n" % sys.version.replace('\n', ' '))
+ self.assertEqual(botan2.version_major(), 2)
+ self.assertTrue(botan2.version_minor() >= 8)
- def test_kdf():
- print("KDF2(SHA-1) %s" %
- hex_encode(botan2.kdf('KDF2(SHA-1)',
- hex_decode('701F3480DFE95F57941F804B1B2413EF'), 7,
- hex_decode('55A4E9DD5F4CA2EF82'), hex_decode(''))))
+ def test_kdf(self):
- def test_pbkdf():
- print("PBKDF2(SHA-1) %s" %
- hex_encode(botan2.pbkdf('PBKDF2(SHA-1)', '', 32, 10000, hex_decode('0001020304050607'))[2]))
- print("good output %s\n" %
- '59B2B1143B4CB1059EC58D9722FB1C72471E0D85C6F7543BA5228526375B0127')
+ secret = hex_decode('6FD4C3C0F38E5C7A6F83E99CD9BD')
+ salt = hex_decode('DBB986')
+ label = hex_decode('')
+ expected = hex_decode('02AEB40A3D4B66FBA540F9D4B20006F2046E0F3A029DEAB201FC692B79EB27CEF7E16069046A')
- (salt, iterations, psk) = botan2.pbkdf_timed('PBKDF2(SHA-256)', 'xyz', 32, 200)
+ produced = botan2.kdf('KDF2(SHA-1)', secret, 38, salt, label)
- print("PBKDF2(SHA-256) x=timed, y=iterated; salt = %s (len=%d) #iterations = %d\n" %
- (hex_encode(salt), len(salt), iterations))
+ self.assertEqual(hex_encode(produced), hex_encode(expected))
- print('x %s' % hex_encode(psk))
- print('y %s\n' % (hex_encode(botan2.pbkdf('PBKDF2(SHA-256)', 'xyz', 32, iterations, salt)[2])))
+ def test_pbkdf(self):
- def test_bcrypt():
- print("Testing Bcrypt...")
+ (salt, iterations, pbkdf) = botan2.pbkdf('PBKDF2(SHA-1)', '', 32, 10000, hex_decode('0001020304050607'))
+
+ self.assertEqual(iterations, 10000)
+ self.assertEqual(hex_encode(pbkdf),
+ '59b2b1143b4cb1059ec58d9722fb1c72471e0d85c6f7543ba5228526375b0127')
+
+ (salt, iterations, pbkdf) = botan2.pbkdf_timed('PBKDF2(SHA-256)', 'xyz', 32, 200)
+
+ cmp_pbkdf = botan2.pbkdf('PBKDF2(SHA-256)', 'xyz', 32, iterations, salt)[2]
+
+ self.assertEqual(pbkdf, cmp_pbkdf)
+
+ def test_scrypt(self):
+ scrypt = botan2.scrypt(10, '', '', 16, 1, 1)
+ self.assertEqual(hex_encode(scrypt), "77d6576238657b203b19")
+
+ scrypt = botan2.scrypt(32, 'password', 'NaCl', 1024, 8, 16)
+ self.assertEqual(hex_encode(scrypt), "fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b373162")
+
+ def test_bcrypt(self):
r = botan2.rng()
phash = botan2.bcrypt('testing', r)
- print("bcrypt returned %s (%d bytes)" % (hex_encode(phash), len(phash)))
- print("validating the hash produced: %r" % (botan2.check_bcrypt('testing', phash)))
- print("\n")
+ self.assertTrue(isinstance(phash, str))
+ self.assertTrue(phash.startswith("$2a$"))
- def test_hmac():
+ self.assertTrue(botan2.check_bcrypt('testing', phash))
+ self.assertFalse(botan2.check_bcrypt('live fire', phash))
+
+ self.assertTrue(botan2.check_bcrypt('test', '$2a$04$wjen1fAA.UW6UxthpKK.huyOoxvCR7ATRCVC4CBIEGVDOCtr8Oj1C'))
+
+ def test_hmac(self):
hmac = botan2.message_authentication_code('HMAC(SHA-256)')
+ self.assertEqual(hmac.algo_name(), 'HMAC(SHA-256)')
hmac.set_key(hex_decode('0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20'))
hmac.update(hex_decode('616263'))
- hmac_vec = hex_decode('A21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181')
- hmac_output = hmac.final()
+ expected = hex_decode('A21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181')
+ produced = hmac.final()
- 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))
+ self.assertEqual(hex_encode(expected), hex_encode(produced))
- def test_rng():
+ def test_rng(self):
user_rng = botan2.rng("user")
- 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))))
+ output1 = user_rng.get(32)
+ output2 = user_rng.get(32)
+
+ self.assertEqual(len(output1), 32)
+ self.assertEqual(len(output2), 32)
+ self.assertNotEqual(output1, output2)
- def test_hash():
- md5 = botan2.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'))
+ output3 = user_rng.get(1021)
+ self.assertEqual(len(output3), 1021)
- md5.update(hex_decode('f468025b'))
- h2 = md5.final()
- print("md5 hash: %s (%s)\n" % (hex_encode(h2), '47efd2be302a937775e93dea281b6751'))
+ def test_hash(self):
+ h = botan2.hash_function('SHA-256')
+ self.assertEqual(h.algo_name(), 'SHA-256')
+ assert h.output_length() == 32
+ h.update('ignore this please')
+ h.clear()
+ h.update('a')
+ h1 = h.final()
- def test_cipher():
+ self.assertEqual(hex_encode(h1), "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb")
+
+ h.update(hex_decode('616263'))
+ h2 = h.final()
+ self.assertEqual(hex_encode(h2), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
+
+ def test_cipher(self):
for mode in ['AES-128/CTR-BE', 'Serpent/GCM', 'ChaCha20Poly1305']:
enc = botan2.cipher(mode, encrypt=True)
+ if mode == 'AES-128/CTR-BE':
+ self.assertEqual(enc.algo_name(), 'CTR-BE(AES-128)')
+ elif mode == 'Serpent/GCM':
+ self.assertEqual(enc.algo_name(), 'Serpent/GCM(16)')
+ else:
+ self.assertEqual(enc.algo_name(), mode)
+
(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))
+
+ self.assertTrue(kmin <= kmax)
rng = botan2.rng()
iv = rng.get(enc.default_nonce_length())
key = rng.get(kmax)
pt = rng.get(21)
- print(" plaintext %s (%d)" % (hex_encode(pt), len(pt)))
-
enc.set_key(key)
enc.start(iv)
@@ -110,61 +135,47 @@ def test():
assert not update_result
ct = enc.finish(pt)
- print(" ciphertext %s (%d)" % (hex_encode(ct), len(ct)))
dec = botan2.cipher(mode, encrypt=False)
dec.set_key(key)
dec.start(iv)
decrypted = dec.finish(ct)
- print(" decrypted %s (%d)\n" % (hex_encode(decrypted), len(decrypted)))
+ self.assertEqual(decrypted, pt)
- def test_mceliece():
- mce_priv = botan2.private_key('mce', [2960, 57], botan2.rng())
+ def test_mceliece(self):
+ rng = botan2.rng()
+ mce_priv = botan2.private_key('mce', [2960, 57], rng)
mce_pub = mce_priv.get_public_key()
+ self.assertEqual(mce_pub.estimated_strength(), 128)
- mce_plaintext = 'mce plaintext'
- mce_ad = 'mce AD'
+ mce_plaintext = rng.get(16)
+ mce_ad = rng.get(48)
mce_ciphertext = botan2.mceies_encrypt(mce_pub, botan2.rng(), 'ChaCha20Poly1305', mce_plaintext, mce_ad)
- print("mceies len(pt)=%d len(ct)=%d" % (len(mce_plaintext), len(mce_ciphertext)))
-
mce_decrypt = botan2.mceies_decrypt(mce_priv, 'ChaCha20Poly1305', mce_ciphertext, mce_ad)
- print(" mceies plaintext \'%s\' (%d)" % (mce_plaintext, len(mce_plaintext)))
-
- # Since mceies_decrypt() returns bytes in Python3, the following line
- # needs .decode('utf-8') to convert mce_decrypt from bytes to a
- # text string (Unicode).
- # You don't need to add .decode() if
- # (a) your expected output is bytes rather than a text string, or
- # (b) you are using Python2 rather than Python3.
- print(" mceies decrypted \'%s\' (%d)" % (mce_decrypt.decode('utf-8'), len(mce_decrypt)))
-
- print("mce_pub %s/SHA-1 fingerprint: %s\nEstimated strength %s bits (len %d)\n" % (
- mce_pub.algo_name(), mce_pub.fingerprint("SHA-1"),
- mce_pub.estimated_strength(), len(mce_pub.encoding())
- ))
-
- def test_rsa():
- rsapriv = botan2.private_key('rsa', 1536, botan2.rng())
+
+ self.assertEqual(mce_plaintext, mce_decrypt)
+
+ def test_rsa(self):
+ rng = botan2.rng()
+ rsapriv = botan2.private_key('RSA', '1024', rng)
+ self.assertEqual(rsapriv.algo_name(), 'RSA')
+
rsapub = rsapriv.get_public_key()
+ self.assertEqual(rsapub.algo_name(), 'RSA')
+ self.assertEqual(rsapub.estimated_strength(), 80)
+
+ enc = botan2.pk_op_encrypt(rsapub, "OAEP(SHA-256)")
+ dec = botan2.pk_op_decrypt(rsapriv, "OAEP(SHA-256)")
- 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())
- ))
+ symkey = rng.get(32)
+ ctext = enc.encrypt(symkey, rng)
- dec = botan2.pk_op_decrypt(rsapriv, "EME1(SHA-256)")
- enc = botan2.pk_op_encrypt(rsapub, "EME1(SHA-256)")
+ ptext = dec.decrypt(ctext)
- sys_rng = botan2.rng()
- symkey = sys_rng.get(32)
- ctext = enc.encrypt(symkey, sys_rng)
- print("ptext \'%s\' (%d)" % (hex_encode(symkey), len(symkey)))
- print("ctext \'%s\' (%d)" % (hex_encode(ctext), len(ctext)))
- print("decrypt \'%s\' (%d)\n" % (hex_encode(dec.decrypt(ctext)),
- len(dec.decrypt(ctext))))
+ self.assertEqual(ptext, symkey)
signer = botan2.pk_op_sign(rsapriv, 'EMSA4(SHA-384)')
@@ -172,29 +183,27 @@ def test():
signer.update('ge')
sig = signer.finish(botan2.rng())
- print("EMSA4(SHA-384) signature: %s" % hex_encode(sig))
-
verify = botan2.pk_op_verify(rsapub, 'EMSA4(SHA-384)')
verify.update('mess')
verify.update('age')
- print("good sig accepted? %s" % verify.check_signature(sig))
+ self.assertTrue(verify.check_signature(sig))
verify.update('mess of things')
verify.update('age')
- print("bad sig accepted? %s" % verify.check_signature(sig))
+ self.assertFalse(verify.check_signature(sig))
verify.update('message')
- print("good sig accepted? %s\n" % verify.check_signature(sig))
+ self.assertTrue(verify.check_signature(sig))
- def test_dh():
+ def test_dh(self):
a_rng = botan2.rng('user')
b_rng = botan2.rng('user')
for dh_grp in ['secp256r1', 'curve25519']:
dh_kdf = 'KDF2(SHA-384)'.encode('utf-8')
- a_dh_priv = botan2.private_key('ecdh', dh_grp, botan2.rng())
- b_dh_priv = botan2.private_key('ecdh', dh_grp, botan2.rng())
+ a_dh_priv = botan2.private_key('ecdh', dh_grp, a_rng)
+ b_dh_priv = botan2.private_key('ecdh', dh_grp, b_rng)
a_dh = botan2.pk_op_key_agreement(a_dh_priv, dh_kdf)
b_dh = botan2.pk_op_key_agreement(b_dh_priv, dh_kdf)
@@ -202,65 +211,34 @@ def test():
a_dh_pub = a_dh.public_value()
b_dh_pub = b_dh.public_value()
- a_salt = a_rng.get(8)
- b_salt = b_rng.get(8)
-
- print("ecdh %s pubs:\n %s (salt %s)\n %s (salt %s)\n" %
- (dh_grp,
- hex_encode(a_dh_pub), hex_encode(a_salt),
- hex_encode(b_dh_pub), hex_encode(b_salt)))
+ salt = a_rng.get(8) + b_rng.get(8)
- a_key = a_dh.agree(b_dh_pub, 32, a_salt + b_salt)
- b_key = b_dh.agree(a_dh_pub, 32, a_salt + b_salt)
+ a_key = a_dh.agree(b_dh_pub, 32, salt)
+ b_key = b_dh.agree(a_dh_pub, 32, salt)
- print("ecdh %s shared:\n %s\n %s\n" %
- (dh_grp, hex_encode(a_key), hex_encode(b_key)))
+ self.assertEqual(a_key, b_key)
- def test_certs():
+ def test_certs(self):
cert = botan2.x509_cert(filename="src/tests/data/x509/ecc/CSCA.CSCA.csca-germany.1.crt")
- print("CSCA (Germany) Certificate\nDetails:")
- print("SHA-1 fingerprint: %s" % cert.fingerprint("SHA-1"))
- print("Expected: 32:42:1C:C3:EC:54:D7:E9:43:EC:51:F0:19:23:BD:85:1D:F2:1B:B9")
+ pubkey = cert.subject_public_key()
- print("Not before: %s" % cert.time_starts())
- print("Not after: %s" % cert.time_expires())
+ self.assertEqual(pubkey.algo_name(), 'ECDSA')
+ self.assertEqual(pubkey.estimated_strength(), 112)
- print("Serial number: %s" % hex_encode(cert.serial_number()))
- print("Authority Key ID: %s" % hex_encode(cert.authority_key_id()))
- print("Subject Key ID: %s" % hex_encode(cert.subject_key_id()))
- print("Public key bits:\n%s\n" % binascii.b2a_base64(cert.subject_public_key_bits()))
+ self.assertEqual(cert.fingerprint("SHA-1"),
+ "32:42:1C:C3:EC:54:D7:E9:43:EC:51:F0:19:23:BD:85:1D:F2:1B:B9")
- pubkey = cert.subject_public_key()
- print("Public key algo: %s" % pubkey.algo_name())
- print("Public key strength: %s" % pubkey.estimated_strength() + " bits")
-
- dn_fields = ("Name", "Email", "Organization", "Organizational Unit", "Country")
- for field in dn_fields:
- try:
- print("%s: %s" % (field, cert.subject_dn(field, 0)))
- except botan2.BotanException:
- print("Field: %s not found in certificate" % field)
-
- print(cert.to_string())
-
- test_version()
- test_kdf()
- test_pbkdf()
- test_bcrypt()
- test_hmac()
- test_rng()
- test_hash()
- test_cipher()
- test_mceliece()
- test_rsa()
- test_dh()
- test_certs()
-
-
-def main(args=None):
- if args is None:
- args = sys.argv
- test()
+ self.assertEqual(hex_encode(cert.serial_number()), "01")
+ self.assertEqual(hex_encode(cert.authority_key_id()),
+ "0096452de588f966c4ccdf161dd1f3f5341b71e7")
+
+ self.assertEqual(cert.subject_dn('Name', 0), 'csca-germany')
+ self.assertEqual(cert.subject_dn('Email', 0), '[email protected]')
+ self.assertEqual(cert.subject_dn('Organization', 0), 'bund')
+ self.assertEqual(cert.subject_dn('Organizational Unit', 0), 'bsi')
+ self.assertEqual(cert.subject_dn('Country', 0), 'DE')
+
+ self.assertTrue(cert.to_string().startswith("Version: 3"))
if __name__ == '__main__':
- sys.exit(main())
+ unittest.main()