aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/manual/contents.rst3
-rw-r--r--doc/manual/python.rst222
-rw-r--r--src/lib/asn1/oid_lookup/default.cpp2
-rw-r--r--src/lib/ffi/ffi.cpp196
-rw-r--r--src/lib/ffi/ffi.h13
-rwxr-xr-xsrc/python/botan.py101
6 files changed, 452 insertions, 85 deletions
diff --git a/doc/manual/contents.rst b/doc/manual/contents.rst
index 83c36e71f..df1a665ed 100644
--- a/doc/manual/contents.rst
+++ b/doc/manual/contents.rst
@@ -6,6 +6,7 @@ Contents
index
building
+ python
firststep
secmem
rng
@@ -26,4 +27,4 @@ Contents
fpe
versions
ffi
- python
+
diff --git a/doc/manual/python.rst b/doc/manual/python.rst
index f851cbaca..a1239253b 100644
--- a/doc/manual/python.rst
+++ b/doc/manual/python.rst
@@ -6,5 +6,227 @@ Python Binding
.. highlight:: python
+.. py:module:: botan
+
The Python binding is based on the `ffi` module of botan and the
`ctypes` module of the Python standard library.
+
+Versioning
+----------------------------------------
+.. py:function:: version_major()
+
+ Returns the major number of the library version (currently, 1)
+.. py:function:: version_minor()
+
+ Returns the minor number of the library version (currently, 11)
+.. py:function:: version_patch()
+
+ Returns the patch number of the library version (currently, 14)
+
+.. py:function:: version_string()
+
+ Returns a free form version string for the library
+
+Random Number Generators
+----------------------------------------
+.. py:class:: rng(rng_type = 'system')
+
+ Type 'user' also allowed (userspace HKDF RNG seeded from system
+ rng). The system RNG is very cheap to create, as just a single file
+ handle or CSP handle is kept open, from first use until shutdown,
+ no matter how many 'system' rng instances are created. Thus it is
+ easy to use the RNG in a one-off way, with `botan.rng().get(32)`.
+
+ .. py:method:: get(length)
+
+ Return some bits
+
+ .. py:method:: reseed(bits = 256)
+
+ Meaningless on system RNG, on userspace RNG causes a reseed/rekey
+
+
+Hash Functions
+----------------------------------------
+.. py:class:: hash_function(algo)
+
+ Algo is a string (eg 'SHA-1', 'SHA-384', 'Skein-512')
+
+ .. py:method:: clear()
+
+ Clear state
+
+ .. py:method:: output_length()
+
+ .. py:method:: update(x)
+
+ Add some input
+
+ .. py:method:: final()
+
+ Returns the hash of all input provided, resets
+ for another message.
+
+Message Authentication Codes
+----------------------------------------
+.. py:class:: message_authentication_code(algo)
+
+ Algo is a string (eg 'HMAC(SHA-256)', 'Poly1305', 'CMAC(AES-256)')
+
+ .. py:method:: clear()
+
+ .. py:method:: output_length()
+
+ .. py:method:: set_key(key)
+
+ Set the key
+
+ .. py:method:: update(x)
+
+ Add some input
+
+ .. py:method:: final()
+
+ Returns the MAC of all input provided, resets
+ for another message with the same key.
+
+Ciphers
+----------------------------------------
+.. py:class:: cipher(object, algo, encrypt = True)
+
+ The algorithm is spcified as a string (eg 'AES-128/GCM',
+ 'Serpent/OCB(12)', 'Threefish-512/EAX').
+
+ Set the second param to False for decryption
+
+ .. py:method:: tag_length()
+
+ Returns the tag length (0 for unauthenticated modes)
+
+ .. py:method:: default_nonce_length()
+
+ Returns default nonce length
+
+ .. py:method:: update_granularity()
+
+ Returns update block size
+
+ .. py:method:: is_authenticated()
+
+ Returns True if this is an AEAD mode
+
+ .. py:method:: valid_nonce_length(nonce_len)
+
+ Returns True if nonce_len is a valid nonce len for
+ this mode
+
+ .. py:method:: clear()
+
+ Resets all state
+
+ .. py:method:: set_key(key)
+
+ Set the key
+
+ .. py:method:: start(nonce)
+
+ Start processing a message using nonce
+
+ .. py:method:: update(txt)
+
+ Consumes input text and returns output
+
+ .. py:method:: finish(txt = None)
+
+ Finish processing (with an optional final input). May throw if
+ message authentication checks fail, in which case all plaintext
+ previously processed must be discarded. Alternately, always
+ call finish with the entire message, avoiding calls to update
+ entirely.
+
+Bcrypt
+----------------------------------------
+.. py:function:: bcrypt(passwd, rng, work_factor = 10)
+
+ Provided the password and an RNG object, returns a bcrypt string
+
+.. py:function:: check_bcrypt(passwd, bcrypt)
+
+ Check a bcrypt hash against the provided password, returning True
+ iff the password matches.
+
+PBKDF
+----------------------------------------
+.. py:function:: pbkdf(algo, password, out_len, iterations = 100000, salt = rng().get(12))
+
+ Runs a PBKDF2 algo specified as a string (eg 'PBKDF2(SHA-256)', 'PBKDF2(CMAC(Blowfish))').
+ Runs with n iterations with meaning depending on the algorithm.
+ The salt can be provided or otherwise is randomly chosen. In any case it is returned
+ from the call.
+
+ Returns out_len bytes of output (or potentially less depending on
+ the algorithm and the size of the request).
+
+ Returns tuple of salt, iterations, and psk
+
+.. py:function:: pbkdf_timed(algo, password, out_len, ms_to_run = 300, salt = rng().get(12))
+
+ Runs for as many iterations as needed to consumed ms_to_run
+ milliseconds on whatever we're running on. Returns tuple of salt,
+ iterations, and psk
+
+KDF
+----------------------------------------
+.. py:function:: kdf(algo, secret, out_len, salt)
+
+Public Key
+----------------------------------------
+.. py:class:: public_key(object)
+
+ .. py:method:: fingerprint(hash = 'SHA-256')
+
+.. py:class:: private_key(algo, param, rng)
+
+ Constructor creates a new private key. The paramater type/value
+ depends on the algorithm. For "rsa" is is the size of the key in
+ bits. For "ecdsa" and "ecdh" it is a group name (for instance
+ "secp256r1"). For "ecdh" there is also a special case for group
+ "curve25519" (which is actually a completely distinct key type
+ with a non-standard encoding).
+
+ .. py:method:: get_public_key()
+
+ Return a public_key object
+
+ .. py:method:: export()
+
+Public Key Operations
+----------------------------------------
+.. py:class:: pk_op_encrypt(pubkey, padding, rng)
+
+ .. py:method:: encrypt(msg, rng)
+
+.. py:class:: pk_op_decrypt(privkey, padding)
+
+ .. py:method:: decrypt(msg)
+
+.. py:class:: pk_op_sign(privkey, hash_w_padding)
+
+ .. py:method:: update(msg)
+ .. py:method:: finish(rng)
+
+.. py:class:: pk_op_verify(pubkey, hash_w_padding)
+
+ .. py:method:: update(msg)
+ .. py:method:: check_signature(signature)
+
+.. py:class:: pk_op_key_agreement(privkey, kdf)
+
+ .. py:method:: public_value()
+
+ Returns the public value to be passed to the other party
+
+ .. py:method:: agree(other, key_len, salt)
+
+ Returns a key derived by the KDF.
+
diff --git a/src/lib/asn1/oid_lookup/default.cpp b/src/lib/asn1/oid_lookup/default.cpp
index de04f542a..0e23b18a2 100644
--- a/src/lib/asn1/oid_lookup/default.cpp
+++ b/src/lib/asn1/oid_lookup/default.cpp
@@ -28,7 +28,7 @@ const char* default_oid_list()
// X9.62 ecPublicKey, valid for ECDSA and ECDH (RFC 3279 sec 2.3.5)
"1.2.840.10045.2.1 = ECDSA" "\n"
- //"1.3.132.1.12 = ECDH" "\n"
+ "1.3.132.1.12 = ECDH" "\n"
"1.2.643.2.2.19 = GOST-34.10" "\n"
diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp
index 937423c69..94fc546fe 100644
--- a/src/lib/ffi/ffi.cpp
+++ b/src/lib/ffi/ffi.cpp
@@ -31,6 +31,10 @@
#include <botan/ecdh.h>
#endif
+#if defined(BOTAN_HAS_CURVE_25519)
+ #include <botan/curve25519.h>
+#endif
+
#if defined(BOTAN_HAS_BCRYPT)
#include <botan/bcrypt.h>
#endif
@@ -49,7 +53,9 @@ struct botan_struct
T* get() const
{
- BOTAN_ASSERT_EQUAL(m_magic, MAGIC, "Bad magic value - memory corruption?");
+ if(m_magic != MAGIC)
+ throw std::runtime_error("Bad magic " + std::to_string(m_magic) +
+ " in ffi object expected" + std::to_string(MAGIC));
return m_obj.get();
}
private:
@@ -59,7 +65,7 @@ struct botan_struct
void log_exception(const char* func_name, const char* what)
{
- printf("botan ffi %s: %s\n", func_name, what);
+ fprintf(stderr, "%s: %s\n", func_name, what);
}
template<typename T, uint32_t M>
@@ -96,20 +102,32 @@ int apply_fn(botan_struct<T, M>* o, const char* func_name, F func)
return -1;
}
-template<typename Alloc>
-int write_output(uint8_t out[], size_t* out_len, const std::vector<uint8_t, Alloc>& buf)
+int write_output(uint8_t out[], size_t* out_len, const uint8_t buf[], size_t buf_len)
{
Botan::clear_mem(out, *out_len);
const size_t avail = *out_len;
- *out_len = buf.size();
- if(avail >= buf.size())
+ *out_len = buf_len;
+ if(avail >= buf_len)
{
- Botan::copy_mem(out, &buf[0], buf.size());
+ Botan::copy_mem(out, &buf[0], buf_len);
return 0;
}
return -1;
}
+template<typename Alloc>
+int write_vec_output(uint8_t out[], size_t* out_len, const std::vector<uint8_t, Alloc>& buf)
+ {
+ return write_output(out, out_len, &buf[0], buf.size());
+ }
+
+int write_str_output(uint8_t out[], size_t* out_len, const std::string& str)
+ {
+ return write_output(out, out_len,
+ reinterpret_cast<const uint8_t*>(str.c_str()),
+ str.size() + 1);
+ }
+
#define BOTAN_FFI_DO(T, obj, block) apply_fn(obj, BOTAN_CURRENT_FUNCTION, [=](T& obj) { do { block } while(0); return 0; })
}
@@ -261,8 +279,10 @@ int botan_hash_init(botan_hash_t* hash, const char* hash_name, uint32_t flags)
{
try
{
- if(!hash || !hash_name || flags != 0)
- return -1;
+ if(hash == nullptr || hash_name == nullptr || *hash_name == 0)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+ if(flags != 0)
+ return BOTAN_FFI_ERROR_BAD_FLAG;
if(auto h = Botan::get_hash_function(hash_name))
{
@@ -279,7 +299,7 @@ int botan_hash_init(botan_hash_t* hash, const char* hash_name, uint32_t flags)
log_exception(BOTAN_CURRENT_FUNCTION, "unknown");
}
- return -2;
+ return BOTAN_FFI_ERROR_EXCEPTION_THROWN;
}
int botan_hash_destroy(botan_hash_t hash)
@@ -634,25 +654,26 @@ int botan_kdf(const char* kdf_algo,
}
#if defined(BOTAN_HAS_BCRYPT)
-int botan_bcrypt_generate(char* out, size_t out_len, const char* pass,
- botan_rng_t rng_obj, size_t wf)
+int botan_bcrypt_generate(uint8_t* out, size_t* out_len,
+ const char* pass,
+ botan_rng_t rng_obj, size_t wf,
+ uint32_t flags)
{
try
{
BOTAN_ASSERT_ARG_NON_NULL(out);
+ BOTAN_ASSERT_ARG_NON_NULL(out_len);
BOTAN_ASSERT_ARG_NON_NULL(pass);
+ if(flags != 0)
+ return BOTAN_FFI_ERROR_BAD_FLAG;
+
if(wf < 2 || wf > 30)
throw std::runtime_error("Bad bcrypt work factor " + std::to_string(wf));
Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
-
- std::memset(out, 0, out_len);
- const std::string c = Botan::generate_bcrypt(pass, rng, wf);
- if(out_len <= c.size())
- return ENOMEM;
- std::memcpy(out, c.c_str(), c.size());
- return 0;
+ const std::string bcrypt = Botan::generate_bcrypt(pass, rng, wf);
+ return write_str_output(out, out_len, bcrypt);
}
catch(std::exception& e)
{
@@ -663,7 +684,7 @@ int botan_bcrypt_generate(char* out, size_t out_len, const char* pass,
log_exception(BOTAN_CURRENT_FUNCTION, "unknown");
}
- return -1;
+ return BOTAN_FFI_ERROR_EXCEPTION_THROWN;
}
int botan_bcrypt_is_valid(const char* pass, const char* hash)
@@ -683,21 +704,26 @@ int botan_bcrypt_is_valid(const char* pass, const char* hash)
log_exception(BOTAN_CURRENT_FUNCTION, "unknown");
}
- return -1;
+ return BOTAN_FFI_ERROR_EXCEPTION_THROWN;
}
#endif
-int botan_privkey_create_rsa(botan_privkey_t* key, botan_rng_t rng_obj, size_t n_bits)
+int botan_privkey_create_rsa(botan_privkey_t* key_obj, botan_rng_t rng_obj, size_t n_bits)
{
try
{
- *key = nullptr;
+ if(key_obj == nullptr || rng_obj == nullptr)
+ return -1;
+ if(n_bits < 1024 || n_bits > 16*1024)
+ return -2;
+
+ *key_obj = nullptr;
#if defined(BOTAN_HAS_RSA)
Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
- std::unique_ptr<Botan::RSA_PrivateKey> rsa(new Botan::RSA_PrivateKey(rng, n_bits));
- *key = new botan_privkey_struct(rsa.release());
+ std::unique_ptr<Botan::Private_Key> key(new Botan::RSA_PrivateKey(rng, n_bits));
+ *key_obj = new botan_privkey_struct(key.release());
return 0;
#endif
}
@@ -706,19 +732,24 @@ int botan_privkey_create_rsa(botan_privkey_t* key, botan_rng_t rng_obj, size_t n
log_exception(BOTAN_CURRENT_FUNCTION, e.what());
}
- return -1;
+ return BOTAN_FFI_ERROR_EXCEPTION_THROWN;
}
-int botan_privkey_create_ecdsa(botan_privkey_t* key, botan_rng_t rng_obj, const char* params)
+int botan_privkey_create_ecdsa(botan_privkey_t* key_obj, botan_rng_t rng_obj, const char* param_str)
{
try
{
+ if(key_obj == nullptr || rng_obj == nullptr || param_str == nullptr || *param_str == 0)
+ return -1;
+
+ *key_obj = nullptr;
+
#if defined(BOTAN_HAS_ECDSA)
Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
- Botan::EC_Group grp(params);
- std::unique_ptr<Botan::ECDSA_PrivateKey> ecdsa(new Botan::ECDSA_PrivateKey(rng, grp));
- *key = new botan_privkey_struct(ecdsa.release());
+ Botan::EC_Group grp(param_str);
+ std::unique_ptr<Botan::Private_Key> key(new Botan::ECDSA_PrivateKey(rng, grp));
+ *key_obj = new botan_privkey_struct(key.release());
return 0;
#endif
}
@@ -727,18 +758,33 @@ int botan_privkey_create_ecdsa(botan_privkey_t* key, botan_rng_t rng_obj, const
log_exception(BOTAN_CURRENT_FUNCTION, e.what());
}
- return -1;
+ return BOTAN_FFI_ERROR_EXCEPTION_THROWN;
}
-int botan_privkey_create_ecdh(botan_privkey_t* key, botan_rng_t rng_obj, const char* params)
+int botan_privkey_create_ecdh(botan_privkey_t* key_obj, botan_rng_t rng_obj, const char* param_str)
{
try
{
+ if(key_obj == nullptr || rng_obj == nullptr || param_str == nullptr || *param_str == 0)
+ return -1;
+
+ *key_obj = nullptr;
+
+ const std::string params(param_str);
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ if(params == "curve25519")
+ {
+ std::unique_ptr<Botan::Private_Key> key(new Botan::Curve25519_PrivateKey(safe_get(rng_obj)));
+ *key_obj = new botan_privkey_struct(key.release());
+ return 0;
+ }
+#endif
+
#if defined(BOTAN_HAS_ECDH)
- Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
Botan::EC_Group grp(params);
- std::unique_ptr<Botan::ECDH_PrivateKey> ecdh(new Botan::ECDH_PrivateKey(rng, grp));
- *key = new botan_privkey_struct(ecdh.release());
+ std::unique_ptr<Botan::Private_Key> key(new Botan::ECDH_PrivateKey(safe_get(rng_obj), grp));
+ *key_obj = new botan_privkey_struct(key.release());
return 0;
#endif
}
@@ -747,7 +793,7 @@ int botan_privkey_create_ecdh(botan_privkey_t* key, botan_rng_t rng_obj, const c
log_exception(BOTAN_CURRENT_FUNCTION, e.what());
}
- return -1;
+ return BOTAN_FFI_ERROR_EXCEPTION_THROWN;
}
int botan_privkey_load(botan_privkey_t* key, botan_rng_t rng_obj,
@@ -779,7 +825,7 @@ int botan_privkey_load(botan_privkey_t* key, botan_rng_t rng_obj,
return -1;
*key = nullptr;
- return -1;
+ return BOTAN_FFI_ERROR_EXCEPTION_THROWN;
}
int botan_privkey_destroy(botan_privkey_t key)
@@ -809,7 +855,7 @@ int botan_privkey_export_pubkey(botan_pubkey_t* pubout, botan_privkey_t key_obj)
log_exception(BOTAN_CURRENT_FUNCTION, e.what());
}
- return -1;
+ return BOTAN_FFI_ERROR_EXCEPTION_THROWN;
}
int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len)
@@ -833,29 +879,44 @@ int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len)
int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out_len, uint32_t flags)
{
return BOTAN_FFI_DO(Botan::Public_Key, key, {
- return write_output(out, out_len, Botan::X509::BER_encode(key));
+ if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
+ return write_vec_output(out, out_len, Botan::X509::BER_encode(key));
+ else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
+ return write_str_output(out, out_len, Botan::X509::PEM_encode(key));
+ else
+ return -2;
});
}
int botan_privkey_export(botan_privkey_t key, uint8_t out[], size_t* out_len, uint32_t flags)
{
return BOTAN_FFI_DO(Botan::Private_Key, key, {
- return write_output(out, out_len, Botan::PKCS8::BER_encode(key));
+ if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
+ return write_vec_output(out, out_len, Botan::PKCS8::BER_encode(key));
+ else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
+ return write_str_output(out, out_len, Botan::PKCS8::PEM_encode(key));
+ else
+ return -2;
});
}
int botan_privkey_export_encrypted(botan_privkey_t key,
uint8_t out[], size_t* out_len,
botan_rng_t rng_obj,
- const char* passphrase,
- const char* encryption_algo,
+ const char* pass,
+ const char* pbe,
uint32_t flags)
{
return BOTAN_FFI_DO(Botan::Private_Key, key, {
- auto ber = Botan::PKCS8::BER_encode(key, safe_get(rng_obj), passphrase,
- std::chrono::milliseconds(300),
- encryption_algo);
- return write_output(out, out_len, ber);
+ auto pbkdf_time = std::chrono::milliseconds(300);
+ Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+
+ if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
+ return write_vec_output(out, out_len, Botan::PKCS8::BER_encode(key, rng, pass, pbkdf_time, pbe));
+ else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
+ return write_str_output(out, out_len, Botan::PKCS8::PEM_encode(key, rng, pass, pbkdf_time, pbe));
+ else
+ return -2;
});
}
@@ -876,8 +937,6 @@ int botan_pubkey_fingerprint(botan_pubkey_t key, const char* hash_fn,
Botan::copy_mem(out, &z[0], *out_len);
return 0;
});
- *out_len = 0;
- return -1;
}
int botan_pk_op_encrypt_create(botan_pk_op_encrypt_t* op,
@@ -890,7 +949,7 @@ int botan_pk_op_encrypt_create(botan_pk_op_encrypt_t* op,
BOTAN_ASSERT_NONNULL(op);
if(flags != 0)
- return -2;
+ return BOTAN_FFI_ERROR_BAD_FLAG;
std::unique_ptr<Botan::PK_Encryptor> pk(new Botan::PK_Encryptor_EME(safe_get(key_obj), padding));
*op = new botan_pk_op_encrypt_struct(pk.release());
@@ -916,7 +975,7 @@ int botan_pk_op_encrypt(botan_pk_op_encrypt_t op,
const uint8_t plaintext[], size_t plaintext_len)
{
return BOTAN_FFI_DO(Botan::PK_Encryptor, op, {
- return write_output(out, out_len, op.encrypt(plaintext, plaintext_len, safe_get(rng_obj)));
+ return write_vec_output(out, out_len, op.encrypt(plaintext, plaintext_len, safe_get(rng_obj)));
});
}
@@ -933,7 +992,7 @@ int botan_pk_op_decrypt_create(botan_pk_op_decrypt_t* op,
BOTAN_ASSERT_NONNULL(op);
if(flags != 0)
- return -2;
+ return BOTAN_FFI_ERROR_BAD_FLAG;
std::unique_ptr<Botan::PK_Decryptor> pk(new Botan::PK_Decryptor_EME(safe_get(key_obj), padding));
*op = new botan_pk_op_decrypt_struct(pk.release());
@@ -958,7 +1017,7 @@ int botan_pk_op_decrypt(botan_pk_op_decrypt_t op,
uint8_t ciphertext[], size_t ciphertext_len)
{
return BOTAN_FFI_DO(Botan::PK_Decryptor, op, {
- return write_output(out, out_len, op.decrypt(ciphertext, ciphertext_len));
+ return write_vec_output(out, out_len, op.decrypt(ciphertext, ciphertext_len));
});
}
@@ -975,7 +1034,7 @@ int botan_pk_op_sign_create(botan_pk_op_sign_t* op,
BOTAN_ASSERT_NONNULL(op);
if(flags != 0)
- return -2;
+ return BOTAN_FFI_ERROR_BAD_FLAG;
std::unique_ptr<Botan::PK_Signer> pk(new Botan::PK_Signer(safe_get(key_obj), hash));
*op = new botan_pk_op_sign_struct(pk.release());
@@ -986,7 +1045,7 @@ int botan_pk_op_sign_create(botan_pk_op_sign_t* op,
log_exception(BOTAN_CURRENT_FUNCTION, e.what());
}
- return -1;
+ return BOTAN_FFI_ERROR_EXCEPTION_THROWN;
}
int botan_pk_op_sign_destroy(botan_pk_op_sign_t op)
@@ -1003,7 +1062,7 @@ int botan_pk_op_sign_update(botan_pk_op_sign_t op, const uint8_t in[], size_t in
int botan_pk_op_sign_finish(botan_pk_op_sign_t op, botan_rng_t rng_obj, uint8_t out[], size_t* out_len)
{
return BOTAN_FFI_DO(Botan::PK_Signer, op, {
- return write_output(out, out_len, op.signature(safe_get(rng_obj)));
+ return write_vec_output(out, out_len, op.signature(safe_get(rng_obj)));
});
}
@@ -1017,7 +1076,7 @@ int botan_pk_op_verify_create(botan_pk_op_verify_t* op,
BOTAN_ASSERT_NONNULL(op);
if(flags != 0)
- return -2;
+ return BOTAN_FFI_ERROR_BAD_FLAG;
std::unique_ptr<Botan::PK_Verifier> pk(new Botan::PK_Verifier(safe_get(key_obj), hash));
*op = new botan_pk_op_verify_struct(pk.release());
@@ -1064,7 +1123,7 @@ int botan_pk_op_key_agreement_create(botan_pk_op_ka_t* op,
BOTAN_ASSERT_NONNULL(op);
if(flags != 0)
- return -2;
+ return BOTAN_FFI_ERROR_BAD_FLAG;
std::unique_ptr<Botan::PK_Key_Agreement> pk(new Botan::PK_Key_Agreement(safe_get(key_obj), kdf));
*op = new botan_pk_op_ka_struct(pk.release());
@@ -1084,6 +1143,27 @@ int botan_pk_op_key_agreement_destroy(botan_pk_op_ka_t op)
return 0;
}
+int botan_pk_op_key_agreement_export_public(botan_privkey_t key,
+ uint8_t out[], size_t* out_len)
+ {
+ return BOTAN_FFI_DO(Botan::Private_Key, key, {
+ auto kak = dynamic_cast<const Botan::PK_Key_Agreement_Key*>(&key);
+ if(!kak)
+ return -2;
+ const std::vector<uint8_t> pv = kak->public_value();
+ const size_t avail = *out_len;
+ *out_len = pv.size();
+
+ if(pv.size() <= avail)
+ {
+ Botan::copy_mem(out, &pv[0], pv.size());
+ return 0;
+ }
+
+ return -1;
+ });
+ }
+
int botan_pk_op_key_agreement(botan_pk_op_ka_t op,
uint8_t out[], size_t* out_len,
const uint8_t other_key[], size_t other_key_len,
@@ -1091,7 +1171,7 @@ int botan_pk_op_key_agreement(botan_pk_op_ka_t op,
{
return BOTAN_FFI_DO(Botan::PK_Key_Agreement, op, {
auto k = op.derive_key(*out_len, other_key, other_key_len, salt, salt_len).bits_of();
- return write_output(out, out_len, k);
+ return write_vec_output(out, out_len, k);
});
}
diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h
index cda2c1480..34e706e2a 100644
--- a/src/lib/ffi/ffi.h
+++ b/src/lib/ffi/ffi.h
@@ -38,6 +38,11 @@ BOTAN_DLL uint32_t botan_version_datestamp();
/*
* Error handling
*/
+#define BOTAN_FFI_ERROR_EXCEPTION_THROWN (-20)
+#define BOTAN_FFI_ERROR_BAD_FLAG (-30)
+#define BOTAN_FFI_ERROR_NULL_POINTER (-31)
+#define BOTAN_FFI_ERROR_NULL_POINTER (-31)
+
//const char* botan_error_description(int err);
/*
@@ -146,10 +151,11 @@ BOTAN_DLL int botan_kdf(const char* kdf_algo,
*/
#if defined(BOTAN_HAS_BCRYPT)
-BOTAN_DLL int botan_bcrypt_generate(char* out, size_t out_len,
+BOTAN_DLL int botan_bcrypt_generate(uint8_t* out, size_t* out_len,
const char* pass,
botan_rng_t rng,
- size_t work_factor);
+ size_t work_factor,
+ uint32_t flags);
/**
* Returns 0 if if this password/hash combination is valid
@@ -290,6 +296,9 @@ BOTAN_DLL int botan_pk_op_key_agreement_create(botan_pk_op_ka_t* op,
uint32_t flags);
BOTAN_DLL int botan_pk_op_key_agreement_destroy(botan_pk_op_ka_t op);
+BOTAN_DLL int botan_pk_op_key_agreement_export_public(botan_privkey_t key,
+ uint8_t out[], size_t* out_len);
+
BOTAN_DLL int botan_pk_op_key_agreement(botan_pk_op_ka_t op,
uint8_t out[], size_t* out_len,
const uint8_t other_key[], size_t other_key_len,
diff --git a/src/python/botan.py b/src/python/botan.py
index 8414308ec..2957dce29 100755
--- a/src/python/botan.py
+++ b/src/python/botan.py
@@ -44,7 +44,7 @@ RNG
"""
class rng(object):
# Can also use type "system"
- def __init__(self, rng_type = 'user'):
+ def __init__(self, rng_type = 'system'):
botan.botan_rng_init.argtypes = [c_void_p, c_char_p]
self.rng = c_void_p(0)
rc = botan.botan_rng_init(byref(self.rng), rng_type)
@@ -155,12 +155,6 @@ class cipher(object):
botan.botan_cipher_destroy.argtypes = [c_void_p]
botan.botan_cipher_destroy(self.cipher)
- def tag_length(self):
- botan.botan_cipher_tag_length.argtypes = [c_void_p,POINTER(c_size_t)]
- l = c_size_t(0)
- botan.botan_cipher_tag_size(self.cipher, byref(l))
- return l.value
-
def default_nonce_length(self):
botan.botan_cipher_default_nonce_length.argtypes = [c_void_p, POINTER(c_size_t)]
l = c_size_t(0)
@@ -234,14 +228,19 @@ class cipher(object):
Bcrypt
TODO: might not be enabled - handle that gracefully!
"""
-def generate_bcrypt(passwd, rng, work_factor = 10):
- botan.botan_bcrypt_generate.argtypes = [POINTER(c_char), c_size_t, c_char_p, c_void_p, c_size_t]
- out = create_string_buffer(61)
- rc = botan.botan_bcrypt_generate(out, sizeof(out), passwd, rng.rng, c_size_t(work_factor))
-
+def bcrypt(passwd, rng, work_factor = 10):
+ botan.botan_bcrypt_generate.argtypes = [POINTER(c_char), POINTER(c_size_t),
+ c_char_p, c_void_p, c_size_t, c_uint32]
+ out_len = c_size_t(64)
+ out = create_string_buffer(out_len.value)
+ flags = c_uint32(0)
+ rc = botan.botan_bcrypt_generate(out, byref(out_len), passwd, rng.rng, c_size_t(work_factor), flags)
if rc != 0:
raise Exception('botan bcrypt failed, error %s' % (rc))
- return str(out.raw)
+ b = out.raw[0:out_len.value]
+ if b[-1] == '\x00':
+ b = b[:-1]
+ return b
def check_bcrypt(passwd, bcrypt):
rc = botan.botan_bcrypt_is_valid(passwd, bcrypt)
@@ -250,17 +249,16 @@ def check_bcrypt(passwd, bcrypt):
"""
PBKDF
"""
-def pbkdf(algo, password, out_len, iterations, salt):
+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)
- return out_buf.raw
+ return (salt,iterations,out_buf.raw)
-def pbkdf_timed(algo, password, out_len, rng, ms_to_run, salt_len = 12):
+def pbkdf_timed(algo, password, out_len, ms_to_run = 300, salt = rng().get(12))
botan.botan_pbkdf_timed.argtypes = [c_char_p, POINTER(c_char), c_size_t, c_char_p,
c_void_p, c_size_t, c_size_t, POINTER(c_size_t)]
out_buf = create_string_buffer(out_len)
- salt = rng.get(salt_len)
iterations = c_size_t(0)
botan.botan_pbkdf_timed(algo, out_buf, out_len, password, salt, len(salt), ms_to_run, byref(iterations))
return (salt,iterations.value,out_buf.raw)
@@ -434,10 +432,48 @@ class pk_op_verify(object):
return True
return False
+class pk_op_key_agreement(object):
+ def __init__(self, key, kdf):
+ botan.botan_pk_op_key_agreement_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32]
+ botan.botan_pk_op_key_agreement_export_public.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t)]
+ self.op = c_void_p(0)
+ flags = 0 # always zero in this ABI
+ botan.botan_pk_op_key_agreement_create(byref(self.op), key.privkey, kdf, flags)
+ if not self.op:
+ raise Exception("No key agreement for you")
+
+ pub = create_string_buffer(4096)
+ pub_len = c_size_t(len(pub))
+ botan.botan_pk_op_key_agreement_export_public(key.privkey, pub, byref(pub_len))
+ self.m_public_value = pub.raw[0:pub_len.value]
+
+ def __del__(self):
+ botan.botan_pk_op_key_agreement_destroy.argtypes = [c_void_p]
+ botan.botan_pk_op_key_agreement_destroy(self.op)
+
+ def public_value(self):
+ return self.m_public_value
+
+ def agree(self, other, key_len, salt):
+ botan.botan_pk_op_key_agreement.argtypes = [c_void_p, POINTER(c_char), POINTER(c_size_t),
+ POINTER(c_char), c_size_t, POINTER(c_char), c_size_t]
+
+ outbuf_sz = c_size_t(key_len)
+ outbuf = create_string_buffer(outbuf_sz.value)
+ rc = botan.botan_pk_op_key_agreement(self.op, outbuf, byref(outbuf_sz), other, len(other), salt, len(salt))
+
+ if rc == -1 and outbuf_sz.value > len(outbuf):
+ outbuf = create_string_buffer(outbuf_sz.value)
+ botan.botan_pk_op_key_agreement(self.op, outbuf, byref(outbuf_sz), other, len(other), salt, len(salt))
+ return outbuf.raw[0:outbuf_sz.value]
+
"""
Tests and examples
"""
def test():
+ r = rng("user")
+
+
print version_string()
print version_major(), version_minor(), version_patch()
@@ -447,7 +483,6 @@ def test():
print pbkdf('PBKDF2(SHA-1)', '', 32, 10000, '0001020304050607'.decode('hex')).encode('hex').upper()
print '59B2B1143B4CB1059EC58D9722FB1C72471E0D85C6F7543BA5228526375B0127'
- r = rng("user")
(salt,iterations,psk) = pbkdf_timed('PBKDF2(SHA-256)', 'xyz', 32, r, 200, 12)
print salt.encode('hex'), iterations
print 'x', psk.encode('hex')
@@ -483,8 +518,8 @@ def test():
signer = pk_op_sign(rsapriv, 'EMSA4(SHA-384)')
- signer.update('mess')
- signer.update('age')
+ signer.update('messa')
+ signer.update('ge')
sig = signer.finish(r)
r.reseed(200)
@@ -494,14 +529,34 @@ def test():
verify.update('mess')
verify.update('age')
- print "correct sig accepted?", verify.check_signature(sig)
+ print "good sig accepted?", verify.check_signature(sig)
verify.update('mess of things')
verify.update('age')
print "bad sig accepted?", verify.check_signature(sig)
- key = private_key('ecdsa', 'secp256r1', r)
- blob = key.export()
+ verify.update('message')
+ print "good sig accepted?", verify.check_signature(sig)
+
+ dh_grp = 'secp256r1'
+ #dh_grp = 'curve25519'
+ dh_kdf = 'KDF2(SHA-384)'
+ a_dh_priv = private_key('ecdh', dh_grp, r)
+ a_dh_pub = a_dh_priv.get_public_key()
+
+ b_dh_priv = private_key('ecdh', dh_grp, r)
+ b_dh_pub = b_dh_priv.get_public_key()
+
+ a_dh = pk_op_key_agreement(a_dh_priv, dh_kdf)
+ b_dh = pk_op_key_agreement(b_dh_priv, dh_kdf)
+
+ print "dh pubs", a_dh.public_value().encode('hex'), b_dh.public_value().encode('hex')
+
+ a_key = a_dh.agree(b_dh.public_value(), 20, 'salt')
+ b_key = b_dh.agree(a_dh.public_value(), 20, 'salt')
+
+ print "dh shared", a_key.encode('hex'), b_key.encode('hex')
+
#f = open('key.ber','wb')
#f.write(blob)