aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/block/idea/idea.cpp28
-rw-r--r--src/lib/block/idea_sse2/idea_sse2.cpp12
-rw-r--r--src/lib/ffi/ffi.cpp10
-rw-r--r--src/lib/ffi/ffi.h4
-rw-r--r--src/lib/ffi/info.txt2
-rw-r--r--src/lib/math/mp/mp_monty.cpp23
-rw-r--r--src/lib/pk_pad/eme_oaep/oaep.cpp16
-rw-r--r--src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp16
-rw-r--r--src/lib/pubkey/curve25519/donna.cpp10
-rw-r--r--src/lib/pubkey/pk_keys.cpp6
-rw-r--r--src/lib/utils/ct_utils.h119
-rwxr-xr-xsrc/python/botan.py539
-rwxr-xr-xsrc/scripts/ci/travis/build.sh8
-rwxr-xr-xsrc/scripts/ci/travis/install_osx_packages.sh2
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