diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/block/idea/idea.cpp | 28 | ||||
-rw-r--r-- | src/lib/block/idea_sse2/idea_sse2.cpp | 12 | ||||
-rw-r--r-- | src/lib/ffi/ffi.cpp | 10 | ||||
-rw-r--r-- | src/lib/ffi/ffi.h | 4 | ||||
-rw-r--r-- | src/lib/ffi/info.txt | 2 | ||||
-rw-r--r-- | src/lib/math/mp/mp_monty.cpp | 23 | ||||
-rw-r--r-- | src/lib/pk_pad/eme_oaep/oaep.cpp | 16 | ||||
-rw-r--r-- | src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp | 16 | ||||
-rw-r--r-- | src/lib/pubkey/curve25519/donna.cpp | 10 | ||||
-rw-r--r-- | src/lib/pubkey/pk_keys.cpp | 6 | ||||
-rw-r--r-- | src/lib/utils/ct_utils.h | 119 | ||||
-rwxr-xr-x | src/python/botan.py | 539 | ||||
-rwxr-xr-x | src/scripts/ci/travis/build.sh | 8 | ||||
-rwxr-xr-x | src/scripts/ci/travis/install_osx_packages.sh | 2 |
14 files changed, 413 insertions, 382 deletions
diff --git a/src/lib/block/idea/idea.cpp b/src/lib/block/idea/idea.cpp index c7706b372..8069e16f7 100644 --- a/src/lib/block/idea/idea.cpp +++ b/src/lib/block/idea/idea.cpp @@ -20,7 +20,7 @@ inline u16bit mul(u16bit x, u16bit y) { const u32bit P = static_cast<u32bit>(x) * y; - const u16bit Z_mask = static_cast<u16bit>(ct_expand_mask_32(P) & 0xFFFF); + const u16bit Z_mask = static_cast<u16bit>(CT::expand_mask(P) & 0xFFFF); const u32bit P_hi = P >> 16; const u32bit P_lo = P & 0xFFFF; @@ -28,7 +28,7 @@ inline u16bit mul(u16bit x, u16bit y) const u16bit r_1 = (P_lo - P_hi) + (P_lo < P_hi); const u16bit r_2 = 1 - x - y; - return ct_select_mask_16(Z_mask, r_1, r_2); + return CT::select(Z_mask, r_1, r_2); } /* @@ -62,9 +62,9 @@ void idea_op(const byte in[], byte out[], size_t blocks, const u16bit K[52]) { const size_t BLOCK_SIZE = 8; - BOTAN_CONST_TIME_POISON(in, blocks * 8); - BOTAN_CONST_TIME_POISON(out, blocks * 8); - BOTAN_CONST_TIME_POISON(K, 52 * 2); + CT::poison(in, blocks * 8); + CT::poison(out, blocks * 8); + CT::poison(K, 52); for(size_t i = 0; i != blocks; ++i) { @@ -101,9 +101,9 @@ void idea_op(const byte in[], byte out[], size_t blocks, const u16bit K[52]) store_be(out + BLOCK_SIZE*i, X1, X3, X2, X4); } - BOTAN_CONST_TIME_UNPOISON(in, blocks * 8); - BOTAN_CONST_TIME_UNPOISON(out, blocks * 8); - BOTAN_CONST_TIME_UNPOISON(K, 52 * 2); + CT::unpoison(in, blocks * 8); + CT::unpoison(out, blocks * 8); + CT::unpoison(K, 52); } } @@ -132,9 +132,9 @@ void IDEA::key_schedule(const byte key[], size_t) EK.resize(52); DK.resize(52); - BOTAN_CONST_TIME_POISON(key, 16); - BOTAN_CONST_TIME_POISON(EK.data(), 52 * 2); - BOTAN_CONST_TIME_POISON(DK.data(), 52 * 2); + CT::poison(key, 16); + CT::poison(EK.data(), 52); + CT::poison(DK.data(), 52); for(size_t i = 0; i != 8; ++i) EK[i] = load_be<u16bit>(key, i); @@ -168,9 +168,9 @@ void IDEA::key_schedule(const byte key[], size_t) DK[1] = -EK[49]; DK[0] = mul_inv(EK[48]); - BOTAN_CONST_TIME_UNPOISON(key, 16); - BOTAN_CONST_TIME_UNPOISON(EK.data(), 52 * 2); - BOTAN_CONST_TIME_UNPOISON(DK.data(), 52 * 2); + CT::unpoison(key, 16); + CT::unpoison(EK.data(), 52); + CT::unpoison(DK.data(), 52); } void IDEA::clear() diff --git a/src/lib/block/idea_sse2/idea_sse2.cpp b/src/lib/block/idea_sse2/idea_sse2.cpp index 51b5e909b..c7d846e8b 100644 --- a/src/lib/block/idea_sse2/idea_sse2.cpp +++ b/src/lib/block/idea_sse2/idea_sse2.cpp @@ -131,9 +131,9 @@ void transpose_out(__m128i& B0, __m128i& B1, __m128i& B2, __m128i& B3) */ void idea_op_8(const byte in[64], byte out[64], const u16bit EK[52]) { - BOTAN_CONST_TIME_POISON(in, 64); - BOTAN_CONST_TIME_POISON(out, 64); - BOTAN_CONST_TIME_POISON(EK, 52*2); + CT::poison(in, 64); + CT::poison(out, 64); + CT::poison(EK, 52); const __m128i* in_mm = reinterpret_cast<const __m128i*>(in); @@ -195,9 +195,9 @@ void idea_op_8(const byte in[64], byte out[64], const u16bit EK[52]) _mm_storeu_si128(out_mm + 2, B1); _mm_storeu_si128(out_mm + 3, B3); - BOTAN_CONST_TIME_UNPOISON(in, 64); - BOTAN_CONST_TIME_UNPOISON(out, 64); - BOTAN_CONST_TIME_UNPOISON(EK, 52*2); + CT::unpoison(in, 64); + CT::unpoison(out, 64); + CT::unpoison(EK, 52); } } diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp index eb30b4831..7ed279bbd 100644 --- a/src/lib/ffi/ffi.cpp +++ b/src/lib/ffi/ffi.cpp @@ -429,6 +429,16 @@ int botan_cipher_clear(botan_cipher_t cipher) return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, { cipher.clear(); }); } +int botan_cipher_query_keylen(botan_cipher_t cipher, + size_t* out_minimum_keylength, + size_t* out_maximum_keylength) + { + return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, { + *out_minimum_keylength = cipher.key_spec().minimum_keylength(); + *out_maximum_keylength = cipher.key_spec().maximum_keylength(); + }); + } + int botan_cipher_set_key(botan_cipher_t cipher, const uint8_t* key, size_t key_len) { diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index ce2253725..2def4f4d5 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -220,6 +220,10 @@ BOTAN_DLL int botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tag_siz BOTAN_DLL int botan_cipher_get_default_nonce_length(botan_cipher_t cipher, size_t* nl); BOTAN_DLL int botan_cipher_get_update_granularity(botan_cipher_t cipher, size_t* ug); +BOTAN_DLL int botan_cipher_query_keylen(botan_cipher_t, + size_t* out_minimum_keylength, + size_t* out_maximum_keylength); + BOTAN_DLL int botan_cipher_set_key(botan_cipher_t cipher, const uint8_t* key, size_t key_len); diff --git a/src/lib/ffi/info.txt b/src/lib/ffi/info.txt index 4018e4064..7c8968ff0 100644 --- a/src/lib/ffi/info.txt +++ b/src/lib/ffi/info.txt @@ -1,4 +1,4 @@ -define FFI 20151001 +define FFI 20151015 <requires> aead diff --git a/src/lib/math/mp/mp_monty.cpp b/src/lib/math/mp/mp_monty.cpp index 820f41e6c..7e427b540 100644 --- a/src/lib/math/mp/mp_monty.cpp +++ b/src/lib/math/mp/mp_monty.cpp @@ -9,6 +9,7 @@ #include <botan/internal/mp_core.h> #include <botan/internal/mp_madd.h> #include <botan/internal/mp_asmi.h> +#include <botan/internal/ct_utils.h> #include <botan/mem_ops.h> namespace Botan { @@ -22,6 +23,10 @@ void bigint_monty_redc(word z[], { const size_t z_size = 2*(p_size+1); + CT::poison(z, z_size); + CT::poison(p, p_size); + CT::poison(ws, 2*(p_size+1)); + const size_t blocks_of_8 = p_size - (p_size % 8); for(size_t i = 0; i != p_size; ++i) @@ -47,10 +52,10 @@ void bigint_monty_redc(word z[], carry = (z_sum < z_i[p_size]); z_i[p_size] = z_sum; - for(size_t j = p_size + 1; carry && j != z_size - i; ++j) + for(size_t j = p_size + 1; j < z_size - i; ++j) { - ++z_i[j]; - carry = !z_i[j]; + z_i[j] += carry; + carry = carry & !z_i[j]; } } @@ -73,12 +78,18 @@ void bigint_monty_redc(word z[], ws[p_size] = word_sub(z[p_size+p_size], 0, &borrow); - BOTAN_ASSERT(borrow == 0 || borrow == 1, "Expected borrow"); - copy_mem(ws + p_size + 1, z + p_size, p_size + 1); - copy_mem(z, ws + borrow*(p_size+1), p_size + 1); + CT::conditional_copy_mem(borrow, z, ws + (p_size + 1), ws, (p_size + 1)); clear_mem(z + p_size + 1, z_size - p_size - 1); + + CT::unpoison(z, z_size); + CT::unpoison(p, p_size); + CT::unpoison(ws, 2*(p_size+1)); + + // This check comes after we've used it but that's ok here + CT::unpoison(&borrow, 1); + BOTAN_ASSERT(borrow == 0 || borrow == 1, "Expected borrow"); } void bigint_monty_mul(word z[], size_t z_size, diff --git a/src/lib/pk_pad/eme_oaep/oaep.cpp b/src/lib/pk_pad/eme_oaep/oaep.cpp index b114afb8b..370a9fe45 100644 --- a/src/lib/pk_pad/eme_oaep/oaep.cpp +++ b/src/lib/pk_pad/eme_oaep/oaep.cpp @@ -84,7 +84,7 @@ secure_vector<byte> OAEP::unpad(const byte in[], size_t in_length, secure_vector<byte> input(key_length); buffer_insert(input, key_length - in_length, in, in_length); - BOTAN_CONST_TIME_POISON(input.data(), input.size()); + CT::poison(input.data(), input.size()); const size_t hlen = m_Phash.size(); @@ -102,25 +102,25 @@ secure_vector<byte> OAEP::unpad(const byte in[], size_t in_length, for(size_t i = delim_idx; i < input.size(); ++i) { - const byte zero_m = ct_is_zero_8(input[i]); - const byte one_m = ct_is_equal_8(input[i], 1); + const byte zero_m = CT::is_zero<byte>(input[i]); + const byte one_m = CT::is_equal<byte>(input[i], 1); const byte add_m = waiting_for_delim & zero_m; bad_input |= waiting_for_delim & ~(zero_m | one_m); - delim_idx += ct_select_mask_8(add_m, 1, 0); + delim_idx += CT::select<byte>(add_m, 1, 0); waiting_for_delim &= zero_m; } // If we never saw any non-zero byte, then it's not valid input bad_input |= waiting_for_delim; - bad_input |= ct_expand_mask_8(!same_mem(&input[hlen], m_Phash.data(), hlen)); + bad_input |= CT::expand_mask<byte>(!same_mem(&input[hlen], m_Phash.data(), hlen)); - BOTAN_CONST_TIME_UNPOISON(input.data(), input.size()); - BOTAN_CONST_TIME_UNPOISON(&bad_input, sizeof(bad_input)); - BOTAN_CONST_TIME_UNPOISON(&delim_idx, sizeof(delim_idx)); + CT::unpoison(input.data(), input.size()); + CT::unpoison(&bad_input, 1); + CT::unpoison(&delim_idx, 1); if(bad_input) throw Decoding_Error("Invalid OAEP encoding"); diff --git a/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp b/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp index 219e93251..6b3bce0aa 100644 --- a/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp +++ b/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp @@ -44,29 +44,29 @@ secure_vector<byte> EME_PKCS1v15::unpad(const byte in[], size_t inlen, if(inlen != key_len / 8 || inlen < 10) throw Decoding_Error("PKCS1::unpad"); - BOTAN_CONST_TIME_POISON(in, inlen); + CT::poison(in, inlen); byte bad_input_m = 0; byte seen_zero_m = 0; size_t delim_idx = 0; - bad_input_m |= ~ct_is_equal_8(in[0], 2); + bad_input_m |= ~CT::is_equal<byte>(in[0], 2); for(size_t i = 1; i != inlen; ++i) { - const byte is_zero_m = ct_is_zero_8(in[i]); + const byte is_zero_m = CT::is_zero<byte>(in[i]); - delim_idx += ct_select_mask_8(~seen_zero_m, 1, 0); + delim_idx += CT::select<byte>(~seen_zero_m, 1, 0); - bad_input_m |= is_zero_m & ct_expand_mask_8(i < 9); + bad_input_m |= is_zero_m & CT::expand_mask<byte>(i < 9); seen_zero_m |= is_zero_m; } bad_input_m |= ~seen_zero_m; - BOTAN_CONST_TIME_UNPOISON(in, inlen); - BOTAN_CONST_TIME_UNPOISON(&bad_input_m, sizeof(bad_input_m)); - BOTAN_CONST_TIME_UNPOISON(&delim_idx, sizeof(delim_idx)); + CT::unpoison(in, inlen); + CT::unpoison(&bad_input_m, 1); + CT::unpoison(&delim_idx, 1); if(bad_input_m) throw Decoding_Error("Invalid PKCS #1 v1.5 encryption padding"); diff --git a/src/lib/pubkey/curve25519/donna.cpp b/src/lib/pubkey/curve25519/donna.cpp index ab9363761..78966f745 100644 --- a/src/lib/pubkey/curve25519/donna.cpp +++ b/src/lib/pubkey/curve25519/donna.cpp @@ -420,8 +420,8 @@ crecip(felem out, const felem z) { int curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) { - BOTAN_CONST_TIME_POISON(secret, 32); - BOTAN_CONST_TIME_POISON(basepoint, 32); + CT::poison(secret, 32); + CT::poison(basepoint, 32); limb bp[5], x[5], z[5], zmone[5]; uint8_t e[32]; @@ -438,9 +438,9 @@ curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) { fmul(z, x, zmone); fcontract(mypublic, z); - BOTAN_CONST_TIME_UNPOISON(secret, 32); - BOTAN_CONST_TIME_UNPOISON(basepoint, 32); - BOTAN_CONST_TIME_UNPOISON(mypublic, 32); + CT::unpoison(secret, 32); + CT::unpoison(basepoint, 32); + CT::unpoison(mypublic, 32); return 0; } diff --git a/src/lib/pubkey/pk_keys.cpp b/src/lib/pubkey/pk_keys.cpp index f92492fa9..635934037 100644 --- a/src/lib/pubkey/pk_keys.cpp +++ b/src/lib/pubkey/pk_keys.cpp @@ -31,7 +31,7 @@ OID Public_Key::get_oid() const void Public_Key::load_check(RandomNumberGenerator& rng) const { if(!check_key(rng, BOTAN_PUBLIC_KEY_STRONG_CHECKS_ON_LOAD)) - throw Invalid_Argument(algo_name() + ": Invalid public key"); + throw Invalid_Argument("Invalid public key"); } /* @@ -40,7 +40,7 @@ void Public_Key::load_check(RandomNumberGenerator& rng) const void Private_Key::load_check(RandomNumberGenerator& rng) const { if(!check_key(rng, BOTAN_PRIVATE_KEY_STRONG_CHECKS_ON_LOAD)) - throw Invalid_Argument(algo_name() + ": Invalid private key"); + throw Invalid_Argument("Invalid private key"); } /* @@ -49,7 +49,7 @@ void Private_Key::load_check(RandomNumberGenerator& rng) const void Private_Key::gen_check(RandomNumberGenerator& rng) const { if(!check_key(rng, BOTAN_PRIVATE_KEY_STRONG_CHECKS_ON_GENERATE)) - throw Self_Test_Failure(algo_name() + " private key generation failed"); + throw Self_Test_Failure("Private key generation failed"); } } diff --git a/src/lib/utils/ct_utils.h b/src/lib/utils/ct_utils.h index 4ae735330..52a3bc388 100644 --- a/src/lib/utils/ct_utils.h +++ b/src/lib/utils/ct_utils.h @@ -27,105 +27,82 @@ extern "C" void ct_unpoison(const void* address, size_t length); namespace Botan { -#if defined(BOTAN_USE_CTGRIND) - -#define BOTAN_CONST_TIME_POISON(p, l) ct_poison(p, l) -#define BOTAN_CONST_TIME_UNPOISON(p, l) ct_unpoison(p, l) +namespace CT { +template<typename T> +inline void poison(T* p, size_t n) + { +#if defined(BOTAN_USE_CTGRIND) + ct_poison(p, sizeof(T)*n); #else + BOTAN_UNUSED(p); + BOTAN_UNUSED(n); +#endif + } -#define BOTAN_CONST_TIME_POISON(p, l) -#define BOTAN_CONST_TIME_UNPOISON(p, l) - +template<typename T> +inline void unpoison(T* p, size_t n) + { +#if defined(BOTAN_USE_CTGRIND) + ct_unpoison(p, sizeof(T)*n); +#else + BOTAN_UNUSED(p); + BOTAN_UNUSED(n); #endif + } /* +* T should be an unsigned machine integer type * Expand to a mask used for other operations * @param in an integer -* @return 0 if in == 0 else 0xFFFFFFFF +* @return If n is zero, returns zero. Otherwise +* returns a T with all bits set for use as a mask with +* select. */ -inline uint32_t ct_expand_mask_32(uint32_t x) +template<typename T> +inline T expand_mask(T x) { - // First fold x down to a single bit: - uint32_t r = x; - r |= r >> 16; - r |= r >> 8; - r |= r >> 4; - r |= r >> 2; - r |= r >> 1; + T r = x; + // First fold r down to a single bit + for(size_t i = 1; i != sizeof(T)*8; i *= 2) + r |= r >> i; r &= 1; - // assumes 2s complement signed representation r = ~(r - 1); return r; } -inline uint32_t ct_select_mask_32(uint32_t mask, uint32_t a, uint32_t b) +template<typename T> +inline T select(T mask, T from0, T from1) { - return (a & mask) | (b & ~mask); + return (from0 & mask) | (from1 & ~mask); } -inline uint32_t ct_is_zero_32(uint32_t x) +template<typename T> +inline T is_zero(T x) { - return ~ct_expand_mask_32(x); + return ~expand_mask(x); } -inline uint32_t ct_is_equal_32(uint32_t x, uint32_t y) +template<typename T> +inline T is_equal(T x, T y) { - return ct_is_zero_32(x ^ y); + return is_zero(x ^ y); } -inline uint16_t ct_expand_mask_16(uint16_t x) +template<typename T> +inline void conditional_copy_mem(T value, + T* to, + const T* from0, + const T* from1, + size_t bytes) { - uint16_t r = x; - r |= r >> 8; - r |= r >> 4; - r |= r >> 2; - r |= r >> 1; - r &= 1; - r = ~(r - 1); - return r; - } + const T mask = CT::expand_mask(value); -inline uint16_t ct_select_mask_16(uint16_t mask, uint16_t a, uint16_t b) - { - return (a & mask) | (b & ~mask); - } - -inline uint16_t ct_is_zero_16(uint16_t x) - { - return ~ct_expand_mask_16(x); + for(size_t i = 0; i != bytes; ++i) + to[i] = CT::select(mask, from0[i], from1[i]); } -inline uint16_t ct_is_equal_16(uint16_t x, uint16_t y) - { - return ct_is_zero_16(x ^ y); - } - -inline uint8_t ct_expand_mask_8(uint8_t x) - { - uint8_t r = x; - r |= r >> 4; - r |= r >> 2; - r |= r >> 1; - r &= 1; - r = ~(r - 1); - return r; - } - -inline uint8_t ct_select_mask_8(uint8_t mask, uint8_t a, uint8_t b) - { - return (a & mask) | (b & ~mask); - } - -inline uint8_t ct_is_zero_8(uint8_t x) - { - return ~ct_expand_mask_8(x); - } - -inline uint8_t ct_is_equal_8(uint8_t x, uint8_t y) - { - return ct_is_zero_8(x ^ y); - } +} } diff --git a/src/python/botan.py b/src/python/botan.py index 04e574746..95b71d6bc 100755 --- a/src/python/botan.py +++ b/src/python/botan.py @@ -1,20 +1,26 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python -""" -Python wrapper of the botan crypto library +"""Python wrapper of the botan crypto library http://botan.randombit.net (C) 2015 Jack Lloyd (C) 2015 Uri Blumenthal (extensions and patches) 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. Right now the C API +is versioned but as it is still in evolution, no provisions are made +for handling more than a single API version in this module. So this +module should be used only with the library version it accompanied. """ import sys from ctypes import * from binascii import hexlify, unhexlify - """ Module initialization """ @@ -23,14 +29,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 +45,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.encode('ascii')) """ Versions @@ -77,7 +103,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 +113,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 +130,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 +140,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 +160,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 +176,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 +206,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 +233,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 +278,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 +286,7 @@ class cipher(object): botan.botan_cipher_update(self.cipher, flags, out, out_sz, byref(out_written), - inp, inp_sz, byref(inp_consumed)) + _ctype_bits(inp), inp_sz, byref(inp_consumed)) # buffering not supported yet assert inp_consumed.value == inp_sz.value @@ -315,7 +326,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 +334,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 +344,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 +370,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 +380,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 +392,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 +436,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 +454,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 +467,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 +483,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 +491,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 +501,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 +515,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 +525,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 +542,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 +578,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 +591,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 +624,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 +648,210 @@ Tests and examples """ def test(): - r = rng("user") - - print("\n%s" % version_string().decode('utf-8')) - print("v%d.%d.%d\n" % (version_major(), version_minor(), version_patch())) + def test_version(): - print("KDF2(SHA-1) %s" % - hexlify(kdf('KDF2(SHA-1)'.encode('ascii'), unhexlify('701F3480DFE95F57941F804B1B2413EF'), 7, - unhexlify('55A4E9DD5F4CA2EF82')) - ).decode('ascii') - ) + print("\n%s" % version_string()) + print("v%d.%d.%d\n" % (version_major(), version_minor(), version_patch())) + print("\nPython %s\n" % sys.version.replace('\n', ' ')) - 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') + def test_kdf(): + print("KDF2(SHA-1) %s" % + hex_encode(kdf('KDF2(SHA-1)', hex_decode('701F3480DFE95F57941F804B1B2413EF'), 7, + hex_decode('55A4E9DD5F4CA2EF82')))) - (salt,iterations,psk) = pbkdf_timed('PBKDF2(SHA-256)'.encode('ascii'), - 'xyz'.encode('utf-8'), 32, 200) + 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-256) x=timed, y=iterated; salt = %s (len=%d) #iterations = %d\n" % - (hexlify(salt).decode('ascii'), len(salt), iterations)) + (salt,iterations,psk) = pbkdf_timed('PBKDF2(SHA-256)', 'xyz', 32, 200) - 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'))) + print("PBKDF2(SHA-256) x=timed, y=iterated; salt = %s (len=%d) #iterations = %d\n" % + (hex_encode(salt), len(salt), iterations)) - hmac = message_authentication_code('HMAC(SHA-256)'.encode('ascii')) - hmac.set_key(unhexlify('0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20')) - hmac.update(unhexlify('616263')) + print('x %s' % hex_encode(psk)) + print('y %s\n' % (hex_encode(pbkdf('PBKDF2(SHA-256)', 'xyz', 32, iterations, salt)[2]))) - hmac_output = hmac.final() + def test_hmac(): - 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')) + hmac = message_authentication_code('HMAC(SHA-256)') + hmac.set_key(hex_decode('0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20')) + hmac.update(hex_decode('616263')) - 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') - ) - ) + hmac_vec = hex_decode('A21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181') + hmac_output = hmac.final() - 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')) - - - 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) - - 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')) - - 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))) - - 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) - - 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')) - - 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))) - - - mce_priv = private_key('mce', [2960,57], r) - 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) - - print "mce", len(mce_plaintext), len(mce_ciphertext) - - mce_decrypt = mceies_decrypt(mce_priv, 'ChaCha20Poly1305', mce_ciphertext, mce_ad) + 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)) - 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()) - ) - ) + def test_rng(): + user_rng = rng("user") - rsapriv = private_key('rsa', 1536, r) + 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)))) - rsapub = rsapriv.get_public_key() + 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')) - 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()) - ) - ) + md5.update(hex_decode('f468025b')) + h2 = md5.final() + print("md5 hash: %s (%s)\n" % (hex_encode(h2), '47efd2be302a937775e93dea281b6751')) - dec = pk_op_decrypt(rsapriv, "EME1(SHA-256)".encode('utf-8')) - enc = pk_op_encrypt(rsapub, "EME1(SHA-256)".encode('utf-8')) + def test_cipher(): + for mode in ['AES-128/CTR-BE', 'Serpent/GCM', 'ChaCha20Poly1305']: + enc = cipher(mode, encrypt=True) - 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')) + (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) - signer = pk_op_sign(rsapriv, 'EMSA4(SHA-384)'.encode('utf-8')) + print(" plaintext %s (%d)" % (hex_encode(pt), len(pt))) - signer.update('messa'.encode('utf-8')) - signer.update('ge'.encode('utf-8')) - sig = signer.finish(r) + 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))) - r.reseed(200) - print("EMSA4(SHA-384) signature: %s" % hexlify(sig).decode('utf-8')) + dec = 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))) - verify = pk_op_verify(rsapub, 'EMSA4(SHA-384)'.encode('utf-8')) - verify.update('mess'.encode('utf-8')) - verify.update('age'.encode('utf-8')) - print("good sig accepted? %s" % verify.check_signature(sig)) + def test_mceliece(): + mce_priv = private_key('mce', [2960,57], rng()) + mce_pub = mce_priv.get_public_key() - verify.update('mess of things'.encode('utf-8')) - verify.update('age'.encode('utf-8')) - print("bad sig accepted? %s" % verify.check_signature(sig)) + mce_plaintext = 'mce plaintext' + mce_ad = 'mce AD' + mce_ciphertext = mceies_encrypt(mce_pub, rng(), 'ChaCha20Poly1305', mce_plaintext, mce_ad) - verify.update('message'.encode('utf-8')) - print("good sig accepted? %s\n" % verify.check_signature(sig)) + print("mceies len(pt)=%d len(ct)=%d", (len(mce_plaintext), len(mce_ciphertext))) - 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() + mce_decrypt = mceies_decrypt(mce_priv, 'ChaCha20Poly1305', mce_ciphertext, mce_ad) - b_dh_priv = private_key('ecdh', dh_grp, r) - b_dh_pub = b_dh_priv.get_public_key() + 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 = private_key('rsa', 1536, rng()) + rsapub = rsapriv.get_public_key() + + 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)") + enc = pk_op_encrypt(rsapub, "EME1(SHA-256)") + + 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)') + + signer.update('messa') + signer.update('ge') + sig = signer.finish(rng()) + + print("EMSA4(SHA-384) signature: %s" % hex_encode(sig)) + + verify = pk_op_verify(rsapub, 'EMSA4(SHA-384)') + + verify.update('mess') + verify.update('age') + 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)) + + def test_dh(): + a_rng = rng('user') + b_rng = rng('user') + + 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() - a_dh = pk_op_key_agreement(a_dh_priv, dh_kdf) - b_dh = pk_op_key_agreement(b_dh_priv, dh_kdf) + b_dh_priv = private_key('ecdh', dh_grp, rng()) + b_dh_pub = b_dh_priv.get_public_key() - 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'))) + 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) - 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')) + 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))) - print("ecdh %s shared:\n %s\n %s\n" % - (dh_grps, hexlify(a_key).decode('utf-8'), hexlify(b_key).decode('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) - 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("ecdh %s shared:\n %s\n %s\n" % + (dh_grp, hex_encode(a_key), hex_encode(b_key))) - print(cert.time_starts()) - print(cert.time_expires()) + def test_certs(): + cert = x509_cert("src/tests/data/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") - 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("Not before: %s" % cert.time_starts()) + print("Not after: %s" % cert.time_expires()) - print(cert.to_string()) + 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" % hex_encode(cert.subject_public_key_bits())) - return + print(cert.to_string()) + + 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): diff --git a/src/scripts/ci/travis/build.sh b/src/scripts/ci/travis/build.sh index 369450091..bb52f0648 100755 --- a/src/scripts/ci/travis/build.sh +++ b/src/scripts/ci/travis/build.sh @@ -60,4 +60,12 @@ if [ "$MODULES" != "min" ] && [ "${TARGETOS:0:3}" != "ios" ]; then ./botan-test fi +if [ "$MODULES" != "min" ] && [ "$BUILD_MODE" = "shared" ] && [ "$TARGETOS" = "desktop" ] +then + python2 --version + python3 --version + LD_LIBRARY_PATH=. python2 src/python/botan.py + LD_LIBRARY_PATH=. python3 src/python/botan.py +fi + make install diff --git a/src/scripts/ci/travis/install_osx_packages.sh b/src/scripts/ci/travis/install_osx_packages.sh index 1d4a5e001..588855cc0 100755 --- a/src/scripts/ci/travis/install_osx_packages.sh +++ b/src/scripts/ci/travis/install_osx_packages.sh @@ -6,3 +6,5 @@ which shellcheck > /dev/null && shellcheck "$0" # Run shellcheck on this if avai brew update || brew update brew install xz +brew install python # python2 +brew install python3 |