aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build-data/buildh.in2
-rw-r--r--src/cli/utils.cpp85
-rw-r--r--src/lib/asn1/ber_dec.cpp27
-rw-r--r--src/lib/ffi/ffi.cpp282
-rw-r--r--src/lib/ffi/ffi.h41
-rw-r--r--src/lib/modes/cipher_mode.cpp38
-rw-r--r--src/lib/modes/cipher_mode.h13
-rw-r--r--src/lib/prov/openssl/openssl.h7
-rw-r--r--src/lib/prov/openssl/openssl_mode.cpp200
-rw-r--r--src/lib/tls/tls_callbacks.h4
-rw-r--r--src/lib/tls/tls_policy.h14
-rw-r--r--src/lib/utils/assert.h28
-rw-r--r--src/lib/utils/compiler.h9
-rw-r--r--src/lib/utils/parsing.cpp88
-rwxr-xr-xsrc/scripts/ci/travis/lint.sh10
-rw-r--r--src/tests/data/hostnames.vec66
-rw-r--r--src/tests/data/modes/cbc.vec7
-rw-r--r--src/tests/main.cpp12
-rw-r--r--src/tests/test_asn1.cpp67
-rw-r--r--src/tests/test_block.cpp7
-rw-r--r--src/tests/test_ffi.cpp349
-rw-r--r--src/tests/test_hash.cpp7
-rw-r--r--src/tests/test_mac.cpp7
-rw-r--r--src/tests/test_modes.cpp150
-rw-r--r--src/tests/test_pubkey.cpp28
-rw-r--r--src/tests/test_pubkey.h3
-rw-r--r--src/tests/test_stream.cpp3
-rw-r--r--src/tests/test_utils.cpp28
-rw-r--r--src/tests/tests.cpp39
-rw-r--r--src/tests/tests.h15
30 files changed, 1329 insertions, 307 deletions
diff --git a/src/build-data/buildh.in b/src/build-data/buildh.in
index 393bcc4fb..2376b056e 100644
--- a/src/build-data/buildh.in
+++ b/src/build-data/buildh.in
@@ -51,7 +51,7 @@
* Local/misc configuration options (if any) follow
*/
%{local_config}
-%{misc_config}
+%{house_ecc_curve_defines}
/*
* Things you can edit (but probably shouldn't)
diff --git a/src/cli/utils.cpp b/src/cli/utils.cpp
index 76501add4..d561f4cdf 100644
--- a/src/cli/utils.cpp
+++ b/src/cli/utils.cpp
@@ -1,5 +1,6 @@
/*
* (C) 2009,2010,2014,2015 Jack Lloyd
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -36,6 +37,10 @@
#include <botan/bcrypt.h>
#endif
+#if defined(BOTAN_HAS_HMAC)
+ #include <botan/hmac.h>
+#endif
+
namespace Botan_CLI {
class Config_Info final : public Command
@@ -226,6 +231,43 @@ BOTAN_REGISTER_COMMAND("http_get", HTTP_Get);
#endif // http_util
+#if defined(BOTAN_HAS_HEX_CODEC)
+
+class Hex_Encode final : public Command
+ {
+ public:
+ Hex_Encode() : Command("hex_enc file") {}
+
+ void go() override
+ {
+ auto hex_enc_f = [&](const uint8_t b[], size_t l) { output() << Botan::hex_encode(b, l); };
+ this->read_file(get_arg("file"), hex_enc_f, 2);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("hex_enc", Hex_Encode);
+
+class Hex_Decode final : public Command
+ {
+ public:
+ Hex_Decode() : Command("hex_dec file") {}
+
+ void go() override
+ {
+ auto hex_dec_f = [&](const uint8_t b[], size_t l)
+ {
+ std::vector<uint8_t> bin = Botan::hex_decode(reinterpret_cast<const char*>(b), l);
+ output().write(reinterpret_cast<const char*>(bin.data()), bin.size());
+ };
+
+ this->read_file(get_arg("file"), hex_dec_f, 2);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("hex_dec", Hex_Decode);
+
+#endif
+
#if defined(BOTAN_HAS_BASE64_CODEC)
class Base64_Encode final : public Command
@@ -309,4 +351,47 @@ BOTAN_REGISTER_COMMAND("check_bcrypt", Check_Bcrypt);
#endif // bcrypt
+#if defined(BOTAN_HAS_HMAC)
+
+class HMAC final : public Command
+ {
+ public:
+ HMAC() : Command("hmac --hash=SHA-256 --buf-size=4096 key *files") {}
+
+ void go() override
+ {
+ const std::string hash_algo = get_arg("hash");
+ std::unique_ptr<Botan::MessageAuthenticationCode> hmac(Botan::MessageAuthenticationCode::create("HMAC(" + hash_algo + ")"));
+
+ if(!hmac)
+ throw CLI_Error_Unsupported("HMAC", hash_algo);
+
+ hmac->set_key(slurp_file(get_arg("key")));
+
+ const size_t buf_size = get_arg_sz("buf-size");
+
+ std::vector<std::string> files = get_arg_list("files");
+ if(files.empty())
+ files.push_back("-"); // read stdin if no arguments on command line
+
+ for(const std::string& fsname : files)
+ {
+ try
+ {
+ auto update_hmac = [&](const uint8_t b[], size_t l) { hmac->update(b, l); };
+ read_file(fsname, update_hmac, buf_size);
+ output() << Botan::hex_encode(hmac->final()) << " " << fsname << "\n";
+ }
+ catch(CLI_IO_Error& e)
+ {
+ error_output() << e.what() << "\n";
+ }
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("hmac", HMAC);
+
+#endif // hmac
+
}
diff --git a/src/lib/asn1/ber_dec.cpp b/src/lib/asn1/ber_dec.cpp
index 7b2147600..bf728a8e8 100644
--- a/src/lib/asn1/ber_dec.cpp
+++ b/src/lib/asn1/ber_dec.cpp
@@ -218,20 +218,25 @@ BER_Object BER_Decoder::get_next_object()
return next;
}
- decode_tag(m_source, next.type_tag, next.class_tag);
- if(next.type_tag == NO_OBJECT)
- return next;
+ for(;;)
+ {
+ decode_tag(m_source, next.type_tag, next.class_tag);
+ if(next.type_tag == NO_OBJECT)
+ return next;
- const size_t length = decode_length(m_source);
- if(!m_source->check_available(length))
- throw BER_Decoding_Error("Value truncated");
+ const size_t length = decode_length(m_source);
+ if(!m_source->check_available(length))
+ throw BER_Decoding_Error("Value truncated");
- next.value.resize(length);
- if(m_source->read(next.value.data(), length) != length)
- throw BER_Decoding_Error("Value truncated");
+ next.value.resize(length);
+ if(m_source->read(next.value.data(), length) != length)
+ throw BER_Decoding_Error("Value truncated");
- if(next.type_tag == EOC && next.class_tag == UNIVERSAL)
- return get_next_object();
+ if(next.type_tag == EOC && next.class_tag == UNIVERSAL)
+ continue;
+ else
+ break;
+ }
return next;
}
diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp
index 7cf69efb3..42b01be62 100644
--- a/src/lib/ffi/ffi.cpp
+++ b/src/lib/ffi/ffi.cpp
@@ -33,6 +33,10 @@
#include <botan/rsa.h>
#endif
+#if defined(BOTAN_HAS_ELGAMAL)
+ #include <botan/elgamal.h>
+#endif
+
#if defined(BOTAN_HAS_DSA)
#include <botan/dsa.h>
#endif
@@ -203,6 +207,121 @@ inline int write_str_output(char out[], size_t* out_len, const std::vector<uint8
#define BOTAN_FFI_DO(T, obj, param, block) apply_fn(obj, BOTAN_CURRENT_FUNCTION, [=](T& param) -> int { do { block } while(0); return 0; })
+Botan::BigInt pubkey_get_field(const Botan::Public_Key& key,
+ const std::string& field)
+ {
+ // Maybe this should be `return key.get_integer_field(field_name)`?
+
+#if defined(BOTAN_HAS_RSA)
+ if(const Botan::RSA_PublicKey* rsa = dynamic_cast<const Botan::RSA_PublicKey*>(&key))
+ {
+ if(field == "n")
+ return rsa->get_n();
+ else if(field == "e")
+ return rsa->get_e();
+ else
+ throw Botan::Exception("Field not supported");
+ }
+#endif
+
+#if defined(BOTAN_HAS_DL_PUBLIC_KEY_FAMILY)
+ // Handles DSA, ElGamal, etc
+ if(const Botan::DL_Scheme_PublicKey* dl = dynamic_cast<const Botan::DL_Scheme_PublicKey*>(&key))
+ {
+ if(field == "p")
+ return dl->group_p();
+ else if(field == "q")
+ return dl->group_q();
+ else if(field == "g")
+ return dl->group_g();
+ else if(field == "y")
+ return dl->get_y();
+ else
+ throw Botan::Exception("Field not supported");
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+ if(const Botan::EC_PublicKey* ecc = dynamic_cast<const Botan::EC_PublicKey*>(&key))
+ {
+ if(field == "public_x")
+ return ecc->public_point().get_affine_x();
+ else if(field == "public_y")
+ return ecc->public_point().get_affine_y();
+ else if(field == "base_x")
+ return ecc->domain().get_base_point().get_affine_x();
+ else if(field == "base_y")
+ return ecc->domain().get_base_point().get_affine_y();
+ else if(field == "p")
+ return ecc->domain().get_curve().get_p();
+ else if(field == "a")
+ return ecc->domain().get_curve().get_a();
+ else if(field == "b")
+ return ecc->domain().get_curve().get_b();
+ else if(field == "cofactor")
+ return ecc->domain().get_cofactor();
+ else if(field == "order")
+ return ecc->domain().get_order();
+ else
+ throw Botan::Exception("Field not supported");
+ }
+#endif
+
+ // Some other algorithm type not supported by this function
+ throw Botan::Exception("Unsupported algorithm type for botan_pubkey_get_field");
+ }
+
+Botan::BigInt privkey_get_field(const Botan::Private_Key& key,
+ const std::string& field)
+ {
+ //return key.get_integer_field(field);
+
+#if defined(BOTAN_HAS_RSA)
+
+ if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<const Botan::RSA_PrivateKey*>(&key))
+ {
+ if(field == "p")
+ return rsa->get_p();
+ else if(field == "q")
+ return rsa->get_q();
+ else if(field == "d")
+ return rsa->get_d();
+ else if(field == "c")
+ return rsa->get_c();
+ else if(field == "d1")
+ return rsa->get_d1();
+ else if(field == "d2")
+ return rsa->get_d2();
+ else
+ return pubkey_get_field(key, field);
+ }
+#endif
+
+#if defined(BOTAN_HAS_DL_PUBLIC_KEY_FAMILY)
+ // Handles DSA, ElGamal, etc
+ if(const Botan::DL_Scheme_PrivateKey* dl = dynamic_cast<const Botan::DL_Scheme_PrivateKey*>(&key))
+ {
+ if(field == "x")
+ return dl->get_x();
+ else
+ return pubkey_get_field(key, field);
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+ if(const Botan::EC_PrivateKey* ecc = dynamic_cast<const Botan::EC_PrivateKey*>(&key))
+ {
+ if(field == "x")
+ return ecc->private_value();
+ else
+ return pubkey_get_field(key, field);
+ }
+#endif
+
+ // Some other algorithm type not supported by this function
+ throw Botan::Exception("Unsupported algorithm type for botan_privkey_get_field");
+ }
+
}
extern "C" {
@@ -434,7 +553,7 @@ int botan_mp_to_str(const botan_mp_t mp, uint8_t digit_base, char* out, size_t*
std::vector<uint8_t> hex = Botan::BigInt::encode(bn, base);
hex.push_back(0); // null terminator
- write_str_output(out, out_len, hex);
+ return write_str_output(out, out_len, hex);
});
}
@@ -935,6 +1054,7 @@ int botan_cipher_update(botan_cipher_t cipher_obj,
cipher.update(mbuf);
input_size -= ud;
+ copy_mem(output, mbuf.data(), ud);
input += ud;
taken += ud;
@@ -946,6 +1066,7 @@ int botan_cipher_update(botan_cipher_t cipher_obj,
*output_written = written;
*input_consumed = taken;
+ return 0;
}
catch(std::exception& e)
{
@@ -1326,9 +1447,8 @@ int botan_pubkey_load(botan_pubkey_t* key,
int botan_privkey_load_rsa(botan_privkey_t* key,
botan_mp_t p, botan_mp_t q, botan_mp_t d)
{
- *key = nullptr;
-
#if defined(BOTAN_HAS_RSA)
+ *key = nullptr;
try
{
*key = new botan_privkey_struct(new Botan::RSA_PrivateKey(safe_get(p),
@@ -1342,6 +1462,7 @@ int botan_privkey_load_rsa(botan_privkey_t* key,
}
return -1;
#else
+ BOTAN_UNUSED(key, p, q, d);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
@@ -1349,9 +1470,8 @@ int botan_privkey_load_rsa(botan_privkey_t* key,
int botan_pubkey_load_rsa(botan_pubkey_t* key,
botan_mp_t n, botan_mp_t e)
{
- *key = nullptr;
-
#if defined(BOTAN_HAS_RSA)
+ *key = nullptr;
try
{
*key = new botan_pubkey_struct(new Botan::RSA_PublicKey(safe_get(n), safe_get(e)));
@@ -1364,6 +1484,7 @@ int botan_pubkey_load_rsa(botan_pubkey_t* key,
return -1;
#else
+ BOTAN_UNUSED(key, n, e);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
@@ -1371,9 +1492,8 @@ int botan_pubkey_load_rsa(botan_pubkey_t* key,
int botan_privkey_load_dsa(botan_privkey_t* key,
botan_mp_t p, botan_mp_t q, botan_mp_t g, botan_mp_t x)
{
- *key = nullptr;
-
#if defined(BOTAN_HAS_DSA)
+ *key = nullptr;
try
{
Botan::Null_RNG null_rng;
@@ -1387,10 +1507,7 @@ int botan_privkey_load_dsa(botan_privkey_t* key,
}
return -1;
#else
- BOTAN_UNUSED(p);
- BOTAN_UNUSED(q);
- BOTAN_UNUSED(g);
- BOTAN_UNUSED(x);
+ BOTAN_UNUSED(key, p, q, g, x);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
@@ -1398,9 +1515,8 @@ int botan_privkey_load_dsa(botan_privkey_t* key,
int botan_pubkey_load_dsa(botan_pubkey_t* key,
botan_mp_t p, botan_mp_t q, botan_mp_t g, botan_mp_t y)
{
- *key = nullptr;
-
#if defined(BOTAN_HAS_DSA)
+ *key = nullptr;
try
{
Botan::DL_Group group(safe_get(p), safe_get(q), safe_get(g));
@@ -1414,133 +1530,57 @@ int botan_pubkey_load_dsa(botan_pubkey_t* key,
return -1;
#else
- BOTAN_UNUSED(p);
- BOTAN_UNUSED(q);
- BOTAN_UNUSED(g);
- BOTAN_UNUSED(y);
+ BOTAN_UNUSED(key, p, q, g, y);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
-namespace {
-
-Botan::BigInt botan_pubkey_do_get_field(const Botan::Public_Key& key,
- const std::string& field)
+int botan_pubkey_load_elgamal(botan_pubkey_t* key,
+ botan_mp_t p, botan_mp_t g, botan_mp_t y)
{
- // Maybe this should be `return key.get_integer_field(field_name)`?
-
-#if defined(BOTAN_HAS_RSA)
- if(const Botan::RSA_PublicKey* rsa = dynamic_cast<const Botan::RSA_PublicKey*>(&key))
+#if defined(BOTAN_HAS_ELGAMAL)
+ *key = nullptr;
+ try
{
- if(field == "n")
- return rsa->get_n();
- else if(field == "e")
- return rsa->get_e();
- else
- throw Botan::Exception("Field not supported");
+ Botan::DL_Group group(safe_get(p), safe_get(g));
+ *key = new botan_pubkey_struct(new Botan::ElGamal_PublicKey(group, safe_get(y)));
+ return 0;
}
-#endif
-
-#if defined(BOTAN_HAS_DL_PUBLIC_KEY_FAMILY)
- // Handles DSA, ElGamal, etc
- if(const Botan::DL_Scheme_PublicKey* dl = dynamic_cast<const Botan::DL_Scheme_PublicKey*>(&key))
+ catch(std::exception& exn)
{
- if(field == "p")
- return dl->group_p();
- else if(field == "q")
- return dl->group_q();
- else if(field == "g")
- return dl->group_g();
- else if(field == "y")
- return dl->get_y();
- else
- throw Botan::Exception("Field not supported");
+ log_exception(BOTAN_CURRENT_FUNCTION, exn.what());
}
-#endif
-#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
- if(const Botan::EC_PublicKey* ecc = dynamic_cast<const Botan::EC_PublicKey*>(&key))
- {
- if(field == "public_x")
- return ecc->public_point().get_affine_x();
- else if(field == "public_y")
- return ecc->public_point().get_affine_y();
- else if(field == "base_x")
- return ecc->domain().get_base_point().get_affine_x();
- else if(field == "base_y")
- return ecc->domain().get_base_point().get_affine_y();
- else if(field == "p")
- return ecc->domain().get_curve().get_p();
- else if(field == "a")
- return ecc->domain().get_curve().get_a();
- else if(field == "b")
- return ecc->domain().get_curve().get_b();
- else if(field == "cofactor")
- return ecc->domain().get_cofactor();
- else if(field == "order")
- return ecc->domain().get_order();
- else
- throw Botan::Exception("Field not supported");
- }
+ return -1;
+#else
+ BOTAN_UNUSED(key, p, g, y);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
-
- // Some other algorithm type not supported by this function
- throw Botan::Exception("Unsupported algorithm type for botan_pubkey_get_field");
}
-Botan::BigInt botan_privkey_do_get_field(const Botan::Private_Key& key,
- const std::string& field)
+int botan_privkey_load_elgamal(botan_privkey_t* key,
+ botan_mp_t p, botan_mp_t g, botan_mp_t x)
{
- //return key.get_integer_field(field);
-
-#if defined(BOTAN_HAS_RSA)
-
- if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<const Botan::RSA_PrivateKey*>(&key))
- {
- if(field == "p")
- return rsa->get_p();
- else if(field == "q")
- return rsa->get_q();
- else if(field == "d")
- return rsa->get_d();
- else if(field == "c")
- return rsa->get_c();
- else if(field == "d1")
- return rsa->get_d1();
- else if(field == "d2")
- return rsa->get_d2();
- else
- return botan_pubkey_do_get_field(key, field);
- }
-#endif
-
-#if defined(BOTAN_HAS_DL_PUBLIC_KEY_FAMILY)
- // Handles DSA, ElGamal, etc
- if(const Botan::DL_Scheme_PrivateKey* dl = dynamic_cast<const Botan::DL_Scheme_PrivateKey*>(&key))
+#if defined(BOTAN_HAS_ELGAMAL)
+ *key = nullptr;
+ try
{
- if(field == "x")
- return dl->get_x();
- else
- return botan_pubkey_do_get_field(key, field);
+ Botan::Null_RNG null_rng;
+ Botan::DL_Group group(safe_get(p), safe_get(g));
+ *key = new botan_privkey_struct(new Botan::ElGamal_PrivateKey(null_rng, group, safe_get(x)));
+ return 0;
}
-#endif
-
-#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
- if(const Botan::EC_PrivateKey* ecc = dynamic_cast<const Botan::EC_PrivateKey*>(&key))
+ catch(std::exception& e)
{
- if(field == "x")
- return ecc->private_value();
- else
- return botan_pubkey_do_get_field(key, field);
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
}
+ return -1;
+#else
+ BOTAN_UNUSED(key, p, g, x);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
-
- // Some other algorithm type not supported by this function
- throw Botan::Exception("Unsupported algorithm type for botan_privkey_get_field");
}
-}
-
int botan_pubkey_get_field(botan_mp_t output,
botan_pubkey_t key,
const char* field_name_cstr)
@@ -1551,7 +1591,7 @@ int botan_pubkey_get_field(botan_mp_t output,
const std::string field_name(field_name_cstr);
return BOTAN_FFI_DO(Botan::Public_Key, key, k, {
- safe_get(output) = botan_pubkey_do_get_field(k, field_name);
+ safe_get(output) = pubkey_get_field(k, field_name);
});
}
@@ -1565,7 +1605,7 @@ int botan_privkey_get_field(botan_mp_t output,
const std::string field_name(field_name_cstr);
return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
- safe_get(output) = botan_privkey_do_get_field(k, field_name);
+ safe_get(output) = privkey_get_field(k, field_name);
});
}
diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h
index 59e767385..8fb7ca832 100644
--- a/src/lib/ffi/ffi.h
+++ b/src/lib/ffi/ffi.h
@@ -175,7 +175,7 @@ typedef struct botan_rng_struct* botan_rng_t;
* "system": System_RNG, "user": AutoSeeded_RNG
* Set rng_type to null or empty string to let the library choose
*
-* TODO: replace rng_type with simple flags?
+* TODO: replace rng_type with simple flags?
*/
BOTAN_DLL int botan_rng_init(botan_rng_t* rng, const char* rng_type);
@@ -785,6 +785,45 @@ BOTAN_DLL int botan_pubkey_dsa_get_g(botan_mp_t d, botan_pubkey_t key);
BOTAN_DLL int botan_pubkey_dsa_get_y(botan_mp_t y, botan_pubkey_t key);
/*
+* Algorithm specific key operations: ElGamal
+*/
+
+/*
+* Loads ElGamal public key
+* @param key variable populated with key material
+* @param p prime order of a Z_p group
+* @param g group generator
+* @param y public key
+*
+* @pre key is NULL on input
+* @post function allocates memory and assigns to `key'
+*
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_DLL int botan_pubkey_load_elgamal(botan_pubkey_t* key,
+ botan_mp_t p,
+ botan_mp_t g,
+ botan_mp_t y);
+
+/*
+* Loads ElGamal private key
+*
+* @param key variable populated with key material
+* @param p prime order of a Z_p group
+* @param g group generator
+* @param x private key
+*
+* @pre key is NULL on input
+* @post function allocates memory and assigns to `key'
+*
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_DLL int botan_privkey_load_elgamal(botan_privkey_t* key,
+ botan_mp_t p,
+ botan_mp_t g,
+ botan_mp_t x);
+
+/*
* Public Key Encryption
*/
typedef struct botan_pk_op_encrypt_struct* botan_pk_op_encrypt_t;
diff --git a/src/lib/modes/cipher_mode.cpp b/src/lib/modes/cipher_mode.cpp
index 843e49581..74d565f33 100644
--- a/src/lib/modes/cipher_mode.cpp
+++ b/src/lib/modes/cipher_mode.cpp
@@ -34,10 +34,26 @@
#include <botan/xts.h>
#endif
+#if defined(BOTAN_HAS_OPENSSL)
+ #include <botan/internal/openssl.h>
+#endif
+
namespace Botan {
-Cipher_Mode* get_cipher_mode(const std::string& algo, Cipher_Dir direction)
+Cipher_Mode* get_cipher_mode(const std::string& algo, Cipher_Dir direction,
+ const std::string& provider)
{
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider.empty() || provider == "openssl")
+ {
+ if(Cipher_Mode* bc = make_openssl_cipher_mode(algo, direction))
+ return bc;
+
+ if(!provider.empty())
+ return nullptr;
+ }
+#endif
+
if(auto sc = StreamCipher::create(algo))
{
return new Stream_Cipher_Mode(sc.release());
@@ -69,7 +85,7 @@ Cipher_Mode* get_cipher_mode(const std::string& algo, Cipher_Dir direction)
alg_args << ')';
const std::string mode_name = mode_info[0] + alg_args.str();
- return get_cipher_mode(mode_name, direction);
+ return get_cipher_mode(mode_name, direction, provider);
}
#if defined(BOTAN_HAS_BLOCK_CIPHER)
@@ -81,7 +97,7 @@ Cipher_Mode* get_cipher_mode(const std::string& algo, Cipher_Dir direction)
return nullptr;
}
- std::unique_ptr<BlockCipher> bc(BlockCipher::create(spec.arg(0)));
+ std::unique_ptr<BlockCipher> bc(BlockCipher::create(spec.arg(0), provider));
if(!bc)
{
@@ -141,4 +157,20 @@ Cipher_Mode* get_cipher_mode(const std::string& algo, Cipher_Dir direction)
return nullptr;
}
+//static
+std::vector<std::string> Cipher_Mode::providers(const std::string& algo_spec)
+ {
+ const std::vector<std::string>& possible = { "base", "openssl" };
+ std::vector<std::string> providers;
+ for(auto&& prov : possible)
+ {
+ std::unique_ptr<Cipher_Mode> mode(get_cipher_mode(algo_spec, ENCRYPTION, prov));
+ if(mode)
+ {
+ providers.push_back(prov); // available
+ }
+ }
+ return providers;
+ }
+
}
diff --git a/src/lib/modes/cipher_mode.h b/src/lib/modes/cipher_mode.h
index 411a1b3e5..bf1821256 100644
--- a/src/lib/modes/cipher_mode.h
+++ b/src/lib/modes/cipher_mode.h
@@ -25,6 +25,12 @@ class BOTAN_DLL Cipher_Mode
public:
virtual ~Cipher_Mode() = default;
+ /**
+ * @return list of available providers for this algorithm, empty if not available
+ * @param algo_spec algorithm name
+ */
+ static std::vector<std::string> providers(const std::string& algo_spec);
+
/*
* Prepare for processing a message under the specified nonce
*/
@@ -209,14 +215,17 @@ class BOTAN_DLL Cipher_Mode
* The two possible directions for cipher filters, determining whether they
* actually perform encryption or decryption.
*/
-enum Cipher_Dir { ENCRYPTION, DECRYPTION };
+enum Cipher_Dir : int { ENCRYPTION, DECRYPTION };
/**
* Get a cipher mode by name (eg "AES-128/CBC" or "Serpent/XTS")
* @param algo_spec cipher name
* @param direction ENCRYPTION or DECRYPTION
+* @param provider provider implementation to choose
*/
-BOTAN_DLL Cipher_Mode* get_cipher_mode(const std::string& algo_spec, Cipher_Dir direction);
+BOTAN_DLL Cipher_Mode* get_cipher_mode(const std::string& algo_spec,
+ Cipher_Dir direction,
+ const std::string& provider = "");
}
diff --git a/src/lib/prov/openssl/openssl.h b/src/lib/prov/openssl/openssl.h
index e28fb2931..3cd39113b 100644
--- a/src/lib/prov/openssl/openssl.h
+++ b/src/lib/prov/openssl/openssl.h
@@ -24,8 +24,10 @@
namespace Botan {
class BlockCipher;
+class Cipher_Mode;
class StreamCipher;
class HashFunction;
+enum Cipher_Dir : int;
class OpenSSL_Error : public Exception
{
@@ -39,6 +41,11 @@ class OpenSSL_Error : public Exception
std::unique_ptr<BlockCipher>
make_openssl_block_cipher(const std::string& name);
+/* Cipher Modes */
+
+Cipher_Mode*
+make_openssl_cipher_mode(const std::string& name, Cipher_Dir direction);
+
/* Hash */
std::unique_ptr<HashFunction>
diff --git a/src/lib/prov/openssl/openssl_mode.cpp b/src/lib/prov/openssl/openssl_mode.cpp
new file mode 100644
index 000000000..77682bf5c
--- /dev/null
+++ b/src/lib/prov/openssl/openssl_mode.cpp
@@ -0,0 +1,200 @@
+/*
+* Cipher Modes via OpenSSL
+* (C) 1999-2010,2015 Jack Lloyd
+* (C) 2017 Alexander Bluhm (genua GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cipher_mode.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/openssl.h>
+#include <openssl/evp.h>
+
+namespace Botan {
+
+namespace {
+
+class BOTAN_DLL OpenSSL_Cipher_Mode : public Cipher_Mode
+ {
+ public:
+ OpenSSL_Cipher_Mode(const std::string& name,
+ const EVP_CIPHER* cipher,
+ Cipher_Dir direction);
+ ~OpenSSL_Cipher_Mode();
+
+ std::string provider() const override { return "openssl"; }
+ std::string name() const override { return m_mode_name; }
+
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+ size_t process(uint8_t msg[], size_t msg_len) override;
+ void finish(secure_vector<uint8_t>& final_block, size_t offset0) override;
+ size_t output_length(size_t input_length) const override;
+ size_t update_granularity() const override;
+ size_t minimum_final_size() const override;
+ size_t default_nonce_length() const override;
+ bool valid_nonce_length(size_t nonce_len) const override;
+ void clear() override;
+ void reset() override;
+ Key_Length_Specification key_spec() const override;
+
+ private:
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ const std::string m_mode_name;
+ const Cipher_Dir m_direction;
+ size_t m_block_size;
+ EVP_CIPHER_CTX m_cipher;
+ };
+
+OpenSSL_Cipher_Mode::OpenSSL_Cipher_Mode(const std::string& name,
+ const EVP_CIPHER* algo,
+ Cipher_Dir direction) :
+ m_mode_name(name),
+ m_direction(direction)
+ {
+ m_block_size = EVP_CIPHER_block_size(algo);
+
+ if(EVP_CIPHER_mode(algo) != EVP_CIPH_CBC_MODE)
+ throw Invalid_Argument("OpenSSL_BlockCipher: Non-CBC EVP was passed in");
+
+ EVP_CIPHER_CTX_init(&m_cipher);
+ if(!EVP_CipherInit_ex(&m_cipher, algo, nullptr, nullptr, nullptr,
+ m_direction == ENCRYPTION ? 1 : 0))
+ throw Internal_Error("EVP_CipherInit_ex failed");
+ if(!EVP_CIPHER_CTX_set_padding(&m_cipher, 0))
+ throw Internal_Error("EVP_CIPHER_CTX_set_padding failed");
+ }
+
+OpenSSL_Cipher_Mode::~OpenSSL_Cipher_Mode()
+ {
+ EVP_CIPHER_CTX_cleanup(&m_cipher);
+ }
+
+void OpenSSL_Cipher_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+ if(nonce_len)
+ {
+ if(!EVP_CipherInit_ex(&m_cipher, nullptr, nullptr, nullptr, nonce, -1))
+ throw Internal_Error("EVP_CipherInit_ex nonce failed");
+ }
+ }
+
+size_t OpenSSL_Cipher_Mode::process(uint8_t msg[], size_t msg_len)
+ {
+ if(msg_len == 0)
+ return 0;
+ if(msg_len > INT_MAX)
+ throw Internal_Error("msg_len overflow");
+ int outl = msg_len;
+ secure_vector<uint8_t> out(outl);
+
+ if(!EVP_CipherUpdate(&m_cipher, out.data(), &outl, msg, msg_len))
+ throw Internal_Error("EVP_CipherUpdate failed");
+ memcpy(msg, out.data(), outl);
+ return outl;
+ }
+
+void OpenSSL_Cipher_Mode::finish(secure_vector<uint8_t>& buffer,
+ size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset ok");
+ uint8_t* buf = buffer.data() + offset;
+ const size_t buf_size = buffer.size() - offset;
+
+ size_t written = process(buf, buf_size);
+ int outl = buf_size - written;
+ secure_vector<uint8_t> out(outl);
+
+ if(!EVP_CipherFinal_ex(&m_cipher, out.data(), &outl))
+ throw Internal_Error("EVP_CipherFinal_ex failed");
+ memcpy(buf + written, out.data(), outl);
+ written += outl;
+ buffer.resize(offset + written);
+ }
+
+size_t OpenSSL_Cipher_Mode::update_granularity() const
+ {
+ return m_block_size * BOTAN_BLOCK_CIPHER_PAR_MULT;
+ }
+
+size_t OpenSSL_Cipher_Mode::minimum_final_size() const
+ {
+ return 0; // no padding
+ }
+
+size_t OpenSSL_Cipher_Mode::default_nonce_length() const
+ {
+ return m_block_size;
+ }
+
+bool OpenSSL_Cipher_Mode::valid_nonce_length(size_t nonce_len) const
+ {
+ return (nonce_len == 0 || nonce_len == m_block_size);
+ }
+
+size_t OpenSSL_Cipher_Mode::output_length(size_t input_length) const
+ {
+ if(input_length == 0)
+ return m_block_size;
+ else
+ return round_up(input_length, m_block_size);
+ }
+
+void OpenSSL_Cipher_Mode::clear()
+ {
+ const EVP_CIPHER* algo = EVP_CIPHER_CTX_cipher(&m_cipher);
+
+ if(!EVP_CIPHER_CTX_cleanup(&m_cipher))
+ throw Internal_Error("EVP_CIPHER_CTX_cleanup failed");
+ EVP_CIPHER_CTX_init(&m_cipher);
+ if(!EVP_CipherInit_ex(&m_cipher, algo, nullptr, nullptr, nullptr,
+ m_direction == ENCRYPTION ? 1 : 0))
+ throw Internal_Error("EVP_CipherInit_ex clear failed");
+ if(!EVP_CIPHER_CTX_set_padding(&m_cipher, 0))
+ throw Internal_Error("EVP_CIPHER_CTX_set_padding clear failed");
+ }
+
+void OpenSSL_Cipher_Mode::reset()
+ {
+ if(!EVP_CipherInit_ex(&m_cipher, nullptr, nullptr, nullptr, nullptr, -1))
+ throw Internal_Error("EVP_CipherInit_ex clear failed");
+ }
+
+Key_Length_Specification OpenSSL_Cipher_Mode::key_spec() const
+ {
+ return Key_Length_Specification(EVP_CIPHER_CTX_key_length(&m_cipher));
+ }
+
+void OpenSSL_Cipher_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ if(!EVP_CIPHER_CTX_set_key_length(&m_cipher, length))
+ throw Invalid_Argument("EVP_CIPHER_CTX_set_key_length failed");
+ if(!EVP_CipherInit_ex(&m_cipher, nullptr, nullptr, key, nullptr, -1))
+ throw Internal_Error("EVP_CipherInit_ex key failed");
+ }
+
+}
+
+Cipher_Mode*
+make_openssl_cipher_mode(const std::string& name, Cipher_Dir direction)
+ {
+#define MAKE_OPENSSL_MODE(evp_fn) \
+ new OpenSSL_Cipher_Mode(name, (evp_fn)(), direction)
+
+#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC) && !defined(OPENSSL_NO_AES)
+ if(name == "AES-128/CBC/NoPadding")
+ return MAKE_OPENSSL_MODE(EVP_aes_128_cbc);
+ if(name == "AES-192/CBC/NoPadding")
+ return MAKE_OPENSSL_MODE(EVP_aes_192_cbc);
+ if(name == "AES-256/CBC/NoPadding")
+ return MAKE_OPENSSL_MODE(EVP_aes_256_cbc);
+#endif
+
+#undef MAKE_OPENSSL_MODE
+ return nullptr;
+ }
+
+}
diff --git a/src/lib/tls/tls_callbacks.h b/src/lib/tls/tls_callbacks.h
index b20e60218..cccbb4f7e 100644
--- a/src/lib/tls/tls_callbacks.h
+++ b/src/lib/tls/tls_callbacks.h
@@ -188,9 +188,7 @@ class BOTAN_DLL Callbacks
*/
virtual void tls_log_debug_bin(const char* descr, const uint8_t val[], size_t val_len)
{
- BOTAN_UNUSED(descr);
- BOTAN_UNUSED(val);
- BOTAN_UNUSED(val_len);
+ BOTAN_UNUSED(descr, val, val_len);
}
};
diff --git a/src/lib/tls/tls_policy.h b/src/lib/tls/tls_policy.h
index ad95b7eb5..6ddfff749 100644
--- a/src/lib/tls/tls_policy.h
+++ b/src/lib/tls/tls_policy.h
@@ -502,6 +502,18 @@ class BOTAN_DLL Text_Policy : public Policy
size_t minimum_signature_strength() const override
{ return get_len("minimum_signature_strength", Policy::minimum_signature_strength()); }
+ size_t dtls_default_mtu() const override
+ { return get_len("dtls_default_mtu", Policy::dtls_default_mtu()); }
+
+ size_t dtls_initial_timeout() const override
+ { return get_len("dtls_initial_timeout", Policy::dtls_initial_timeout()); }
+
+ size_t dtls_maximum_timeout() const override
+ { return get_len("dtls_maximum_timeout", Policy::dtls_maximum_timeout()); }
+
+ bool require_cert_revocation_info() const override
+ { return get_bool("require_cert_revocation_info", Policy::require_cert_revocation_info()); }
+
bool hide_unknown_users() const override
{ return get_bool("hide_unknown_users", Policy::hide_unknown_users()); }
@@ -532,7 +544,7 @@ class BOTAN_DLL Text_Policy : public Policy
explicit Text_Policy(std::istream& in) : m_kv(read_cfg(in))
{}
- private:
+ protected:
std::vector<std::string> get_list(const std::string& key,
const std::vector<std::string>& def) const
diff --git a/src/lib/utils/assert.h b/src/lib/utils/assert.h
index c49ae62ee..ab459464e 100644
--- a/src/lib/utils/assert.h
+++ b/src/lib/utils/assert.h
@@ -1,6 +1,7 @@
/*
* Runtime assertion checking
* (C) 2010 Jack Lloyd
+* 2017 Simon Warta (Kullo GmbH)
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -87,9 +88,32 @@ BOTAN_NORETURN void BOTAN_DLL assertion_failure(const char* expr_str,
} while(0)
/**
-* Mark variable as unused
+* Mark variable as unused. Takes between 1 and 9 arguments and marks all as unused,
+* e.g. BOTAN_UNUSED(a); or BOTAN_UNUSED(x, y, z);
*/
-#define BOTAN_UNUSED(v) static_cast<void>(v)
+#define _BOTAN_UNUSED_IMPL1(a) static_cast<void>(a)
+#define _BOTAN_UNUSED_IMPL2(a, b) static_cast<void>(a); _BOTAN_UNUSED_IMPL1(b)
+#define _BOTAN_UNUSED_IMPL3(a, b, c) static_cast<void>(a); _BOTAN_UNUSED_IMPL2(b, c)
+#define _BOTAN_UNUSED_IMPL4(a, b, c, d) static_cast<void>(a); _BOTAN_UNUSED_IMPL3(b, c, d)
+#define _BOTAN_UNUSED_IMPL5(a, b, c, d, e) static_cast<void>(a); _BOTAN_UNUSED_IMPL4(b, c, d, e)
+#define _BOTAN_UNUSED_IMPL6(a, b, c, d, e, f) static_cast<void>(a); _BOTAN_UNUSED_IMPL5(b, c, d, e, f)
+#define _BOTAN_UNUSED_IMPL7(a, b, c, d, e, f, g) static_cast<void>(a); _BOTAN_UNUSED_IMPL6(b, c, d, e, f, g)
+#define _BOTAN_UNUSED_IMPL8(a, b, c, d, e, f, g, h) static_cast<void>(a); _BOTAN_UNUSED_IMPL7(b, c, d, e, f, g, h)
+#define _BOTAN_UNUSED_IMPL9(a, b, c, d, e, f, g, h, i) static_cast<void>(a); _BOTAN_UNUSED_IMPL8(b, c, d, e, f, g, h, i)
+#define _BOTAN_UNUSED_GET_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, IMPL_NAME, ...) IMPL_NAME
+
+#define BOTAN_UNUSED(...) _BOTAN_UNUSED_GET_IMPL(__VA_ARGS__, \
+ _BOTAN_UNUSED_IMPL9, \
+ _BOTAN_UNUSED_IMPL8, \
+ _BOTAN_UNUSED_IMPL7, \
+ _BOTAN_UNUSED_IMPL6, \
+ _BOTAN_UNUSED_IMPL5, \
+ _BOTAN_UNUSED_IMPL4, \
+ _BOTAN_UNUSED_IMPL3, \
+ _BOTAN_UNUSED_IMPL2, \
+ _BOTAN_UNUSED_IMPL1, \
+ unused dummy rest value \
+ ) /* we got an one of _BOTAN_UNUSED_IMPL*, now call it */ (__VA_ARGS__)
}
diff --git a/src/lib/utils/compiler.h b/src/lib/utils/compiler.h
index 898412501..7c2593ed8 100644
--- a/src/lib/utils/compiler.h
+++ b/src/lib/utils/compiler.h
@@ -117,6 +117,15 @@
#endif
/*
+* Define BOTAN_ALIGNAS (for MSVC 2013)
+*/
+#if defined(BOTAN_BUILD_COMPILER_IS_MSVC_2013)
+ #define BOTAN_ALIGNAS(n) /**/
+#else
+ #define BOTAN_ALIGNAS(n) alignas(n)
+#endif
+
+/*
* Define BOTAN_PARALLEL_FOR
*/
#if !defined(BOTAN_PARALLEL_FOR)
diff --git a/src/lib/utils/parsing.cpp b/src/lib/utils/parsing.cpp
index cd0c2409e..7583767f0 100644
--- a/src/lib/utils/parsing.cpp
+++ b/src/lib/utils/parsing.cpp
@@ -2,6 +2,7 @@
* Various string utils and parsing functions
* (C) 1999-2007,2013,2014,2015 Jack Lloyd
* (C) 2015 Simon Warta (Kullo GmbH)
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -12,6 +13,7 @@
#include <botan/loadstor.h>
#include <limits>
#include <set>
+#include <algorithm>
namespace Botan {
@@ -337,22 +339,100 @@ std::string replace_char(const std::string& str, char from_char, char to_char)
bool host_wildcard_match(const std::string& issued, const std::string& host)
{
if(issued == host)
+ {
return true;
+ }
- if(issued.size() > 2 && issued[0] == '*' && issued[1] == '.')
+ if(std::count(issued.begin(), issued.end(), '*') > 1)
+ {
+ return false;
+ }
+
+ // first try to match the base, then the left-most label
+ // which can contain exactly one wildcard at any position
+ if(issued.size() > 2)
{
size_t host_i = host.find('.');
if(host_i == std::string::npos || host_i == host.size() - 1)
+ {
return false;
+ }
+
+ size_t issued_i = issued.find('.');
+ if(issued_i == std::string::npos || issued_i == issued.size() - 1)
+ {
+ return false;
+ }
const std::string host_base = host.substr(host_i + 1);
- const std::string issued_base = issued.substr(2);
+ const std::string issued_base = issued.substr(issued_i + 1);
+
+ // if anything but the left-most label doesn't equal,
+ // we are already out here
+ if(host_base != issued_base)
+ {
+ return false;
+ }
+
+ // compare the left-most labels
+ std::string host_prefix = host.substr(0, host_i);
+
+ if(host_prefix.empty())
+ {
+ return false;
+ }
+
+ const std::string issued_prefix = issued.substr(0, issued_i);
+
+ // if split_on would work on strings with less than 2 items,
+ // the if/else block would not be necessary
+ if(issued_prefix == "*")
+ {
+ return true;
+ }
+
+ std::vector<std::string> p;
- if(host_base == issued_base)
+ if(issued_prefix[0] == '*')
+ {
+ p = std::vector<std::string>{"", issued_prefix.substr(1, issued_prefix.size())};
+ }
+ else if(issued_prefix[issued_prefix.size()-1] == '*')
+ {
+ p = std::vector<std::string>{issued_prefix.substr(0, issued_prefix.size() - 1), ""};
+ }
+ else
+ {
+ p = split_on(issued_prefix, '*');
+ }
+
+ if(p.size() != 2)
+ {
+ return false;
+ }
+
+ // match anything before and after the wildcard character
+ const std::string first = p[0];
+ const std::string last = p[1];
+
+ if(host_prefix.substr(0, first.size()) == first)
+ {
+ host_prefix.erase(0, first.size());
+ }
+
+ // nothing to match anymore
+ if(last.empty())
+ {
return true;
}
+ if(host_prefix.size() >= last.size() &&
+ host_prefix.substr(host_prefix.size() - last.size(), last.size()) == last)
+ {
+ return true;
+ }
+ }
+
return false;
}
-
}
diff --git a/src/scripts/ci/travis/lint.sh b/src/scripts/ci/travis/lint.sh
index 91407c3b4..16d7e5fc0 100755
--- a/src/scripts/ci/travis/lint.sh
+++ b/src/scripts/ci/travis/lint.sh
@@ -2,12 +2,16 @@
set -ev
which shellcheck > /dev/null && shellcheck "$0" # Run shellcheck on this if available
+# Disabled rules in Python 2 only
+# superfluous-parens: needed for print statements
+# too-many-locals: variable counting differs from pylint3
+
echo "travis_fold:start:pylint_configure"
python3 -m pylint configure.py
-python2 -m pylint configure.py
+python2 -m pylint --disable=superfluous-parens,too-many-locals configure.py
echo "travis_fold:end:pylint_configure"
echo "travis_fold:start:pylint_botanpy"
-python3 -m pylint src/python/botan2.py
-python2 -m pylint src/python/botan2.py
+python3 -m pylint src/python/botan2.py || true
+python2 -m pylint --disable=superfluous-parens,too-many-locals src/python/botan2.py || true
echo "travis_fold:end:pylint_botanpy"
diff --git a/src/tests/data/hostnames.vec b/src/tests/data/hostnames.vec
new file mode 100644
index 000000000..91296d2d8
--- /dev/null
+++ b/src/tests/data/hostnames.vec
@@ -0,0 +1,66 @@
+# Test vectors derived from from RFC 6125 and OpenSSL test suite
+# https://github.com/openssl/openssl/blob/master/test/v3nametest.c
+
+Issued = example
+Hostname = example
+
+Issued = example.com
+Hostname = example.com
+
+Issued = a.example.com
+Hostname = a.example.com
+
+Issued = test.www.example.com
+Hostname = test.www.example.com
+
+Issued = *.example.com
+Hostname = foo.example.com
+
+Issued = baz*.example.net
+Hostname = baz1.example.net
+
+Issued = baz*.example.net
+Hostname = baz.example.net
+
+Issued = *baz.example.net
+Hostname = foobaz.example.net
+
+Issued = *baz.example.net
+Hostname = baz.example.net
+
+Issued = b*z.example.net
+Hostname = buzz.example.net
+
+Issued = foo*bar.example.net
+Hostname = foobar.example.net
+
+Issued = *.www.example.com
+Hostname = test.www.example.com
+
+Issued = *www.example.com
+Hostname = www.example.com
+
+[Invalid]
+Issued = example.com
+Hostname = www.example.com
+
+Issued = www.example.com
+Hostname = example.com
+
+Issued = bar.*.example.net
+Hostname = bar.foo.example.net
+
+Issued = *.example.com
+Hostname = bar.foo.example.com
+
+Issued = *.example.com
+Hostname = example.com
+
+Issued = foo*foo.example.com
+Hostname = foo.example.com
+
+Issued = **.example.com
+Hostname = foo.example.com
+
+Issued = *.*.example.com
+Hostname = foo.bar.example.com
diff --git a/src/tests/data/modes/cbc.vec b/src/tests/data/modes/cbc.vec
index 4767b7e50..30ca53f3e 100644
--- a/src/tests/data/modes/cbc.vec
+++ b/src/tests/data/modes/cbc.vec
@@ -1217,6 +1217,13 @@ Nonce = 000102030405060708090A0B0C0D0E0F
In = 6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710
Out = F58C4C04D6E5F1BA779EABFB5F7BFBD69CFC4E967EDB808D679F777BC6702C7D39F23369A9D9BACFA530E26304231461B2EB05E2C39BE9FCDA6C19078C6A9D1B
+# test empty nonce, must be equivalent to zero
+[AES-128/CBC/NoPadding]
+Key = 2B7E151628AED2A6ABF7158809CF4F3C
+Nonce =
+In = 6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710
+Out = 3AD77BB40D7A3660A89ECAF32466EF97B148C17F309EE692287AE57CF12ADD49C93D11BFAF08C5DC4D90B37B4DEE002BA7356E1207BB406639E5E5CEB9A9ED93
+
# RFC 3962: Advanced Encryption Standard (AES) Encryption for Kerberos 5
[AES-128/CBC/CTS]
Key = 636869636b656e207465726979616b69
diff --git a/src/tests/main.cpp b/src/tests/main.cpp
index cb105db18..c90aaec7b 100644
--- a/src/tests/main.cpp
+++ b/src/tests/main.cpp
@@ -35,7 +35,7 @@ namespace {
class Test_Runner : public Botan_CLI::Command
{
public:
- Test_Runner() : Command("test --threads=0 --run-long-tests --run-online-tests --test-runs=1 --drbg-seed= --data-dir= --pkcs11-lib= --log-success *suites") {}
+ Test_Runner() : Command("test --threads=0 --run-long-tests --run-online-tests --test-runs=1 --drbg-seed= --data-dir= --pkcs11-lib= --provider= --log-success *suites") {}
std::string help_text() const override
{
@@ -79,6 +79,7 @@ class Test_Runner : public Botan_CLI::Command
const bool run_long_tests = flag_set("run-long-tests");
const std::string data_dir = get_arg_or("data-dir", "src/tests/data");
const std::string pkcs11_lib = get_arg("pkcs11-lib");
+ const std::string provider = get_arg("provider");
const size_t runs = get_arg_sz("test-runs");
std::vector<std::string> req = get_arg_list("suites");
@@ -146,6 +147,13 @@ class Test_Runner : public Botan_CLI::Command
output() << " pkcs11 library:" << pkcs11_lib;
}
+ Botan_Tests::Provider_Filter pf;
+ if(!provider.empty())
+ {
+ output() << " provider:" << provider;
+ pf.set(provider);
+ }
+
std::unique_ptr<Botan::RandomNumberGenerator> rng;
#if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_HAS_SHA2_64)
@@ -186,7 +194,7 @@ class Test_Runner : public Botan_CLI::Command
output() << "\n";
Botan_Tests::Test::setup_tests(log_success, run_online_tests, run_long_tests,
- data_dir, pkcs11_lib, rng.get());
+ data_dir, pkcs11_lib, pf, rng.get());
for(size_t i = 0; i != runs; ++i)
{
diff --git a/src/tests/test_asn1.cpp b/src/tests/test_asn1.cpp
new file mode 100644
index 000000000..3427d391d
--- /dev/null
+++ b/src/tests/test_asn1.cpp
@@ -0,0 +1,67 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_ASN1)
+ #include <botan/der_enc.h>
+ #include <botan/ber_dec.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_ASN1)
+
+namespace {
+
+Test::Result test_ber_stack_recursion()
+ {
+ Test::Result result("BER stack recursion");
+
+ // OSS-Fuzz #813 GitHub #989
+
+ try
+ {
+ const std::vector<uint8_t> in(10000000, 0);
+ Botan::DataSource_Memory input(in.data(), in.size());
+ Botan::BER_Decoder dec(input);
+
+ while(dec.more_items())
+ {
+ Botan::BER_Object obj;
+ dec.get_next(obj);
+ }
+ }
+ catch(Botan::Decoding_Error&)
+ {
+ }
+
+ result.test_success("No crash");
+
+ return result;
+ }
+
+}
+
+class ASN1_Tests : public Test
+ {
+ public:
+ std::vector<Test::Result> run() override
+ {
+ std::vector<Test::Result> results;
+
+ results.push_back(test_ber_stack_recursion());
+
+ return results;
+ }
+ };
+
+BOTAN_REGISTER_TEST("asn1", ASN1_Tests);
+
+#endif
+
+}
+
diff --git a/src/tests/test_block.cpp b/src/tests/test_block.cpp
index 20d887794..62f32c21c 100644
--- a/src/tests/test_block.cpp
+++ b/src/tests/test_block.cpp
@@ -14,6 +14,11 @@ class Block_Cipher_Tests : public Text_Based_Test
public:
Block_Cipher_Tests() : Text_Based_Test("block", "Key,In,Out") {}
+ std::vector<std::string> possible_providers(const std::string& algo) override
+ {
+ return provider_filter(Botan::BlockCipher::providers(algo));
+ }
+
Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
{
const std::vector<uint8_t> key = get_req_bin(vars, "Key");
@@ -22,7 +27,7 @@ class Block_Cipher_Tests : public Text_Based_Test
Test::Result result(algo);
- const std::vector<std::string> providers = Botan::BlockCipher::providers(algo);
+ const std::vector<std::string> providers = possible_providers(algo);
if(providers.empty())
{
diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp
index c84dfd224..d5ddace04 100644
--- a/src/tests/test_ffi.cpp
+++ b/src/tests/test_ffi.cpp
@@ -234,71 +234,6 @@ class FFI_Unit_Tests : public Test
TEST_FFI_FAIL("bad password", botan_bcrypt_is_valid, ("nope", outstr.data()));
}
- // TODO: Cipher test
- /*
- botan_cipher_t cipher_encrypt, cipher_decrypt;
-
- if(TEST_FFI_OK(botan_cipher_init, (&cipher_encrypt, "AES-128/CBC", BOTAN_CIPHER_INIT_FLAG_ENCRYPT)))
- {
- size_t min_keylen = 0;
- size_t max_keylen = 0;
- TEST_FFI_OK(botan_cipher_query_keylen, (cipher_encrypt, &min_keylen, &max_keylen));
- result.test_int_eq(min_keylen, 16, "Min key length");
- result.test_int_eq(max_keylen, 16, "Max key length");
-
- std::vector<uint8_t> plaintext(256);
- std::vector<uint8_t> nonce(16);
- TEST_FFI_OK(botan_rng_get, (rng, plaintext.data(), plaintext.size()));
- TEST_FFI_OK(botan_rng_get, (rng, nonce.data(), nonce.size()));
-
- std::vector<uint8_t> ciphertext(plaintext.size()); // TODO: no way to know this size from API
-
- const std::vector<uint8_t> symkey = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x01,
- 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
-
- size_t output_written = 0;
- size_t input_consumed = 0;
-
- // Test that after clear or final the object can be reused
- for(size_t r = 0; r != 2; ++r)
- {
- TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
- TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
- TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, 0, ciphertext.data(), ciphertext.size(), &output_written,
- plaintext.data(), plaintext.size(), &input_consumed));
- TEST_FFI_OK(botan_cipher_clear, (cipher_encrypt));
-
- TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
- TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
- TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL, ciphertext.data(), ciphertext.size(), &output_written,
- plaintext.data(), plaintext.size(), &input_consumed));
-
- if(TEST_FFI_OK(botan_cipher_init, (&cipher_decrypt, "AES-128/CBC", BOTAN_CIPHER_INIT_FLAG_DECRYPT)))
- {
- std::vector<uint8_t> decrypted(plaintext.size());
-
- TEST_FFI_OK(botan_cipher_start, (cipher_decrypt, nonce.data(), nonce.size()));
- TEST_FFI_OK(botan_cipher_set_key, (cipher_decrypt, symkey.data(), symkey.size()));
- TEST_FFI_OK(botan_cipher_update, (cipher_decrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL, decrypted.data(), decrypted.size(), &output_written,
- ciphertext.data(), ciphertext.size(), &input_consumed));
-
- result.test_eq("AES plaintext", decrypted, plaintext);
-
- TEST_FFI_OK(botan_cipher_destroy, (cipher_decrypt));
- }
- }
-
- TEST_FFI_OK(botan_cipher_destroy, (cipher_encrypt));
- }
- */
-
- // TODO: AEAD test
- // TODO ONCE MORE WITH AES-GCM
- // botan_cipher_set_associated_data
- // botan_cipher_valid_nonce_length
- // botan_cipher_get_default_nonce_length
- // botan_cipher_get_tag_length
-
// x509 cert test
botan_x509_cert_t cert;
if(TEST_FFI_OK(botan_x509_cert_load_file, (&cert, "src/tests/data/ecc/CSCA.CSCA.csca-germany.1.crt")))
@@ -388,6 +323,9 @@ class FFI_Unit_Tests : public Test
std::vector<Test::Result> results;
results.push_back(ffi_test_mp(rng));
results.push_back(ffi_test_block_ciphers());
+ results.push_back(ffi_test_ciphers_cbc());
+ results.push_back(ffi_test_ciphers_aead());
+ results.push_back(ffi_test_stream_ciphers());
#if defined(BOTAN_HAS_RSA)
results.push_back(ffi_test_rsa(rng));
@@ -409,6 +347,10 @@ class FFI_Unit_Tests : public Test
results.push_back(ffi_test_mceliece(rng));
#endif
+#if defined(BOTAN_HAS_ELGAMAL)
+ results.push_back(ffi_test_elgamal(rng));
+#endif
+
TEST_FFI_OK(botan_rng_destroy, (rng));
results.push_back(result);
@@ -416,6 +358,208 @@ class FFI_Unit_Tests : public Test
}
private:
+ Test::Result ffi_test_ciphers_cbc()
+ {
+ Test::Result result("FFI CBC cipher");
+
+ botan_cipher_t cipher_encrypt, cipher_decrypt;
+
+ if(TEST_FFI_OK(botan_cipher_init, (&cipher_encrypt, "AES-128/CBC/PKCS7", BOTAN_CIPHER_INIT_FLAG_ENCRYPT)))
+ {
+ size_t min_keylen = 0;
+ size_t max_keylen = 0;
+ TEST_FFI_OK(botan_cipher_query_keylen, (cipher_encrypt, &min_keylen, &max_keylen));
+ result.test_int_eq(min_keylen, 16, "Min key length");
+ result.test_int_eq(max_keylen, 16, "Max key length");
+
+ // from https://github.com/geertj/bluepass/blob/master/tests/vectors/aes-cbc-pkcs7.txt
+ const std::vector<uint8_t> plaintext = Botan::hex_decode("0397f4f6820b1f9386f14403be5ac16e50213bd473b4874b9bcbf5f318ee686b1d");
+ const std::vector<uint8_t> symkey = Botan::hex_decode("898be9cc5004ed0fa6e117c9a3099d31");
+ const std::vector<uint8_t> nonce = Botan::hex_decode("9dea7621945988f96491083849b068df");
+ const std::vector<uint8_t> exp_ciphertext = Botan::hex_decode("e232cd6ef50047801ee681ec30f61d53cfd6b0bca02fd03c1b234baa10ea82ac9dab8b960926433a19ce6dea08677e34");
+
+ std::vector<uint8_t> ciphertext(16 + plaintext.size()); // TODO: no way to know this size from API
+
+ size_t output_written = 0;
+ size_t input_consumed = 0;
+
+ // Test that after clear or final the object can be reused
+ for(size_t r = 0; r != 2; ++r)
+ {
+ TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
+ TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
+ TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, 0, ciphertext.data(), ciphertext.size(), &output_written,
+ plaintext.data(), plaintext.size(), &input_consumed));
+ TEST_FFI_OK(botan_cipher_clear, (cipher_encrypt));
+
+ TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
+ TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
+ TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL, ciphertext.data(), ciphertext.size(), &output_written,
+ plaintext.data(), plaintext.size(), &input_consumed));
+
+ ciphertext.resize(output_written);
+ result.test_eq("AES/CBC ciphertext", ciphertext, exp_ciphertext);
+
+ if(TEST_FFI_OK(botan_cipher_init, (&cipher_decrypt, "AES-128/CBC", BOTAN_CIPHER_INIT_FLAG_DECRYPT)))
+ {
+ std::vector<uint8_t> decrypted(plaintext.size());
+
+ TEST_FFI_OK(botan_cipher_set_key, (cipher_decrypt, symkey.data(), symkey.size()));
+ TEST_FFI_OK(botan_cipher_start, (cipher_decrypt, nonce.data(), nonce.size()));
+ TEST_FFI_OK(botan_cipher_update, (cipher_decrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL, decrypted.data(), decrypted.size(), &output_written,
+ ciphertext.data(), ciphertext.size(), &input_consumed));
+
+ result.test_eq("AES/CBC plaintext", decrypted, plaintext);
+
+ TEST_FFI_OK(botan_cipher_destroy, (cipher_decrypt));
+ }
+ }
+
+ TEST_FFI_OK(botan_cipher_destroy, (cipher_encrypt));
+ }
+
+ return result;
+ }
+
+ Test::Result ffi_test_ciphers_aead()
+ {
+ Test::Result result("FFI AEAD");
+
+#if defined(BOTAN_HAS_AEAD_GCM)
+
+ botan_cipher_t cipher_encrypt, cipher_decrypt;
+
+ if(TEST_FFI_OK(botan_cipher_init, (&cipher_encrypt, "AES-128/GCM", BOTAN_CIPHER_INIT_FLAG_ENCRYPT)))
+ {
+ size_t min_keylen = 0;
+ size_t max_keylen = 0;
+ size_t nonce_len = 0;
+ size_t tag_len = 0;
+
+ TEST_FFI_OK(botan_cipher_query_keylen, (cipher_encrypt, &min_keylen, &max_keylen));
+ result.test_int_eq(min_keylen, 16, "Min key length");
+ result.test_int_eq(max_keylen, 16, "Max key length");
+
+ TEST_FFI_OK(botan_cipher_get_default_nonce_length, (cipher_encrypt, &nonce_len));
+ result.test_int_eq(nonce_len, 12, "Expected default GCM nonce length");
+
+ TEST_FFI_OK(botan_cipher_get_tag_length, (cipher_encrypt, &tag_len));
+ result.test_int_eq(tag_len, 16, "Expected GCM tag length");
+
+ TEST_FFI_RC(1, botan_cipher_valid_nonce_length, (cipher_encrypt, 12));
+ // GCM accepts any nonce size...
+ TEST_FFI_RC(1, botan_cipher_valid_nonce_length, (cipher_encrypt, 0));
+
+ // NIST test vector
+ const std::vector<uint8_t> plaintext =
+ Botan::hex_decode("D9313225F88406E5A55909C5AFF5269A86A7A9531534F7DA2E4C303D8A318A721C3C0C95956809532FCF0E2449A6B525B16AEDF5AA0DE657BA637B39");
+
+ const std::vector<uint8_t> symkey = Botan::hex_decode("FEFFE9928665731C6D6A8F9467308308");
+ const std::vector<uint8_t> nonce = Botan::hex_decode("CAFEBABEFACEDBADDECAF888");
+ const std::vector<uint8_t> exp_ciphertext = Botan::hex_decode(
+ "42831EC2217774244B7221B784D0D49CE3AA212F2C02A4E035C17E2329ACA12E21D514B25466931C7D8F6A5AAC84AA051BA30B396A0AAC973D58E0915BC94FBC3221A5DB94FAE95AE7121A47");
+ const std::vector<uint8_t> aad = Botan::hex_decode("FEEDFACEDEADBEEFFEEDFACEDEADBEEFABADDAD2");
+
+ std::vector<uint8_t> ciphertext(tag_len + plaintext.size());
+
+ size_t output_written = 0;
+ size_t input_consumed = 0;
+
+ // Test that after clear or final the object can be reused
+ for(size_t r = 0; r != 2; ++r)
+ {
+ TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
+ TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
+ TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, 0,
+ ciphertext.data(), ciphertext.size(), &output_written,
+ plaintext.data(), plaintext.size(), &input_consumed));
+ TEST_FFI_OK(botan_cipher_clear, (cipher_encrypt));
+
+ TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
+ TEST_FFI_OK(botan_cipher_set_associated_data, (cipher_encrypt, aad.data(), aad.size()));
+ TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
+ TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL,
+ ciphertext.data(), ciphertext.size(), &output_written,
+ plaintext.data(), plaintext.size(), &input_consumed));
+
+ ciphertext.resize(output_written);
+ result.test_eq("AES/GCM ciphertext", ciphertext, exp_ciphertext);
+
+ if(TEST_FFI_OK(botan_cipher_init, (&cipher_decrypt, "AES-128/GCM", BOTAN_CIPHER_INIT_FLAG_DECRYPT)))
+ {
+ std::vector<uint8_t> decrypted(plaintext.size());
+
+ TEST_FFI_OK(botan_cipher_set_key, (cipher_decrypt, symkey.data(), symkey.size()));
+ TEST_FFI_OK(botan_cipher_set_associated_data, (cipher_decrypt, aad.data(), aad.size()));
+ TEST_FFI_OK(botan_cipher_start, (cipher_decrypt, nonce.data(), nonce.size()));
+ TEST_FFI_OK(botan_cipher_update, (cipher_decrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL,
+ decrypted.data(), decrypted.size(), &output_written,
+ ciphertext.data(), ciphertext.size(), &input_consumed));
+
+ result.test_int_eq(input_consumed, ciphertext.size(), "All input consumed");
+ result.test_int_eq(output_written, decrypted.size(), "Expected output size produced");
+ result.test_eq("AES/GCM plaintext", decrypted, plaintext);
+
+ TEST_FFI_OK(botan_cipher_destroy, (cipher_decrypt));
+ }
+ }
+
+ TEST_FFI_OK(botan_cipher_destroy, (cipher_encrypt));
+ }
+#endif
+
+ return result;
+ }
+
+ Test::Result ffi_test_stream_ciphers()
+ {
+ Test::Result result("FFI stream ciphers");
+
+#if defined(BOTAN_HAS_CTR_BE)
+
+ const std::vector<uint8_t> key = Botan::hex_decode("2B7E151628AED2A6ABF7158809CF4F3C");
+ const std::vector<uint8_t> nonce = Botan::hex_decode("F0F1F2F3F4F5F6F7F8F9FAFBFCFDFF");
+ const std::vector<uint8_t> pt = Botan::hex_decode(
+ "AE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710");
+ const std::vector<uint8_t> exp_ct = Botan::hex_decode(
+ "9806F66B7970FDFF8617187BB9FFFDFF5AE4DF3EDBD5D35E5B4F09020DB03EAB1E031DDA2FBE03D1792170A0F3009CEE");
+
+ botan_cipher_t ctr;
+
+ std::vector<uint8_t> ct(pt.size());
+
+ if(TEST_FFI_OK(botan_cipher_init, (&ctr, "AES-128/CTR-BE", BOTAN_CIPHER_INIT_FLAG_ENCRYPT)))
+ {
+ size_t input_consumed = 0;
+ size_t output_written = 0;
+
+ TEST_FFI_OK(botan_cipher_set_key, (ctr, key.data(), key.size()));
+ TEST_FFI_OK(botan_cipher_start, (ctr, nonce.data(), nonce.size()));
+
+ // Test partial updates...
+ TEST_FFI_OK(botan_cipher_update, (ctr, 0,
+ ct.data(), ct.size(), &output_written,
+ pt.data(), 5, &input_consumed));
+
+ result.test_int_eq(output_written, 5, "Expected output written");
+ result.test_int_eq(input_consumed, 5, "Expected input consumed");
+
+ TEST_FFI_OK(botan_cipher_update, (ctr, 0,
+ &ct[5], ct.size() - 5, &output_written,
+ &pt[5], pt.size() - 5, &input_consumed));
+
+ result.test_int_eq(output_written, ct.size() - 5, "Expected output written");
+ result.test_int_eq(input_consumed, pt.size() - 5, "Expected input consumed");
+ result.test_eq("AES-128/CTR ciphertext", ct, exp_ct);
+
+ TEST_FFI_OK(botan_cipher_destroy, (ctr));
+ }
+
+#endif
+
+ return result;
+ }
+
Test::Result ffi_test_block_ciphers()
{
Test::Result result("FFI block ciphers");
@@ -636,6 +780,9 @@ class FFI_Unit_Tests : public Test
TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
result.test_eq("botan_mp_mod_mul", std::string(str_buf), "123945920473931248854653259523111998693");
+ str_len = 0;
+ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_mp_to_str, (r, 10, str_buf, &str_len));
+
size_t x_bytes;
botan_mp_rand_bits(x, rng, 512);
TEST_FFI_OK(botan_mp_num_bytes, (x, &x_bytes));
@@ -1151,6 +1298,78 @@ class FFI_Unit_Tests : public Test
return result;
}
+
+ Test::Result ffi_test_elgamal(botan_rng_t rng)
+ {
+ Test::Result result("FFI ELGAMAL");
+
+ botan_privkey_t priv;
+
+ if(TEST_FFI_OK(botan_privkey_create, (&priv, "ElGamal", nullptr, rng)))
+ {
+ TEST_FFI_OK(botan_privkey_check_key, (priv, rng, 0));
+
+ botan_pubkey_t pub;
+ TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv));
+ TEST_FFI_OK(botan_pubkey_check_key, (pub, rng, 0));
+
+ ffi_test_pubkey_export(result, pub, priv, rng);
+ botan_mp_t p, g, x, y;
+ botan_mp_init(&p);
+ botan_mp_init(&g);
+ botan_mp_init(&x);
+ botan_mp_init(&y);
+
+ TEST_FFI_OK(botan_pubkey_get_field, (p, pub, "p"));
+ TEST_FFI_OK(botan_pubkey_get_field, (g, pub, "g"));
+ TEST_FFI_OK(botan_pubkey_get_field, (y, pub, "y"));
+ TEST_FFI_OK(botan_privkey_get_field,(x, priv,"x"));
+
+ size_t p_len = 0;
+ TEST_FFI_OK(botan_mp_num_bytes, (p, &p_len));
+
+ botan_privkey_t loaded_privkey;
+ TEST_FFI_OK(botan_privkey_load_elgamal, (&loaded_privkey, p, g, x));
+ TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0));
+
+ botan_pubkey_t loaded_pubkey;
+ TEST_FFI_OK(botan_pubkey_load_elgamal, (&loaded_pubkey, p, g, y));
+ TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0));
+
+ botan_mp_destroy(p);
+ botan_mp_destroy(g);
+ botan_mp_destroy(y);
+ botan_mp_destroy(x);
+
+
+ std::vector<uint8_t> plaintext(16, 0xFF);
+ std::vector<uint8_t> ciphertext(p_len*2, 0);
+ std::vector<uint8_t> decryption(16, 0);
+
+ // Test encryption
+ botan_pk_op_encrypt_t op_enc;
+ size_t ct_len = ciphertext.size();
+ REQUIRE_FFI_OK(botan_pk_op_encrypt_create, (&op_enc, loaded_pubkey, "Raw", 0));
+ TEST_FFI_OK(botan_pk_op_encrypt, (op_enc, rng, ciphertext.data(), &ct_len, plaintext.data(), plaintext.size()));
+ TEST_FFI_OK(botan_pk_op_encrypt_destroy, (op_enc));
+
+ // Test decryption
+ botan_pk_op_decrypt_t op_dec;
+ size_t pt_len = decryption.size();
+ REQUIRE_FFI_OK(botan_pk_op_decrypt_create, (&op_dec, loaded_privkey, "Raw", 0));
+ TEST_FFI_OK(botan_pk_op_decrypt, (op_dec, decryption.data(), &pt_len, ciphertext.data(), ct_len));
+ TEST_FFI_OK(botan_pk_op_decrypt_destroy, (op_dec));
+
+ TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey));
+ TEST_FFI_OK(botan_pubkey_destroy, (pub));
+ TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey));
+ TEST_FFI_OK(botan_privkey_destroy, (priv));
+ }
+
+ return result;
+ }
+
+
};
BOTAN_REGISTER_TEST("ffi", FFI_Unit_Tests);
diff --git a/src/tests/test_hash.cpp b/src/tests/test_hash.cpp
index fd255131b..6584a20d1 100644
--- a/src/tests/test_hash.cpp
+++ b/src/tests/test_hash.cpp
@@ -17,6 +17,11 @@ class Hash_Function_Tests : public Text_Based_Test
public:
Hash_Function_Tests() : Text_Based_Test("hash", "In,Out") {}
+ std::vector<std::string> possible_providers(const std::string& algo) override
+ {
+ return provider_filter(Botan::HashFunction::providers(algo));
+ }
+
Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
{
const std::vector<uint8_t> input = get_req_bin(vars, "In");
@@ -24,7 +29,7 @@ class Hash_Function_Tests : public Text_Based_Test
Test::Result result(algo);
- const std::vector<std::string> providers = Botan::HashFunction::providers(algo);
+ const std::vector<std::string> providers = possible_providers(algo);
if(providers.empty())
{
diff --git a/src/tests/test_mac.cpp b/src/tests/test_mac.cpp
index 8fb3b38c2..2e12ee6ed 100644
--- a/src/tests/test_mac.cpp
+++ b/src/tests/test_mac.cpp
@@ -22,6 +22,11 @@ class Message_Auth_Tests : public Text_Based_Test
public:
Message_Auth_Tests() : Text_Based_Test("mac", "Key,In,Out", "IV") {}
+ std::vector<std::string> possible_providers(const std::string& algo) override
+ {
+ return provider_filter(Botan::MessageAuthenticationCode::providers(algo));
+ }
+
Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
{
const std::vector<uint8_t> key = get_req_bin(vars, "Key");
@@ -31,7 +36,7 @@ class Message_Auth_Tests : public Text_Based_Test
Test::Result result(algo);
- const std::vector<std::string> providers = Botan::MessageAuthenticationCode::providers(algo);
+ const std::vector<std::string> providers = possible_providers(algo);
if(providers.empty())
{
diff --git a/src/tests/test_modes.cpp b/src/tests/test_modes.cpp
index 480b15527..63edba5f1 100644
--- a/src/tests/test_modes.cpp
+++ b/src/tests/test_modes.cpp
@@ -22,6 +22,11 @@ class Cipher_Mode_Tests : public Text_Based_Test
Text_Based_Test("modes", "Key,Nonce,In,Out")
{}
+ std::vector<std::string> possible_providers(const std::string& algo) override
+ {
+ return provider_filter(Botan::Cipher_Mode::providers(algo));
+ }
+
Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
{
const std::vector<uint8_t> key = get_req_bin(vars, "Key");
@@ -31,102 +36,115 @@ class Cipher_Mode_Tests : public Text_Based_Test
Test::Result result(algo);
- std::unique_ptr<Botan::Cipher_Mode> enc(Botan::get_cipher_mode(algo, Botan::ENCRYPTION));
- std::unique_ptr<Botan::Cipher_Mode> dec(Botan::get_cipher_mode(algo, Botan::DECRYPTION));
+ const std::vector<std::string> providers = possible_providers(algo);
- if(!enc || !dec)
+ if(providers.empty())
{
- result.note_missing(algo);
+ result.note_missing("cipher mode " + algo);
return result;
}
- result.test_is_nonempty("provider", enc->provider());
- result.test_eq("name", enc->name(), algo);
-
- result.test_eq("mode not authenticated", enc->authenticated(), false);
+ for(auto&& provider_ask : providers)
+ {
+ std::unique_ptr<Botan::Cipher_Mode> enc(Botan::get_cipher_mode(
+ algo, Botan::ENCRYPTION, provider_ask));
+ std::unique_ptr<Botan::Cipher_Mode> dec(Botan::get_cipher_mode(
+ algo, Botan::DECRYPTION, provider_ask));
- // Test to make sure reset() resets what we need it to
- enc->set_key(mutate_vec(key));
- Botan::secure_vector<uint8_t> garbage = Test::rng().random_vec(enc->update_granularity());
- enc->start(mutate_vec(nonce));
- enc->update(garbage);
+ if(!enc || !dec)
+ {
+ result.note_missing(algo);
+ return result;
+ }
- enc->reset();
+ result.test_is_nonempty("provider", enc->provider());
+ result.test_eq("name", enc->name(), algo);
- enc->set_key(key);
- enc->start(nonce);
+ result.test_eq("mode not authenticated", enc->authenticated(), false);
- Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
- // TODO: should first update if possible
- enc->finish(buf);
- result.test_eq("encrypt", buf, expected);
+ // Test to make sure reset() resets what we need it to
+ enc->set_key(mutate_vec(key));
+ Botan::secure_vector<uint8_t> garbage = Test::rng().random_vec(enc->update_granularity());
+ enc->start(mutate_vec(nonce));
+ enc->update(garbage);
- // additionally test process() if possible
- size_t update_granularity = enc->update_granularity();
- size_t input_length = input.size();
- size_t min_final_bytes = enc->minimum_final_size();
- if(input_length > (update_granularity + min_final_bytes))
- {
- // reset state first
enc->reset();
+ enc->set_key(key);
enc->start(nonce);
- buf.assign(input.begin(), input.end());
- // we can process at max input_length
- const size_t max_blocks_to_process = (input_length - min_final_bytes) / update_granularity;
- const size_t bytes_to_process = max_blocks_to_process * update_granularity;
+ Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
+ // TODO: should first update if possible
+ enc->finish(buf);
+ result.test_eq("encrypt", buf, expected);
- const size_t bytes_written = enc->process(buf.data(), bytes_to_process);
+ // additionally test process() if possible
+ size_t update_granularity = enc->update_granularity();
+ size_t input_length = input.size();
+ size_t min_final_bytes = enc->minimum_final_size();
+ if(input_length > (update_granularity + min_final_bytes))
+ {
+ // reset state first
+ enc->reset();
- result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
+ enc->start(nonce);
+ buf.assign(input.begin(), input.end());
- enc->finish(buf, bytes_to_process);
- result.test_eq("encrypt", buf, expected);
- }
+ // we can process at max input_length
+ const size_t max_blocks_to_process = (input_length - min_final_bytes) / update_granularity;
+ const size_t bytes_to_process = max_blocks_to_process * update_granularity;
- // decryption
- buf.assign(expected.begin(), expected.end());
+ const size_t bytes_written = enc->process(buf.data(), bytes_to_process);
- // Test to make sure reset() resets what we need it to
- dec->set_key(mutate_vec(key));
- garbage = Test::rng().random_vec(dec->update_granularity());
- dec->start(mutate_vec(nonce));
- dec->update(garbage);
+ result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
- dec->reset();
+ enc->finish(buf, bytes_to_process);
+ result.test_eq("encrypt", buf, expected);
+ }
- dec->set_key(key);
- dec->start(nonce);
- dec->finish(buf);
- result.test_eq("decrypt", buf, input);
+ // decryption
+ buf.assign(expected.begin(), expected.end());
+
+ // Test to make sure reset() resets what we need it to
+ dec->set_key(mutate_vec(key));
+ garbage = Test::rng().random_vec(dec->update_granularity());
+ dec->start(mutate_vec(nonce));
+ dec->update(garbage);
- // additionally test process() if possible
- update_granularity = dec->update_granularity();
- input_length = expected.size();
- min_final_bytes = dec->minimum_final_size();
- if(input_length > (update_granularity + min_final_bytes))
- {
- // reset state first
dec->reset();
+ dec->set_key(key);
dec->start(nonce);
- buf.assign(expected.begin(), expected.end());
+ dec->finish(buf);
+ result.test_eq("decrypt", buf, input);
- // we can process at max input_length
- const size_t max_blocks_to_process = (input_length - min_final_bytes) / update_granularity;
- const size_t bytes_to_process = max_blocks_to_process * update_granularity;
+ // additionally test process() if possible
+ update_granularity = dec->update_granularity();
+ input_length = expected.size();
+ min_final_bytes = dec->minimum_final_size();
+ if(input_length > (update_granularity + min_final_bytes))
+ {
+ // reset state first
+ dec->reset();
- const size_t bytes_written = dec->process(buf.data(), bytes_to_process);
+ dec->start(nonce);
+ buf.assign(expected.begin(), expected.end());
- result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
+ // we can process at max input_length
+ const size_t max_blocks_to_process = (input_length - min_final_bytes) / update_granularity;
+ const size_t bytes_to_process = max_blocks_to_process * update_granularity;
- dec->finish(buf, bytes_to_process);
- result.test_eq("decrypt", buf, input);
- }
+ const size_t bytes_written = dec->process(buf.data(), bytes_to_process);
+
+ result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
- enc->clear();
- dec->clear();
+ dec->finish(buf, bytes_to_process);
+ result.test_eq("decrypt", buf, input);
+ }
+
+ enc->clear();
+ dec->clear();
+ }
return result;
}
diff --git a/src/tests/test_pubkey.cpp b/src/tests/test_pubkey.cpp
index 9a8c5d2e7..d392437c9 100644
--- a/src/tests/test_pubkey.cpp
+++ b/src/tests/test_pubkey.cpp
@@ -19,15 +19,6 @@
namespace Botan_Tests {
-namespace {
-
-std::vector<std::string> possible_pk_providers()
- {
- return { "base", "openssl", "tpm" };
- }
-
-}
-
void check_invalid_signatures(Test::Result& result,
Botan::PK_Verifier& verifier,
const std::vector<uint8_t>& message,
@@ -91,6 +82,11 @@ void check_invalid_ciphertexts(Test::Result& result,
" invalid ciphertexts, rejected " + std::to_string(ciphertext_rejected));
}
+std::vector<std::string> PK_Test::possible_providers(const std::string&)
+ {
+ return Test::provider_filter({ "base", "openssl", "tpm" });
+ }
+
Test::Result
PK_Signature_Generation_Test::run_one_test(const std::string&, const VarMap& vars)
{
@@ -115,7 +111,7 @@ PK_Signature_Generation_Test::run_one_test(const std::string&, const VarMap& var
std::vector<std::unique_ptr<Botan::PK_Verifier>> verifiers;
- for(std::string verify_provider : possible_pk_providers())
+ for(std::string verify_provider : possible_providers(algo_name()))
{
std::unique_ptr<Botan::PK_Verifier> verifier;
@@ -135,7 +131,7 @@ PK_Signature_Generation_Test::run_one_test(const std::string&, const VarMap& var
verifiers.push_back(std::move(verifier));
}
- for(auto&& sign_provider : possible_pk_providers())
+ for(auto&& sign_provider : possible_providers(algo_name()))
{
std::unique_ptr<Botan::RandomNumberGenerator> rng;
if(vars.count("Nonce"))
@@ -189,7 +185,7 @@ PK_Signature_Verification_Test::run_one_test(const std::string&, const VarMap& v
Test::Result result(algo_name() + "/" + padding + " signature verification");
- for(auto&& verify_provider : possible_pk_providers())
+ for(auto&& verify_provider : possible_providers(algo_name()))
{
std::unique_ptr<Botan::PK_Verifier> verifier;
@@ -219,7 +215,7 @@ PK_Signature_NonVerification_Test::run_one_test(const std::string&, const VarMap
Test::Result result(algo_name() + "/" + padding + " verify invalid signature");
- for(auto&& verify_provider : possible_pk_providers())
+ for(auto&& verify_provider : possible_providers(algo_name()))
{
std::unique_ptr<Botan::PK_Verifier> verifier;
@@ -255,7 +251,7 @@ PK_Encryption_Decryption_Test::run_one_test(const std::string&, const VarMap& va
std::vector<std::unique_ptr<Botan::PK_Decryptor>> decryptors;
- for(auto&& dec_provider : possible_pk_providers())
+ for(auto&& dec_provider : possible_providers(algo_name()))
{
std::unique_ptr<Botan::PK_Decryptor> decryptor;
@@ -273,7 +269,7 @@ PK_Encryption_Decryption_Test::run_one_test(const std::string&, const VarMap& va
}
- for(auto&& enc_provider : possible_pk_providers())
+ for(auto&& enc_provider : possible_providers(algo_name()))
{
std::unique_ptr<Botan::PK_Encryptor> encryptor;
@@ -388,7 +384,7 @@ Test::Result PK_Key_Agreement_Test::run_one_test(const std::string& header, cons
const size_t key_len = get_opt_sz(vars, "OutLen", 0);
- for(auto&& provider : possible_pk_providers())
+ for(auto&& provider : possible_providers(algo_name()))
{
std::unique_ptr<Botan::PK_Key_Agreement> kas;
diff --git a/src/tests/test_pubkey.h b/src/tests/test_pubkey.h
index cbcc38f9d..7709f51c7 100644
--- a/src/tests/test_pubkey.h
+++ b/src/tests/test_pubkey.h
@@ -30,6 +30,9 @@ class PK_Test : public Text_Based_Test
std::string algo_name() const { return m_algo; }
+ protected:
+ std::vector<std::string> possible_providers(const std::string&) override;
+
private:
std::string m_algo;
};
diff --git a/src/tests/test_stream.cpp b/src/tests/test_stream.cpp
index 9250f4e2f..c2c7ce901 100644
--- a/src/tests/test_stream.cpp
+++ b/src/tests/test_stream.cpp
@@ -32,7 +32,8 @@ class Stream_Cipher_Tests : public Text_Based_Test
Test::Result result(algo);
- const std::vector<std::string> providers = Botan::StreamCipher::providers(algo);
+ const std::vector<std::string> providers =
+ provider_filter(Botan::StreamCipher::providers(algo));
if(providers.empty())
{
diff --git a/src/tests/test_utils.cpp b/src/tests/test_utils.cpp
index a2601722d..8c1d353b4 100644
--- a/src/tests/test_utils.cpp
+++ b/src/tests/test_utils.cpp
@@ -1,6 +1,7 @@
/*
* (C) 2015 Jack Lloyd
* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -12,6 +13,7 @@
#include <botan/calendar.h>
#include <botan/internal/rounding.h>
#include <botan/charset.h>
+#include <botan/parsing.h>
#if defined(BOTAN_HAS_BASE64_CODEC)
#include <botan/base64.h>
@@ -454,6 +456,32 @@ class Charset_Tests : public Text_Based_Test
BOTAN_REGISTER_TEST("charset", Charset_Tests);
+class Hostname_Tests : public Text_Based_Test
+ {
+ public:
+ Hostname_Tests() : Text_Based_Test("hostnames.vec", "Issued,Hostname")
+ {}
+
+ Test::Result run_one_test(const std::string& type, const VarMap& vars) override
+ {
+ using namespace Botan;
+
+ Test::Result result("Hostname");
+
+ const std::string issued = get_req_str(vars, "Issued");
+ const std::string hostname = get_req_str(vars, "Hostname");
+ const bool expected = (type == "Invalid") ? false : true;
+
+ const std::string what = hostname + ((expected == true) ?
+ " matches " : " does not match ") + issued;
+ result.test_eq(what, Botan::host_wildcard_match(issued, hostname), expected);
+
+ return result;
+ }
+ };
+
+BOTAN_REGISTER_TEST("hostname", Hostname_Tests);
+
}
}
diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp
index e4a755831..a678c64ae 100644
--- a/src/tests/tests.cpp
+++ b/src/tests/tests.cpp
@@ -417,6 +417,22 @@ std::string Test::Result::result_string(bool verbose) const
return report.str();
}
+std::vector<std::string> Provider_Filter::filter(const std::vector<std::string> &in) const
+ {
+ if(m_provider.empty())
+ {
+ return in;
+ }
+ for(auto&& provider : in)
+ {
+ if(provider == m_provider)
+ {
+ return std::vector<std::string> { provider };
+ }
+ }
+ return std::vector<std::string> {};
+ }
+
// static Test:: functions
//static
std::map<std::string, std::unique_ptr<Test>>& Test::global_registry()
@@ -488,6 +504,7 @@ bool Test::m_log_success = false;
bool Test::m_run_online_tests = false;
bool Test::m_run_long_tests = false;
std::string Test::m_pkcs11_lib;
+Botan_Tests::Provider_Filter Test::m_provider_filter;
//static
void Test::setup_tests(bool log_success,
@@ -495,6 +512,7 @@ void Test::setup_tests(bool log_success,
bool run_long,
const std::string& data_dir,
const std::string& pkcs11_lib,
+ const Botan_Tests::Provider_Filter& pf,
Botan::RandomNumberGenerator* rng)
{
m_data_dir = data_dir;
@@ -503,6 +521,7 @@ void Test::setup_tests(bool log_success,
m_run_long_tests = run_long;
m_test_rng = rng;
m_pkcs11_lib = pkcs11_lib;
+ m_provider_filter = pf;
}
//static
@@ -542,6 +561,12 @@ std::string Test::pkcs11_lib()
}
//static
+std::vector<std::string> Test::provider_filter(const std::vector<std::string>& in)
+ {
+ return m_provider_filter.filter(in);
+ }
+
+//static
Botan::RandomNumberGenerator& Test::rng()
{
if(!m_test_rng)
@@ -824,6 +849,11 @@ parse_cpuid_bits(const std::vector<std::string>& tok)
}
+std::vector<std::string> Text_Based_Test::possible_providers(const std::string&)
+ {
+ return Test::provider_filter({ "base" });
+ }
+
bool Text_Based_Test::skip_this_test(const std::string& /*header*/,
const VarMap& /*vars*/)
{
@@ -868,6 +898,7 @@ std::vector<Test::Result> Text_Based_Test::run()
header = line.substr(1, line.size() - 2);
header_or_name = header;
test_cnt = 0;
+ vars.clear();
continue;
}
@@ -895,7 +926,8 @@ std::vector<Test::Result> Text_Based_Test::run()
{
try
{
- if(skip_this_test(header, vars))
+ if(possible_providers(header).empty() ||
+ skip_this_test(header, vars))
{
continue;
}
@@ -938,6 +970,11 @@ std::vector<Test::Result> Text_Based_Test::run()
}
}
+ if(results.empty())
+ {
+ return results;
+ }
+
try
{
std::vector<Test::Result> final_tests = run_final_tests();
diff --git a/src/tests/tests.h b/src/tests/tests.h
index f90e0037b..406cff57a 100644
--- a/src/tests/tests.h
+++ b/src/tests/tests.h
@@ -46,6 +46,16 @@ class Test_Error : public Botan::Exception
explicit Test_Error(const std::string& what) : Exception("Test error", what) {}
};
+class Provider_Filter
+ {
+ public:
+ Provider_Filter() {}
+ void set(const std::string& provider) { m_provider = provider; }
+ std::vector<std::string> filter(const std::vector<std::string> &) const;
+ private:
+ std::string m_provider;
+ };
+
/*
* A generic test which returns a set of results when run.
* The tests may not all have the same type (for example test
@@ -370,12 +380,14 @@ class Test
bool run_long_tests,
const std::string& data_dir,
const std::string& pkcs11_lib,
+ const Botan_Tests::Provider_Filter& pf,
Botan::RandomNumberGenerator* rng);
static bool log_success();
static bool run_online_tests();
static bool run_long_tests();
static std::string pkcs11_lib();
+ static std::vector<std::string> provider_filter(const std::vector<std::string>&);
static const std::string& data_dir();
@@ -388,6 +400,7 @@ class Test
static Botan::RandomNumberGenerator* m_test_rng;
static bool m_log_success, m_run_online_tests, m_run_long_tests;
static std::string m_pkcs11_lib;
+ static Botan_Tests::Provider_Filter m_provider_filter;
};
/*
@@ -428,8 +441,8 @@ class Text_Based_Test : public Test
virtual Test::Result run_one_test(const std::string& header,
const VarMap& vars) = 0;
-
// Called before run_one_test
+ virtual std::vector<std::string> possible_providers(const std::string&);
virtual bool skip_this_test(const std::string& header,
const VarMap& vars);