aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build-data/arch/ppc64le.txt22
-rw-r--r--src/build-data/buildh.in19
-rw-r--r--src/build-data/os/dragonfly.txt2
-rw-r--r--src/build-data/os/openbsd.txt4
-rw-r--r--src/cli/speed.cpp12
-rw-r--r--src/cli/timing_tests.cpp459
-rw-r--r--src/cli/tls_client.cpp58
-rw-r--r--src/extra_tests/timing/README.md15
-rwxr-xr-xsrc/extra_tests/timing/setup.sh22
-rwxr-xr-xsrc/extra_tests/timing/testsuite.sh23
-rw-r--r--src/extra_tests/timing/timing-tests/BleichenbacherTest.cpp36
-rw-r--r--src/extra_tests/timing/timing-tests/Lucky13Test.cpp55
-rw-r--r--src/extra_tests/timing/timing-tests/Makefile25
-rw-r--r--src/extra_tests/timing/timing-tests/MangerTest.cpp41
-rw-r--r--src/extra_tests/timing/timing-tests/TimingTest.cpp82
-rw-r--r--src/extra_tests/timing/timing-tests/TimingTest.h121
-rw-r--r--src/extra_tests/timing/timing-tests/ecdsa.cpp41
-rw-r--r--src/extra_tests/timing/timing-tests/main.cpp147
-rw-r--r--src/lib/asn1/asn1_time.cpp4
-rw-r--r--src/lib/asn1/ber_dec.h32
-rw-r--r--src/lib/block/blowfish/blowfish.cpp18
-rw-r--r--src/lib/entropy/entropy_src.h6
-rw-r--r--src/lib/entropy/entropy_srcs.cpp11
-rw-r--r--src/lib/entropy/getentropy/getentropy.cpp30
-rw-r--r--src/lib/entropy/getentropy/getentropy.h28
-rw-r--r--src/lib/entropy/getentropy/info.txt9
-rw-r--r--src/lib/ffi/ffi.cpp399
-rw-r--r--src/lib/ffi/ffi.h127
-rw-r--r--src/lib/ffi/info.txt3
-rw-r--r--src/lib/filters/threaded_fork.cpp1
-rw-r--r--src/lib/math/ec_gfp/point_gfp.cpp50
-rw-r--r--src/lib/math/mp/mp_madd.h2
-rw-r--r--src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp8
-rw-r--r--src/lib/pubkey/ec_group/ec_group.cpp41
-rw-r--r--src/lib/pubkey/ec_group/ec_group.h9
-rw-r--r--src/lib/pubkey/ecc_key/ecc_key.cpp32
-rw-r--r--src/lib/utils/calendar.cpp7
-rw-r--r--src/lib/utils/compiler.h4
-rw-r--r--src/lib/utils/os_utils.cpp36
-rw-r--r--src/lib/utils/os_utils.h3
-rw-r--r--src/lib/x509/ocsp.cpp37
-rw-r--r--src/lib/x509/ocsp.h12
-rw-r--r--src/lib/x509/ocsp_types.cpp6
-rw-r--r--src/lib/x509/ocsp_types.h2
-rw-r--r--src/lib/x509/x509path.cpp2
-rw-r--r--src/lib/x509/x509path.h2
-rwxr-xr-xsrc/python/botan2.py15
-rw-r--r--src/scripts/ci/appveyor.yml25
-rwxr-xr-xsrc/scripts/install.py20
-rwxr-xr-xsrc/scripts/website.sh12
-rw-r--r--src/tests/data/bcrypt.vec272
-rw-r--r--src/tests/data/dates.vec3
-rw-r--r--src/tests/data/pubkey/dh_invalid.vec3
-rw-r--r--src/tests/data/pubkey/ecc_invalid.vec18
-rw-r--r--src/tests/data/pubkey/ecdsa_invalid.vec368
-rw-r--r--src/tests/data/timing/bleichenbacher.vec (renamed from src/extra_tests/timing/timing-tests/data/bleichenbacher.vec)0
-rw-r--r--src/tests/data/timing/ecdsa.vec (renamed from src/extra_tests/timing/timing-tests/data/ecdsa.vec)0
-rw-r--r--src/tests/data/timing/lucky13sec3.vec (renamed from src/extra_tests/timing/timing-tests/data/lucky13sec3.vec)0
-rw-r--r--src/tests/data/timing/lucky13sec4sha1.vec (renamed from src/extra_tests/timing/timing-tests/data/lucky13sec4sha1.vec)0
-rw-r--r--src/tests/data/timing/lucky13sec4sha256.vec (renamed from src/extra_tests/timing/timing-tests/data/lucky13sec4sha256.vec)0
-rw-r--r--src/tests/data/timing/lucky13sha384.vec (renamed from src/extra_tests/timing/timing-tests/data/lucky13sha384.vec)0
-rw-r--r--src/tests/data/timing/manger.vec (renamed from src/extra_tests/timing/timing-tests/data/manger.vec)0
-rw-r--r--src/tests/test_ecdsa.cpp40
-rw-r--r--src/tests/test_ffi.cpp236
-rw-r--r--src/tests/test_filters.cpp44
-rw-r--r--src/tests/test_name_constraint.cpp8
-rw-r--r--src/tests/test_ocsp.cpp13
-rw-r--r--src/tests/test_os_utils.cpp7
-rw-r--r--src/tests/test_passhash.cpp26
-rw-r--r--src/tests/test_utils.cpp5
-rw-r--r--src/tests/unit_ecc.cpp27
-rw-r--r--src/tests/unit_tls.cpp10
-rw-r--r--src/tests/unit_x509.cpp117
73 files changed, 2520 insertions, 854 deletions
diff --git a/src/build-data/arch/ppc64le.txt b/src/build-data/arch/ppc64le.txt
new file mode 100644
index 000000000..533a4b0e8
--- /dev/null
+++ b/src/build-data/arch/ppc64le.txt
@@ -0,0 +1,22 @@
+endian little
+
+family ppc
+wordsize 64
+
+<aliases>
+powerpc64
+</aliases>
+
+<submodel_aliases>
+</submodel_aliases>
+
+<submodels>
+power8
+</submodels>
+
+<submodel_aliases>
+</submodel_aliases>
+
+<isa_extensions>
+altivec
+</isa_extensions>
diff --git a/src/build-data/buildh.in b/src/build-data/buildh.in
index de2b5c8b5..393bcc4fb 100644
--- a/src/build-data/buildh.in
+++ b/src/build-data/buildh.in
@@ -80,21 +80,14 @@
#define BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO 1
/*
-* If enabled the ECC implementation will use Montgomery ladder
-* instead of a fixed window implementation.
+* If enabled the ECC implementation will use scalar blinding with order.bits()/2
+* bit long masks.
*/
-#define BOTAN_POINTGFP_BLINDED_MULTIPLY_USE_MONTGOMERY_LADDER 0
-
-/*
-* Set number of bits used to generate mask for blinding the scalar of
-* a point multiplication. Set to zero to disable this side-channel
-* countermeasure.
-*/
-#define BOTAN_POINTGFP_SCALAR_BLINDING_BITS 20
+#define BOTAN_POINTGFP_USE_SCALAR_BLINDING 1
/*
* Set number of bits used to generate mask for blinding the
-* representation of an ECC point. Set to zero to diable this
+* representation of an ECC point. Set to zero to disable this
* side-channel countermeasure.
*/
#define BOTAN_POINTGFP_RANDOMIZE_BLINDING_BITS 80
@@ -104,7 +97,7 @@
* its inverse, of a form appropriate to the algorithm being blinded), and
* then choosing new blinding operands by successive squaring of both
* values. This is much faster than computing a new starting point but
-* introduces some possible coorelation
+* introduces some possible corelation
*
* To avoid possible leakage problems in long-running processes, the blinder
* periodically reinitializes the sequence. This value specifies how often
@@ -129,7 +122,7 @@
* seriously broken system RNG.
*/
#define BOTAN_ENTROPY_DEFAULT_SOURCES \
- { "rdseed", "rdrand", "darwin_secrandom", "dev_random", \
+ { "rdseed", "rdrand", "darwin_secrandom", "getentropy", "dev_random", \
"win32_cryptoapi", "proc_walk", "system_stats" }
diff --git a/src/build-data/os/dragonfly.txt b/src/build-data/os/dragonfly.txt
index 628ac7c98..1e567ffd2 100644
--- a/src/build-data/os/dragonfly.txt
+++ b/src/build-data/os/dragonfly.txt
@@ -9,4 +9,6 @@ posix_mlock
sockets
threads
filesystem
+readdir
+dlopen
</target_features>
diff --git a/src/build-data/os/openbsd.txt b/src/build-data/os/openbsd.txt
index 0e60abd2e..89d291ddc 100644
--- a/src/build-data/os/openbsd.txt
+++ b/src/build-data/os/openbsd.txt
@@ -1,6 +1,8 @@
os_type unix
-soname_suffix "so"
+soname_pattern_base "libbotan-{version_major}.so"
+soname_pattern_abi "libbotan-{version_major}.so.{abi_rev}"
+soname_pattern_patch "libbotan-{version_major}.so.{abi_rev}.{version_minor}"
<target_features>
clock_gettime
diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp
index 772cd6a46..6ea70d3d1 100644
--- a/src/cli/speed.cpp
+++ b/src/cli/speed.cpp
@@ -45,6 +45,10 @@
#include <botan/hmac_rng.h>
#endif
+#if defined(BOTAN_HAS_RDRAND_RNG)
+ #include <botan/rdrand_rng.h>
+#endif
+
#if defined(BOTAN_HAS_FPE_FE1)
#include <botan/fpe_fe1.h>
#endif
@@ -535,6 +539,14 @@ class Speed final : public Command
bench_rng(Botan::system_rng(), "System_RNG", msec, buf_size);
#endif
+#if defined(BOTAN_HAS_RDRAND_RNG)
+ if(Botan::CPUID::has_rdrand())
+ {
+ Botan::RDRAND_RNG rdrand;
+ bench_rng(rdrand, "RDRAND", msec, buf_size);
+ }
+#endif
+
#if defined(BOTAN_HAS_HMAC_DRBG)
for(std::string hash : { "SHA-256", "SHA-384", "SHA-512" })
{
diff --git a/src/cli/timing_tests.cpp b/src/cli/timing_tests.cpp
new file mode 100644
index 000000000..570957cc3
--- /dev/null
+++ b/src/cli/timing_tests.cpp
@@ -0,0 +1,459 @@
+/*
+* Timing Analysis Tests
+*
+* These tests are not for performance, but verifying that two inputs are not handled
+* in a way that is vulnerable to simple timing attacks.
+*
+* Produces output which can be analyzed with the Mona reporting library
+*
+* $ git clone https://github.com/seecurity/mona-timing-report.git
+* $ cd mona-timing-report && ant
+* $ java -jar ReportingTool.jar --lowerBound=0.4 --upperBound=0.5 --inputFile=$file --name=$file
+*
+* (C) 2016 Juraj Somorovsky - [email protected]
+* (C) 2017 Neverhub
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include <botan/hex.h>
+#include <sstream>
+#include <botan/internal/os_utils.h>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ #include <botan/system_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_AUTO_SEEDED_RNG)
+ #include <botan/auto_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_RAW)
+ #include <botan/pubkey.h>
+ #include <botan/rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_TLS_CBC)
+ #include <botan/internal/tls_cbc.h>
+ #include <botan/tls_exceptn.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ #include <botan/ecdsa.h>
+ #include <botan/reducer.h>
+ #include <botan/numthry.h>
+#endif
+
+namespace Botan_CLI {
+
+typedef uint64_t ticks;
+
+class Timing_Test
+ {
+ public:
+ Timing_Test() {}
+ virtual ~Timing_Test() {}
+
+ std::vector<std::vector<ticks>>
+ execute_evaluation(const std::vector<std::string>& inputs,
+ size_t warmup_runs,
+ size_t measurement_runs);
+
+ virtual std::vector<uint8_t> prepare_input(std::string input) = 0;
+
+ virtual ticks measure_critical_function(std::vector<uint8_t> input) = 0;
+
+ protected:
+ static ticks get_ticks()
+ {
+ // Returns CPU counter or best approximation (monotonic clock of some kind)
+ return Botan::OS::get_high_resolution_clock();
+ }
+
+ static Botan::RandomNumberGenerator& timing_test_rng()
+ {
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ return Botan::system_rng();
+#elif defined(BOTAN_HAS_AUTO_SEEDED_RNG)
+ static AutoSeeded_RNG static_timing_test_rng(Botan::Entropy_Sources::global_sources(), 0);
+ return static_timing_test_rng;
+#else
+ // we could just use SHA-256 in OFB mode for these purposes
+ throw CLI_Error("Timing tests require a PRNG");
+#endif
+ }
+
+ };
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_PKCS1v15) && defined(BOTAN_HAS_EME_RAW)
+
+class Bleichenbacker_Timing_Test : public Timing_Test
+ {
+ public:
+ Bleichenbacker_Timing_Test(size_t keysize) :
+ m_privkey(Timing_Test::timing_test_rng(), keysize),
+ m_pubkey(m_privkey),
+ m_enc(m_pubkey, Timing_Test::timing_test_rng(), "Raw"),
+ m_dec(m_privkey, Timing_Test::timing_test_rng(), "PKCS1v15")
+ {
+ }
+
+ std::vector<uint8_t> prepare_input(std::string input) override
+ {
+ const std::vector<uint8_t> input_vector = Botan::hex_decode(input);
+ const std::vector<uint8_t> encrypted = m_enc.encrypt(input_vector, Timing_Test::timing_test_rng());
+ return encrypted;
+ }
+
+ ticks measure_critical_function(std::vector<uint8_t> input) override
+ {
+ const ticks start = get_ticks();
+ m_dec.decrypt_or_random(input.data(), m_ctext_length, m_expected_content_size, Timing_Test::timing_test_rng());
+ const ticks end = get_ticks();
+ return (end - start);
+ }
+
+ private:
+ const size_t m_expected_content_size = 48;
+ const size_t m_ctext_length = 256;
+ Botan::RSA_PrivateKey m_privkey;
+ Botan::RSA_PublicKey m_pubkey;
+ Botan::PK_Encryptor_EME m_enc;
+ Botan::PK_Decryptor_EME m_dec;
+ };
+
+#endif
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_OAEP) && defined(BOTAN_HAS_EME_RAW)
+
+/*
+* Test Manger OAEP side channel
+*
+* "A Chosen Ciphertext Attack on RSA Optimal Asymmetric Encryption
+* Padding (OAEP) as Standardized in PKCS #1 v2.0" James Manger
+* http://archiv.infsec.ethz.ch/education/fs08/secsem/Manger01.pdf
+*/
+class Manger_Timing_Test : public Timing_Test
+ {
+ public:
+ Manger_Timing_Test(size_t keysize) :
+ m_privkey(Timing_Test::timing_test_rng(), keysize),
+ m_pubkey(m_privkey),
+ m_enc(m_pubkey, Timing_Test::timing_test_rng(), m_encrypt_padding),
+ m_dec(m_privkey, Timing_Test::timing_test_rng(), m_decrypt_padding)
+ {}
+
+ std::vector<uint8_t> prepare_input(std::string input) override
+ {
+ const std::vector<uint8_t> input_vector = Botan::hex_decode(input);
+ const std::vector<uint8_t> encrypted = m_enc.encrypt(input_vector, Timing_Test::timing_test_rng());
+ return encrypted;
+ }
+
+ ticks measure_critical_function(std::vector<uint8_t> input) override
+ {
+ ticks start = get_ticks();
+ try
+ {
+ m_dec.decrypt(input.data(), m_ctext_length);
+ }
+ catch (Botan::Decoding_Error e)
+ {
+ }
+ ticks end = get_ticks();
+
+ return (end - start);
+ }
+
+ private:
+ const std::string m_encrypt_padding = "Raw";
+ const std::string m_decrypt_padding = "EME1(SHA-256)";
+ const size_t m_ctext_length = 256;
+ Botan::RSA_PrivateKey m_privkey;
+ Botan::RSA_PublicKey m_pubkey;
+ Botan::PK_Encryptor_EME m_enc;
+ Botan::PK_Decryptor_EME m_dec;
+ };
+
+#endif
+
+#if defined(BOTAN_HAS_TLS_CBC)
+
+/*
+* Test handling of countermeasure to the Lucky13 attack
+*/
+class Lucky13_Timing_Test : public Timing_Test
+ {
+ public:
+ Lucky13_Timing_Test(const std::string& mac_name,
+ size_t mac_keylen) :
+ m_mac_algo(mac_name),
+ m_mac_keylen (mac_keylen),
+ m_dec("AES-128", 16, m_mac_algo, m_mac_keylen, true, false)
+ {}
+
+ std::vector<uint8_t> prepare_input(std::string input) override;
+ ticks measure_critical_function(std::vector<uint8_t> input) override;
+
+ private:
+ const std::string m_mac_algo;
+ const size_t m_mac_keylen;
+ Botan::TLS::TLS_CBC_HMAC_AEAD_Decryption m_dec;
+ };
+
+std::vector<uint8_t> Lucky13_Timing_Test::prepare_input(std::string input)
+ {
+ const std::vector<uint8_t> input_vector = Botan::hex_decode(input);
+ const std::vector<uint8_t> key(16);
+ const std::vector<uint8_t> iv(16);
+
+ std::unique_ptr<Botan::Cipher_Mode> enc(Botan::get_cipher_mode("AES-128/CBC/NoPadding", Botan::ENCRYPTION));
+ enc->set_key(key);
+ enc->start(iv);
+ Botan::secure_vector<uint8_t> buf(input_vector.begin(), input_vector.end());
+ enc->finish(buf);
+
+ return unlock(buf);
+ }
+
+ticks Lucky13_Timing_Test::measure_critical_function(std::vector<uint8_t> input)
+ {
+ Botan::secure_vector<uint8_t> data(input.begin(), input.end());
+ Botan::secure_vector<uint8_t> aad(13);
+ const Botan::secure_vector<uint8_t> iv(16);
+ Botan::secure_vector<uint8_t> key(16 + m_mac_keylen);
+
+ m_dec.set_key(unlock(key));
+ m_dec.set_ad(unlock(aad));
+ m_dec.start(unlock(iv));
+
+ ticks start = get_ticks();
+ try
+ {
+ m_dec.finish(data);
+ }
+ catch(Botan::TLS::TLS_Exception& e)
+ {
+ }
+ ticks end = get_ticks();
+ return (end - start);
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+
+class ECDSA_Timing_Test : public Timing_Test
+ {
+ public:
+ ECDSA_Timing_Test(std::string ecgroup);
+
+ std::vector<uint8_t> prepare_input(std::string input) override;
+ ticks measure_critical_function(std::vector<uint8_t> input) override;
+
+ private:
+ const Botan::ECDSA_PrivateKey m_privkey;
+ const Botan::BigInt m_order;
+ Botan::Blinded_Point_Multiply m_base_point;
+ const Botan::BigInt m_x;
+ const Botan::Modular_Reducer m_mod_order;
+ };
+
+ECDSA_Timing_Test::ECDSA_Timing_Test(std::string ecgroup) :
+ m_privkey(Timing_Test::timing_test_rng(), Botan::EC_Group(ecgroup)),
+ m_order(m_privkey.domain().get_order()),
+ m_base_point(m_privkey.domain().get_base_point(), m_order),
+ m_x(m_privkey.private_value()),
+ m_mod_order(m_order)
+ {
+ }
+
+std::vector<uint8_t> ECDSA_Timing_Test::prepare_input(std::string input)
+ {
+ const std::vector<uint8_t> input_vector = Botan::hex_decode(input);
+ return input_vector;
+ }
+
+ticks ECDSA_Timing_Test::measure_critical_function(std::vector<uint8_t> input)
+ {
+ const Botan::BigInt k(input.data(), input.size());
+ const Botan::BigInt msg(Timing_Test::timing_test_rng(), m_order.bits());
+
+ ticks start = get_ticks();
+
+ //The following ECDSA operations involve and should not leak any information about k.
+ const Botan::PointGFp k_times_P = m_base_point.blinded_multiply(k, Timing_Test::timing_test_rng());
+ const Botan::BigInt r = m_mod_order.reduce(k_times_P.get_affine_x());
+ const Botan::BigInt s = m_mod_order.multiply(inverse_mod(k, m_order), mul_add(m_x, r, msg));
+
+ ticks end = get_ticks();
+
+ return (end - start);
+ }
+
+#endif
+
+std::vector<std::vector<ticks>> Timing_Test::execute_evaluation(const std::vector<std::string>& raw_inputs, size_t warmup_runs, size_t measurement_runs)
+ {
+ std::vector<std::vector<ticks>> all_results(raw_inputs.size());
+ std::vector<std::vector<uint8_t>> inputs(raw_inputs.size());
+
+ for(size_t i = 0; i != all_results.size(); ++i)
+ all_results[i].reserve(measurement_runs);
+
+ for(size_t i = 0; i != inputs.size(); ++i)
+ inputs[i] = prepare_input(raw_inputs[i]);
+
+ // arbitrary upper bounds of 1 and 10 million resp
+ if(warmup_runs > 1000000 || measurement_runs > 100000000)
+ throw CLI_Error("Requested execution counts too large, rejecting");
+
+ size_t total_runs = 0;
+ while(total_runs < (warmup_runs + measurement_runs))
+ {
+ std::vector<ticks> results(inputs.size());
+
+ for(size_t i = 0; i != inputs.size(); ++i)
+ {
+ results[i] = measure_critical_function(inputs[i]);
+ }
+
+ total_runs++;
+
+ if(total_runs >= warmup_runs)
+ {
+ for(size_t i = 0; i != results.size(); ++i)
+ all_results[i].push_back(results[i]);
+ }
+ }
+
+ return all_results;
+ }
+
+class Timing_Test_Command : public Command
+ {
+ public:
+ Timing_Test_Command() : Command("timing_test test_type --test-data-file= --test-data-dir=src/tests/data/timing --warmup-runs=1000 --measurement-runs=10000")
+ {}
+
+ virtual void go() override
+ {
+ const std::string test_type = get_arg("test_type");
+ const size_t warmup_runs = get_arg_sz("warmup-runs");
+ const size_t measurement_runs = get_arg_sz("measurement-runs");
+
+ std::unique_ptr<Timing_Test> test = lookup_timing_test(test_type);
+
+ if(!test)
+ {
+ throw CLI_Error("Unknown or unavailable test type '" + test_type + "'");
+ }
+
+ std::string filename = get_arg_or("test-data-file", "");
+
+ if(filename.empty())
+ {
+ const std::string test_data_dir = get_arg("test-data-dir");
+ filename = test_data_dir + "/" + test_type + ".vec";
+ }
+
+ std::vector<std::string> lines;
+
+ {
+ std::ifstream infile(filename);
+ if(infile.good() == false)
+ throw CLI_Error("Error reading test data from '" + filename + "'");
+ std::string line;
+ while (std::getline(infile, line))
+ {
+ if (line.size() > 0 && line.at(0) != '#')
+ {
+ lines.push_back(line);
+ }
+ }
+ }
+
+ std::vector<std::vector<ticks>> results =
+ test->execute_evaluation(lines, warmup_runs, measurement_runs);
+
+ size_t unique_id = 0;
+ std::ostringstream oss;
+ for(size_t secret_id = 0; secret_id != results.size(); ++secret_id)
+ {
+ for(size_t i = 0; i != results[secret_id].size(); ++i)
+ {
+ oss << unique_id++ << ";" << secret_id << ";" << results[secret_id][i] << "\n";
+ }
+ }
+
+ output() << oss.str();
+ }
+ private:
+ std::unique_ptr<Timing_Test> lookup_timing_test(const std::string& test_type);
+
+ virtual std::string help_text() const override
+ {
+ // TODO check feature macros
+ return (Command::help_text() +
+ "\ntest_type can take on values " +
+ "bleichenbacher " +
+ "manger "
+ "ecdsa " +
+ "lucky13sha1sec3 " +
+ "lucky13sha256sec3 " +
+ "lucky13sec4sha1 " +
+ "lucky13sec4sha256 " +
+ "lucky13sec4sha384 "
+ );
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("timing_test", Timing_Test_Command);
+
+std::unique_ptr<Timing_Test> Timing_Test_Command::lookup_timing_test(const std::string& test_type)
+ {
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_PKCS1v15) && defined(BOTAN_HAS_EME_RAW)
+ if(test_type == "bleichenbacher")
+ {
+ return std::unique_ptr<Timing_Test>(new Bleichenbacker_Timing_Test(2048));
+ }
+#endif
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_OAEP) && defined(BOTAN_HAS_EME_RAW)
+ if(test_type == "manger")
+ {
+ return std::unique_ptr<Timing_Test>(new Manger_Timing_Test(2048));
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ if(test_type == "ecdsa")
+ {
+ return std::unique_ptr<Timing_Test>(new ECDSA_Timing_Test("secp384r1"));
+ }
+#endif
+
+#if defined(BOTAN_HAS_TLS_CBC)
+ if(test_type == "lucky13sha1sec3" || test_type == "lucky13sha1sec4")
+ {
+ return std::unique_ptr<Timing_Test>(new Lucky13_Timing_Test("SHA-1", 20));
+ }
+ if(test_type == "lucky13sha256sec3" || test_type == "lucky13sha256sec4")
+ {
+ return std::unique_ptr<Timing_Test>(new Lucky13_Timing_Test("SHA-256", 32));
+ }
+ if(test_type == "lucky13sha384")
+ {
+ return std::unique_ptr<Timing_Test>(new Lucky13_Timing_Test("SHA-384", 48));
+ }
+#endif
+
+ BOTAN_UNUSED(test_type);
+
+ return nullptr;
+ }
+
+
+}
diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp
index 30871791c..3cba471f0 100644
--- a/src/cli/tls_client.cpp
+++ b/src/cli/tls_client.cpp
@@ -25,6 +25,7 @@
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
@@ -117,12 +118,21 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks
version = Botan::TLS::Protocol_Version::TLS_V11;
}
+ struct sockaddr_storage addrbuf;
+ std::string hostname;
+ if(!host.empty() &&
+ inet_pton(AF_INET, host.c_str(), &addrbuf) != 1 &&
+ inet_pton(AF_INET6, host.c_str(), &addrbuf) != 1)
+ {
+ hostname = host;
+ }
+
Botan::TLS::Client client(*this,
*session_mgr,
creds,
*policy,
rng(),
- Botan::TLS::Server_Information(host, port),
+ Botan::TLS::Server_Information(hostname, port),
version,
protocols_to_offer);
@@ -218,37 +228,43 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks
private:
int connect_to_host(const std::string& host, uint16_t port, bool tcp)
{
- hostent* host_addr = ::gethostbyname(host.c_str());
-
- if(!host_addr)
- throw CLI_Error("gethostbyname failed for " + host);
+ addrinfo hints = {};
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = tcp ? SOCK_STREAM : SOCK_DGRAM;
+ addrinfo *res, *rp = nullptr;
- if(host_addr->h_addrtype != AF_INET) // FIXME
- throw CLI_Error(host + " has IPv6 address, not supported");
+ if(::getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res) != 0)
+ {
+ throw CLI_Error("getaddrinfo failed for " + host);
+ }
- int type = tcp ? SOCK_STREAM : SOCK_DGRAM;
+ int fd = 0;
- int fd = ::socket(PF_INET, type, 0);
- if(fd == -1)
- throw CLI_Error("Unable to acquire socket");
+ for(rp = res; rp != nullptr; rp = rp->ai_next)
+ {
+ fd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
- sockaddr_in socket_info;
- ::memset(&socket_info, 0, sizeof(socket_info));
- socket_info.sin_family = AF_INET;
- socket_info.sin_port = htons(port);
+ if(fd == -1)
+ {
+ continue;
+ }
- ::memcpy(&socket_info.sin_addr,
- host_addr->h_addr,
- host_addr->h_length);
+ if(::connect(fd, rp->ai_addr, rp->ai_addrlen) != 0)
+ {
+ close(fd);
+ continue;
+ }
- socket_info.sin_addr = *reinterpret_cast<struct in_addr*>(host_addr->h_addr); // FIXME
+ break;
+ }
- if(::connect(fd, reinterpret_cast<sockaddr*>(&socket_info), sizeof(struct sockaddr)) != 0)
+ if(rp == nullptr) // no address succeeded
{
- ::close(fd);
throw CLI_Error("connect failed");
}
+ ::freeaddrinfo(res);
+
return fd;
}
diff --git a/src/extra_tests/timing/README.md b/src/extra_tests/timing/README.md
deleted file mode 100644
index 98fde428c..000000000
--- a/src/extra_tests/timing/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Timing testsuite
-
-Extended Botan library test suite with mona-timing-lib.
-
-Run
-```bash
-setup.sh
-```
-to download and build the recent mona-timing-lib version.
-
-Run
-```bash
-testsuite.sh
-```
-to start the test suite. \ No newline at end of file
diff --git a/src/extra_tests/timing/setup.sh b/src/extra_tests/timing/setup.sh
deleted file mode 100755
index 4e2c52fb0..000000000
--- a/src/extra_tests/timing/setup.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-
-mkdir timing-tests/results
-mkdir timing-tests/results/bleichenbacher
-mkdir timing-tests/results/lucky13sec3
-mkdir timing-tests/results/lucky13sec4
-mkdir timing-tests/results/lucky13sha256sec3
-mkdir timing-tests/results/lucky13sha256sec4
-mkdir timing-tests/results/lucky13sha384
-mkdir timing-tests/results/manger
-
-if [ ! -d mona-timing-report ]
-then
- git clone https://github.com/seecurity/mona-timing-report.git
-fi
-
-cd mona-timing-report
-ant
-
-cd ../../../../
-./configure.py
-make -j4
diff --git a/src/extra_tests/timing/testsuite.sh b/src/extra_tests/timing/testsuite.sh
deleted file mode 100755
index 186624c46..000000000
--- a/src/extra_tests/timing/testsuite.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/sh
-
-cd timing-tests
-make
-./main
-
-zipdate=$(date +%d-%H-%M)
-zipfile="${zipdate}-results.zip"
-zip -r $zipfile results
-
-cd ..
-cd mona-timing-report
-
-for dir in ../timing-tests/results/*;
-do
- echo "Working in directory: " $dir
- for file in $dir/*;
- do
- echo "Creating report for: " $file
- java -jar ReportingTool.jar --inputFile=$file --name=$file --lowerBound=0.4 --upperBound=0.5
- rm $file
- done
-done
diff --git a/src/extra_tests/timing/timing-tests/BleichenbacherTest.cpp b/src/extra_tests/timing/timing-tests/BleichenbacherTest.cpp
deleted file mode 100644
index abef8a7be..000000000
--- a/src/extra_tests/timing/timing-tests/BleichenbacherTest.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * File: BleichenbacherTest.cpp
- * Author: Juraj Somorovsky - [email protected]
- *
- */
-
-#include "TimingTest.h"
-
-BleichenbacherTest::BleichenbacherTest(std::vector<std::string> &inputs,
- std::string result_folder, int keysize) :
- m_privkey(system_rng(), keysize),
- m_pubkey(m_privkey),
- m_enc(m_pubkey, m_encrypt_padding),
- m_dec(m_privkey, m_decrypt_padding)
- {
- m_inputs = inputs;
- m_result_folder = result_folder;
- }
-
-std::vector<byte> BleichenbacherTest::prepare_input(std::string input)
- {
- const std::vector<uint8_t> input_vector = Botan::hex_decode(input);
- const std::vector<byte> encrypted = m_enc.encrypt(input_vector, system_rng());
- return encrypted;
- }
-
-ticks BleichenbacherTest::measure_critical_function(std::vector<byte> input)
- {
- const Botan::byte* in = &input[0];
-
- ticks start = this->get_ticks();
- m_dec.decrypt_or_random(in, m_ctext_length, m_expected_content_size, system_rng());
- ticks end = get_ticks();
-
- return (end - start);
- }
diff --git a/src/extra_tests/timing/timing-tests/Lucky13Test.cpp b/src/extra_tests/timing/timing-tests/Lucky13Test.cpp
deleted file mode 100644
index 55f7dab1b..000000000
--- a/src/extra_tests/timing/timing-tests/Lucky13Test.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * File: Lucky13Test.cpp
- * Author: Juraj Somorovsky - [email protected]
- *
- */
-
-#include "TimingTest.h"
-
-Lucky13Test::Lucky13Test(std::vector<std::string> &inputs, std::string result_folder,
- const std::string& mac_name, size_t mac_keylen) :
- m_dec("AES-128", 16, mac_name, mac_keylen, true, false),
- m_mac_keylen (mac_keylen)
- {
- m_inputs = inputs;
- m_result_folder = result_folder;
- }
-
-std::vector<byte> Lucky13Test::prepare_input(std::string input)
- {
- const std::vector<uint8_t> input_vector = Botan::hex_decode(input);
- const std::vector<uint8_t> key(16);
- const std::vector<uint8_t> iv(16);
-
- std::unique_ptr<Botan::Cipher_Mode> enc(Botan::get_cipher_mode("AES-128/CBC/NoPadding", Botan::ENCRYPTION));
- enc->set_key(key);
- enc->start(iv);
- Botan::secure_vector<uint8_t> buf(input_vector.begin(), input_vector.end());
- enc->finish(buf);
-
- return unlock(buf);
- }
-
-ticks Lucky13Test::measure_critical_function(std::vector<byte> input)
- {
- Botan::secure_vector<byte> data(input.begin(), input.end());
- Botan::secure_vector<byte> aad(13);
- const Botan::secure_vector<byte> iv(16);
- Botan::secure_vector<byte> key(16 + m_mac_keylen);
-
- m_dec.set_key(unlock(key));
- m_dec.set_ad(unlock(aad));
- m_dec.start(unlock(iv));
-
- ticks start = this->get_ticks();
- try
- {
- m_dec.finish(data);
- }
- catch (Botan::TLS::TLS_Exception e)
- {
-
- }
- ticks end = get_ticks();
- return (end - start);
- }
diff --git a/src/extra_tests/timing/timing-tests/Makefile b/src/extra_tests/timing/timing-tests/Makefile
deleted file mode 100644
index 56cc7b9eb..000000000
--- a/src/extra_tests/timing/timing-tests/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-CC=g++
-BOTAN_DIR=../../../../
-LDIR=$(BOTAN_DIR)
-IDIR=$(BOTAN_DIR)build/include
-DBG_FLAGS=-g -O3
-LIBS=-lbotan-2
-
-CPPFLAGS=$(DBG_FLAGS) -std=c++11 -I$(IDIR) -L$(LDIR) -Wl,-R$(LDIR) '-Wl,-R$$ORIGIN'
-LDFLAGS=$(DBG_FLAGS) -I$(IDIR) -L$(LDIR) -Wl,-R$(LDIR) '-Wl,-R$$ORIGIN' $(LIBS)
-
-SOURCES = $(shell find . -maxdepth 1 -name "*.cpp")
-HEADERS = $(shell find . -maxdepth 1 -name "*.h")
-OBJECTS = $(SOURCES:%.cpp=%.o)
-BINARY = main
-
-all: ${BINARY}
-
-${BINARY}: $(OBJECTS) $(HEADERS)
- ${CC} $(LDFLAGS) -o ${BINARY} $(OBJECTS) $(LIBS)
-
-# rebuild objects if headers changed
-$(OBJECTS): $(HEADERS)
-
-clean:
- rm -f ${BINARY} $(OBJECTS)
diff --git a/src/extra_tests/timing/timing-tests/MangerTest.cpp b/src/extra_tests/timing/timing-tests/MangerTest.cpp
deleted file mode 100644
index 6ed39819b..000000000
--- a/src/extra_tests/timing/timing-tests/MangerTest.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * File: MangerTest.cpp
- * Author: Juraj Somorovsky - [email protected]
- *
- */
-
-#include "TimingTest.h"
-
-MangerTest::MangerTest(std::vector<std::string> &inputs, std::string result_folder, int keysize) :
- m_privkey(system_rng(), keysize),
- m_pubkey(m_privkey),
- m_enc(m_pubkey, m_encrypt_padding),
- m_dec(m_privkey, m_decrypt_padding)
- {
- m_inputs = inputs;
- m_result_folder = result_folder;
- }
-
-std::vector<byte> MangerTest::prepare_input(std::string input)
- {
- const std::vector<uint8_t> input_vector = Botan::hex_decode(input);
- const std::vector<byte> encrypted = m_enc.encrypt(input_vector, system_rng());
- return encrypted;
- }
-
-ticks MangerTest::measure_critical_function(std::vector<byte> input)
- {
- const Botan::byte* in = &input[0];
-
- ticks start = this->get_ticks();
- try
- {
- m_dec.decrypt(in, m_ctext_length);
- }
- catch (Botan::Decoding_Error e)
- {
- }
- ticks end = get_ticks();
-
- return (end - start);
- }
diff --git a/src/extra_tests/timing/timing-tests/TimingTest.cpp b/src/extra_tests/timing/timing-tests/TimingTest.cpp
deleted file mode 100644
index f33cbd34e..000000000
--- a/src/extra_tests/timing/timing-tests/TimingTest.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * File: TimingTest.cpp
- * Author: Juraj Somorovsky - [email protected]
- *
- */
-
-#include "TimingTest.h"
-#include <time.h>
-
-TimingTest::TimingTest()
- {
- }
-
-TimingTest::~TimingTest()
- {
- }
-
-void TimingTest::execute_evaluation()
- {
- m_results = std::vector<ticks>(m_measurement_iterations * 2);
-
- for (int i = 0; i < m_inputs.size() - 1; i++)
- {
- for (int j = i + 1; j < m_inputs.size(); j++)
- {
- std::cout << "\nExecuting measurements for inputs " << i << " and " << j;
- std::vector<byte> input1 = prepare_input(m_inputs[i]);
- std::vector<byte> input2 = prepare_input(m_inputs[j]);
-
- for (int k = 0; k < m_warmup_iterations + m_measurement_iterations; k++)
- {
- ticks t1 = measure_critical_function(input1);
- ticks t2 = measure_critical_function(input2);
-
- if (k >= m_warmup_iterations)
- {
- m_results[ (k - m_warmup_iterations) * 2] = t1;
- m_results[ (k - m_warmup_iterations) * 2 + 1] = t2;
- }
- }
- clock_t t;
- t = clock();
- store_results_in_file(std::to_string(t) + "test" + std::to_string(i) + std::to_string(j));
- }
- }
- }
-
-void TimingTest::store_results_in_file(std::string file)
- {
- std::ofstream output(m_result_folder + "/" + file);
- for (int i = 0; i < m_measurement_iterations; i++)
- {
- output << 2 * i << ";1;" << m_results[2 * i] << "\n";
- output << 2 * i + 1 << ";2;" << m_results[2 * i + 1] << "\n";
- }
- }
-
-/**
- * Taken from Mona Timing Lib
- * Thanks Sebastian ;)
- *
- * @return Number of processor ticks read using the RDTSC assembler instruction.
- */
-ticks TimingTest::get_ticks()
- {
- ticks ret = 0;
- unsigned long minor = 0;
- unsigned long mayor = 0;
-
- asm volatile(
- "cpuid \n"
- "rdtsc"
- : "=a"(minor),
- "=d"(mayor)
- : "a" (0)
- : "%ebx", "%ecx"
- );
-
- ret = ((((ticks) mayor) << 32) | ((ticks) minor));
-
- return ret;
- }
diff --git a/src/extra_tests/timing/timing-tests/TimingTest.h b/src/extra_tests/timing/timing-tests/TimingTest.h
deleted file mode 100644
index a0295c6fd..000000000
--- a/src/extra_tests/timing/timing-tests/TimingTest.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * File: TimingTest.h
- * Author: Juraj Somorovsky - [email protected]
- *
- */
-
-#ifndef TIMINGTEST_H
-#define TIMINGTEST_H
-
-#include <botan/pubkey.h>
-#include <botan/rsa.h>
-#include <botan/system_rng.h>
-#include <botan/hex.h>
-#include <botan/rng.h>
-#include <botan/numthry.h>
-#include <iostream>
-#include <vector>
-#include <fstream>
-#include <botan/internal/tls_cbc.h>
-#include <botan/tls_exceptn.h>
-#include <botan/cipher_mode.h>
-#include <botan/ecdsa.h>
-#include <botan/reducer.h>
-
-using namespace Botan;
-
-typedef unsigned long long ticks;
-
-class TimingTest
- {
- protected:
- std::string m_result_folder;
- std::vector<std::string> m_inputs;
- std::vector<ticks> m_results;
- unsigned int m_warmup_iterations = 500;
- unsigned int m_measurement_iterations = 10000;
- virtual std::vector<byte> prepare_input(std::string input) = 0;
- virtual ticks measure_critical_function(std::vector<byte> input) = 0;
-
- public:
- TimingTest();
- virtual ~TimingTest();
- void execute_evaluation();
- void store_results_in_file(std::string file_name);
- ticks get_ticks();
- };
-
-class BleichenbacherTest : public TimingTest
- {
- private:
- const std::string m_encrypt_padding = "Raw";
- const std::string m_decrypt_padding = "PKCS1v15";
- const size_t m_expected_content_size = 48;
- const size_t m_ctext_length = 256;
- RSA_PrivateKey m_privkey;
- RSA_PublicKey m_pubkey;
- PK_Encryptor_EME m_enc;
- PK_Decryptor_EME m_dec;
-
- protected:
- std::vector<byte> prepare_input(std::string input) override;
- ticks measure_critical_function(std::vector<byte> input) override;
-
- public:
- BleichenbacherTest(std::vector<std::string> &inputs, std::string result_file, int keysize);
- };
-
-class MangerTest : public TimingTest
- {
- private:
- const std::string m_encrypt_padding = "Raw";
- const std::string m_decrypt_padding = "EME1(SHA-256)";
- const size_t m_ctext_length = 256;
- RSA_PrivateKey m_privkey;
- RSA_PublicKey m_pubkey;
- PK_Encryptor_EME m_enc;
- PK_Decryptor_EME m_dec;
-
- protected:
- std::vector<byte> prepare_input(std::string input) override;
- ticks measure_critical_function(std::vector<byte> input) override;
-
- public:
- MangerTest(std::vector<std::string> &inputs, std::string result_file, int keysize);
- };
-
-class Lucky13Test : public TimingTest
- {
- private:
- const std::string m_mac_algo;
- const size_t m_mac_keylen;
- Botan::TLS::TLS_CBC_HMAC_AEAD_Decryption m_dec;
-
- protected:
- std::vector<byte> prepare_input(std::string input) override;
- ticks measure_critical_function(std::vector<byte> input) override;
-
- public:
- Lucky13Test(std::vector<std::string> &inputs, std::string result_file,
- const std::string& mac_name, size_t mac_keylen);
- };
-
-class ECDSATest : public TimingTest
- {
- private:
- const ECDSA_PrivateKey m_privkey;
- const BigInt m_order;
- Blinded_Point_Multiply m_base_point;
- const BigInt m_x;
- const Modular_Reducer m_mod_order;
-
- protected:
- std::vector<byte> prepare_input(std::string input) override;
- ticks measure_critical_function(std::vector<byte> input) override;
-
- public:
- ECDSATest(std::vector<std::string> &inputs, std::string result_folder, std::string ecgroup);
- };
-
-
-#endif /* TIMINGTEST_H */ \ No newline at end of file
diff --git a/src/extra_tests/timing/timing-tests/ecdsa.cpp b/src/extra_tests/timing/timing-tests/ecdsa.cpp
deleted file mode 100644
index 42f281911..000000000
--- a/src/extra_tests/timing/timing-tests/ecdsa.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * File: ecdsa.cpp
- *
- *
- */
-
-#include "TimingTest.h"
-
-ECDSATest::ECDSATest(std::vector<std::string> &inputs, std::string result_folder, std::string ecgroup) :
- m_privkey(system_rng(), Botan::EC_Group(ecgroup)),
- m_order(m_privkey.domain().get_order()),
- m_base_point(m_privkey.domain().get_base_point(), m_order),
- m_x(m_privkey.private_value()),
- m_mod_order(m_order)
- {
- m_inputs = inputs;
- m_result_folder = result_folder;
- }
-
-std::vector<byte> ECDSATest::prepare_input(std::string input)
- {
- const std::vector<byte> input_vector = Botan::hex_decode(input);
- return input_vector;
- }
-
-ticks ECDSATest::measure_critical_function(std::vector<byte> input)
- {
- const BigInt k(input.data(), input.size());
- const BigInt msg(system_rng(), m_order.bits());
-
- ticks start = this->get_ticks();
-
- //The following ECDSA operations involve and should not leak any information about k.
- const Botan::PointGFp k_times_P = m_base_point.blinded_multiply(k, system_rng());
- const BigInt r = m_mod_order.reduce(k_times_P.get_affine_x());
- const BigInt s = m_mod_order.multiply(inverse_mod(k, m_order), mul_add(m_x, r, msg));
-
- ticks end = get_ticks();
-
- return (end - start);
- }
diff --git a/src/extra_tests/timing/timing-tests/main.cpp b/src/extra_tests/timing/timing-tests/main.cpp
deleted file mode 100644
index 02742da84..000000000
--- a/src/extra_tests/timing/timing-tests/main.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * File: main.cpp
- * Author: Juraj Somorovsky - [email protected]
- *
- */
-
-#include <botan/pubkey.h>
-#include <botan/rsa.h>
-#include <botan/system_rng.h>
-#include <botan/hex.h>
-#include <botan/rng.h>
-#include <iostream>
-#include <vector>
-#include <fstream>
-#include <sstream>
-#include <dirent.h>
-#include <botan/numthry.h>
-
-
-#include "TimingTest.h"
-
-/**
- * Reads directory and outputs a vector of files.
- *
- * @param dir_path
- * @return
- */
-std::vector<std::string> read_dir(const std::string& dir_path)
- {
- DIR *dir;
- struct dirent *ent;
- std::vector<std::string> out;
- if ((dir = opendir(dir_path.c_str())) != NULL)
- {
- while ((ent = readdir(dir)) != NULL)
- {
- const std::string filename = ent->d_name;
- if (filename == "." || filename == "..")
- {
- continue;
- }
- const std::string full_path = dir_path + "/" + filename;
- out.push_back(full_path);
- }
- closedir(dir);
- }
- return out;
- }
-
-/*
- * Reads vectors from a given file
- *
- */
-std::vector<std::string> read_vectors(const std::string& filename)
- {
- std::string line;
- std::ifstream infile(filename);
- std::vector<std::string> out;
- while (std::getline(infile, line))
- {
- if (line.at(0) != '#')
- {
- out.push_back(line);
- }
- }
- return out;
- }
-
-bool executeEvaluationWithFile(std::string test, std::string filename, std::string arg)
- {
- if ((arg == "" || test.find(arg) != std::string::npos) &&
- (filename.find(test) != std::string::npos))
- {
- return true;
- }
- return false;
- }
-
-int main(int argc, char* argv[])
- {
- std::vector<std::string> files = read_dir("data");
- std::string test_arg;
- if(argc < 2)
- {
- test_arg = "";
- }
- else
- {
- test_arg = argv[1];
- }
- for (auto const& file : files)
- {
- std::vector<std::string> inputs = read_vectors(file);
-
- if (executeEvaluationWithFile("bleichenbacher", file, test_arg))
- {
- std::string result_folder = "results/bleichenbacher";
- std::unique_ptr<BleichenbacherTest> test(new BleichenbacherTest(inputs, result_folder, 2048));
- test->execute_evaluation();
- }
- else if (executeEvaluationWithFile("manger", file, test_arg))
- {
- std::string result_folder = "results/manger";
- std::unique_ptr<MangerTest> test(new MangerTest(inputs, result_folder, 2048));
- test->execute_evaluation();
- }
- else if (executeEvaluationWithFile("lucky13sec3", file, test_arg))
- {
- std::string result_folder_sha1 = "results/lucky13sha1sec3";
- std::unique_ptr<Lucky13Test> test_sha1(new Lucky13Test(inputs, result_folder_sha1, "SHA-1", 20));
- test_sha1->execute_evaluation();
- std::string result_folder_sha256 = "results/lucky13sha256sec3";
- std::unique_ptr<Lucky13Test> test_sha256(new Lucky13Test(inputs, result_folder_sha256, "SHA-256", 32));
- test_sha256->execute_evaluation();
- }
- else if (executeEvaluationWithFile("lucky13sec4sha1", file, test_arg))
- {
- std::string result_folder_sha1 = "results/lucky13sha1sec4";
- std::unique_ptr<Lucky13Test> test_sha1(new Lucky13Test(inputs, result_folder_sha1, "SHA-1", 20));
- test_sha1->execute_evaluation();
- }
- else if (executeEvaluationWithFile("lucky13sec4sha256", file, test_arg))
- {
- std::string result_folder_sha256 = "results/lucky13sha256sec4";
- std::unique_ptr<Lucky13Test> test_sha256(new Lucky13Test(inputs, result_folder_sha256, "SHA-256", 32));
- test_sha256->execute_evaluation();
- }
- else if (executeEvaluationWithFile("lucky13sha384", file, test_arg))
- {
- std::string result_folder_sha384 = "results/lucky13sha384";
- std::unique_ptr<Lucky13Test> test_sha384(new Lucky13Test(inputs, result_folder_sha384, "SHA-384", 48));
- test_sha384->execute_evaluation();
- }
- else if (executeEvaluationWithFile("ecdsa", file, test_arg))
- {
- std::string result_folder_ecdsa = "results/ecdsa";
- std::unique_ptr<ECDSATest> test_ecdsa(new ECDSATest(inputs, result_folder_ecdsa, "secp384r1"));
- test_ecdsa->execute_evaluation();
- }
- else
- {
- std::cout << "\nSkipping the following test: " << file;
- }
- }
-
- return 1;
- }
diff --git a/src/lib/asn1/asn1_time.cpp b/src/lib/asn1/asn1_time.cpp
index ef259740b..5890ac7ca 100644
--- a/src/lib/asn1/asn1_time.cpp
+++ b/src/lib/asn1/asn1_time.cpp
@@ -171,12 +171,12 @@ void X509_Time::set_to(const std::string& t_spec, ASN1_Tag spec_tag)
if(spec_tag == GENERALIZED_TIME)
{
- if(t_spec.size() != 13 && t_spec.size() != 15)
+ if(t_spec.size() != 15)
throw Invalid_Argument("Invalid GeneralizedTime string: '" + t_spec + "'");
}
else if(spec_tag == UTC_TIME)
{
- if(t_spec.size() != 11 && t_spec.size() != 13)
+ if(t_spec.size() != 13)
throw Invalid_Argument("Invalid UTCTime string: '" + t_spec + "'");
}
diff --git a/src/lib/asn1/ber_dec.h b/src/lib/asn1/ber_dec.h
index 810896880..6320ab9fc 100644
--- a/src/lib/asn1/ber_dec.h
+++ b/src/lib/asn1/ber_dec.h
@@ -34,6 +34,38 @@ class BOTAN_DLL BER_Decoder
BER_Decoder& get_next(BER_Object& ber);
+ /**
+ * Get next object and copy value to POD type
+ * Asserts value length is equal to POD type sizeof.
+ * Asserts Type tag and optional Class tag according to parameters.
+ * Copy value to POD type (struct, union, C-style array, std::array, etc.).
+ * @param out POD type reference where to copy object value
+ * @param type_tag ASN1_Tag enum to assert type on object read
+ * @param class_tag ASN1_Tag enum to assert class on object read (default: CONTEXT_SPECIFIC)
+ * @return this reference
+ */
+ template <typename T>
+ BER_Decoder& get_next_value(T &out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC)
+ {
+ static_assert(std::is_pod<T>::value, "Type must be POD");
+
+ BER_Object obj = get_next_object();
+ obj.assert_is_a(type_tag, class_tag);
+
+ if (obj.value.size() != sizeof(T))
+ throw BER_Decoding_Error(
+ "Size mismatch. Object value size is " +
+ std::to_string(obj.value.size()) +
+ "; Output type size is " +
+ std::to_string(sizeof(T)));
+
+ copy_mem(reinterpret_cast<uint8_t*>(&out), obj.value.data(), obj.value.size());
+
+ return (*this);
+ }
+
BER_Decoder& raw_bytes(secure_vector<uint8_t>& v);
BER_Decoder& raw_bytes(std::vector<uint8_t>& v);
diff --git a/src/lib/block/blowfish/blowfish.cpp b/src/lib/block/blowfish/blowfish.cpp
index 17ac00a1f..68d73cafd 100644
--- a/src/lib/block/blowfish/blowfish.cpp
+++ b/src/lib/block/blowfish/blowfish.cpp
@@ -291,27 +291,31 @@ void Blowfish::key_expansion(const uint8_t key[],
void Blowfish::eks_key_schedule(const uint8_t key[], size_t length,
const uint8_t salt[16], size_t workfactor)
{
- // Truncate longer passwords to the 56 byte limit Blowfish enforces
- length = std::min<size_t>(length, 55);
-
- if(workfactor == 0)
- throw Invalid_Argument("Bcrypt work factor must be at least 1");
/*
* On a 2.8 GHz Core-i7, workfactor == 18 takes about 25 seconds to
* hash a password. This seems like a reasonable upper bound for the
* time being.
+ * Bcrypt allows up to work factor 31 (2^31 iterations)
*/
if(workfactor > 18)
throw Invalid_Argument("Requested Bcrypt work factor " +
- std::to_string(workfactor) + " too large");
+ std::to_string(workfactor) + " too large");
+
+ if(workfactor < 4)
+ throw Invalid_Argument("Bcrypt requires work factor at least 4");
+
+ if(length > 72)
+ {
+ // Truncate longer passwords to the 72 char bcrypt limit
+ length = 72;
+ }
m_P.resize(18);
copy_mem(m_P.data(), P_INIT, 18);
m_S.resize(1024);
copy_mem(m_S.data(), S_INIT, 1024);
-
key_expansion(key, length, salt);
const uint8_t null_salt[16] = { 0 };
diff --git a/src/lib/entropy/entropy_src.h b/src/lib/entropy/entropy_src.h
index 94c67a18e..fe177657c 100644
--- a/src/lib/entropy/entropy_src.h
+++ b/src/lib/entropy/entropy_src.h
@@ -25,11 +25,11 @@ class RandomNumberGenerator;
class BOTAN_DLL Entropy_Source
{
public:
- /*
+ /**
* Return a new entropy source of a particular type, or null
* Each entropy source may require substantial resources (eg, a file handle
* or socket instance), so try to share them among multiple RNGs, or just
- * use the preconfigured global list accessed by global_entropy_sources()
+ * use the preconfigured global list accessed by Entropy_Sources::global_sources()
*/
static std::unique_ptr<Entropy_Source> create(const std::string& type);
@@ -41,7 +41,7 @@ class BOTAN_DLL Entropy_Source
/**
* Perform an entropy gathering poll
* @param rng will be provided with entropy via calls to add_entropy
- @ @return conservative estimate of actual entropy added to rng during poll
+ * @return conservative estimate of actual entropy added to rng during poll
*/
virtual size_t poll(RandomNumberGenerator& rng) = 0;
diff --git a/src/lib/entropy/entropy_srcs.cpp b/src/lib/entropy/entropy_srcs.cpp
index 252be4fa1..d9d5cfe4b 100644
--- a/src/lib/entropy/entropy_srcs.cpp
+++ b/src/lib/entropy/entropy_srcs.cpp
@@ -36,6 +36,10 @@
#include <botan/internal/darwin_secrandom.h>
#endif
+#if defined(BOTAN_HAS_ENTROPY_SRC_GETENTROPY)
+ #include <botan/internal/getentropy.h>
+#endif
+
namespace Botan {
std::unique_ptr<Entropy_Source> Entropy_Source::create(const std::string& name)
@@ -61,6 +65,13 @@ std::unique_ptr<Entropy_Source> Entropy_Source::create(const std::string& name)
#endif
}
+ if(name == "getentropy")
+ {
+#if defined(BOTAN_HAS_ENTROPY_SRC_GETENTROPY)
+ return std::unique_ptr<Entropy_Source>(new Getentropy);
+#endif
+ }
+
if(name == "dev_random")
{
#if defined(BOTAN_HAS_ENTROPY_SRC_DEV_RANDOM)
diff --git a/src/lib/entropy/getentropy/getentropy.cpp b/src/lib/entropy/getentropy/getentropy.cpp
new file mode 100644
index 000000000..56c356eba
--- /dev/null
+++ b/src/lib/entropy/getentropy/getentropy.cpp
@@ -0,0 +1,30 @@
+/*
+* System Call getentropy(2)
+* (C) 2017 Alexander Bluhm (genua GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/getentropy.h>
+
+#include <unistd.h>
+
+namespace Botan {
+
+/**
+* Gather BOTAN_SYSTEM_RNG_POLL_REQUEST bytes entropy from getentropy(2).
+* This is 64 bytes, note that maximum buffer size is limited to 256 bytes.
+*/
+size_t Getentropy::poll(RandomNumberGenerator& rng)
+ {
+ secure_vector<uint8_t> buf(BOTAN_SYSTEM_RNG_POLL_REQUEST);
+
+ if(::getentropy(buf.data(), buf.size()) == 0)
+ {
+ rng.add_entropy(buf.data(), buf.size());
+ return buf.size() * 8;
+ }
+
+ return 0;
+ }
+}
diff --git a/src/lib/entropy/getentropy/getentropy.h b/src/lib/entropy/getentropy/getentropy.h
new file mode 100644
index 000000000..ebc49320b
--- /dev/null
+++ b/src/lib/entropy/getentropy/getentropy.h
@@ -0,0 +1,28 @@
+/*
+* Entropy Source Using OpenBSD getentropy(2) system call
+* (C) 2017 Alexander Bluhm (genua GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENTROPY_SRC_GETENTROPY_H__
+#define BOTAN_ENTROPY_SRC_GETENTROPY_H__
+
+#include <botan/entropy_src.h>
+
+namespace Botan {
+
+/**
+* Entropy source using the getentropy(2) sustem call first introduced in
+* OpenBSD 5.6 and added to Solaris 11.3.
+*/
+class Getentropy final : public Entropy_Source
+ {
+ public:
+ std::string name() const override { return "getentropy"; }
+ size_t poll(RandomNumberGenerator& rng) override;
+ };
+
+}
+
+#endif
diff --git a/src/lib/entropy/getentropy/info.txt b/src/lib/entropy/getentropy/info.txt
new file mode 100644
index 000000000..89e7b120d
--- /dev/null
+++ b/src/lib/entropy/getentropy/info.txt
@@ -0,0 +1,9 @@
+define ENTROPY_SRC_GETENTROPY 20170327
+
+<header:internal>
+getentropy.h
+</header:internal>
+
+<os>
+openbsd
+</os>
diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp
index 07c20ce3b..8eb984c55 100644
--- a/src/lib/ffi/ffi.cpp
+++ b/src/lib/ffi/ffi.cpp
@@ -1,5 +1,5 @@
/*
-* (C) 2015 Jack Lloyd
+* (C) 2015,2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -17,11 +17,14 @@
#include <botan/x509cert.h>
#include <botan/data_src.h>
#include <botan/pubkey.h>
-#include <botan/data_src.h>
#include <botan/hex.h>
#include <botan/mem_ops.h>
#include <botan/x509_key.h>
#include <botan/pk_algs.h>
+#include <botan/bigint.h>
+#include <botan/reducer.h>
+#include <botan/numthry.h>
+#include <botan/divide.h>
#include <cstring>
#include <memory>
@@ -95,7 +98,7 @@ void log_exception(const char* func_name, const char* what)
int ffi_error_exception_thrown(const char* exn)
{
- printf("exception %s\n", exn);
+ fprintf(stderr, "exception %s\n", exn);
return BOTAN_FFI_ERROR_EXCEPTION_THROWN;
}
@@ -109,6 +112,16 @@ T& safe_get(botan_struct<T,M>* p)
throw FFI_Error("Invalid object pointer");
}
+template<typename T, uint32_t M>
+const T& safe_get(const botan_struct<T,M>* p)
+ {
+ if(!p)
+ throw FFI_Error("Null pointer argument");
+ if(const T* t = p->get())
+ return *t;
+ throw FFI_Error("Invalid object pointer");
+ }
+
template<typename T, uint32_t M, typename F>
int apply_fn(botan_struct<T, M>* o, const char* func_name, F func)
{
@@ -168,6 +181,13 @@ inline int write_str_output(char out[], size_t* out_len, const std::string& str)
return write_str_output(reinterpret_cast<uint8_t*>(out), out_len, str);
}
+inline int write_str_output(char out[], size_t* out_len, const std::vector<uint8_t>& str_vec)
+ {
+ return write_output(reinterpret_cast<uint8_t*>(out), out_len,
+ reinterpret_cast<const uint8_t*>(str_vec.data()),
+ str_vec.size());
+ }
+
#define BOTAN_FFI_DO(T, obj, param, block) apply_fn(obj, BOTAN_CURRENT_FUNCTION, [=](T& param) -> int { do { block } while(0); return 0; })
}
@@ -184,6 +204,7 @@ struct botan_cipher_struct : public botan_struct<Botan::Cipher_Mode, 0xB4A2BF9C>
};
BOTAN_FFI_DECLARE_STRUCT(botan_rng_struct, Botan::RandomNumberGenerator, 0x4901F9C1);
+BOTAN_FFI_DECLARE_STRUCT(botan_mp_struct, Botan::BigInt, 0xC828B9D2);
BOTAN_FFI_DECLARE_STRUCT(botan_hash_struct, Botan::HashFunction, 0x1F0A4F84);
BOTAN_FFI_DECLARE_STRUCT(botan_mac_struct, Botan::MessageAuthenticationCode, 0xA06E8FC1);
BOTAN_FFI_DECLARE_STRUCT(botan_pubkey_struct, Botan::Public_Key, 0x2C286519);
@@ -196,6 +217,7 @@ BOTAN_FFI_DECLARE_STRUCT(botan_pk_op_ka_struct, Botan::PK_Key_Agreement, 0x2939C
BOTAN_FFI_DECLARE_STRUCT(botan_x509_cert_struct, Botan::X509_Certificate, 0x8F628937);
+
#if defined(BOTAN_HAS_TLS)
BOTAN_FFI_DECLARE_STRUCT(botan_tls_channel_struct, Botan::TLS::Channel, 0x0212FE99);
#endif
@@ -302,6 +324,203 @@ int botan_rng_reseed(botan_rng_t rng, size_t bits)
return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { r.reseed_from_rng(Botan::system_rng(), bits); });
}
+int botan_mp_init(botan_mp_t* mp)
+ {
+ *mp = new botan_mp_struct(new Botan::BigInt);
+ return 0;
+ }
+
+int botan_mp_set_from_int(botan_mp_t mp, int initial_value)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, {
+ if(initial_value >= 0)
+ {
+ bn = Botan::BigInt(static_cast<uint64_t>(initial_value));
+ }
+ else
+ {
+ bn = Botan::BigInt(static_cast<uint64_t>(-initial_value));
+ bn.flip_sign();
+ }
+ });
+ }
+
+int botan_mp_set_from_str(botan_mp_t mp, const char* str)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn = Botan::BigInt(str); });
+ }
+
+int botan_mp_set_from_mp(botan_mp_t dest, botan_mp_t source)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, dest, bn, { bn = safe_get(source); });
+ }
+
+int botan_mp_is_negative(botan_mp_t mp)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { return bn.is_negative() ? 1 : 0; });
+ }
+
+int botan_mp_flip_sign(botan_mp_t mp)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.flip_sign(); });
+ }
+
+int botan_mp_from_bin(botan_mp_t mp, const uint8_t bin[], size_t bin_len)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.binary_decode(bin, bin_len); });
+ }
+
+int botan_mp_to_hex(botan_mp_t mp, char* out)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, {
+ std::vector<uint8_t> hex = Botan::BigInt::encode(bn, Botan::BigInt::Hexadecimal);
+ std::memcpy(out, hex.data(), hex.size());
+ out[hex.size()] = 0; // null terminate
+ });
+ }
+
+int botan_mp_to_str(botan_mp_t mp, uint8_t digit_base, char* out, size_t* out_len)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, {
+ Botan::BigInt::Base base;
+ if(digit_base == 0 || digit_base == 10)
+ base = Botan::BigInt::Decimal;
+ else if(digit_base == 16)
+ base = Botan::BigInt::Hexadecimal;
+ else
+ throw FFI_Error("botan_mp_to_str invalid digit base");
+
+ std::vector<uint8_t> hex = Botan::BigInt::encode(bn, base);
+ hex.push_back(0); // null terminator
+ write_str_output(out, out_len, hex);
+ });
+ }
+
+int botan_mp_to_bin(botan_mp_t mp, uint8_t vec[])
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.binary_encode(vec); });
+ }
+
+int botan_mp_destroy(botan_mp_t mp)
+ {
+ delete mp;
+ return 0;
+ }
+
+int botan_mp_add(botan_mp_t result, botan_mp_t x, botan_mp_t y)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, result, res, { res = safe_get(x) + safe_get(y); });
+ }
+
+int botan_mp_sub(botan_mp_t result, botan_mp_t x, botan_mp_t y)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, result, res, { res = safe_get(x) - safe_get(y); });
+ }
+
+int botan_mp_mul(botan_mp_t result, botan_mp_t x, botan_mp_t y)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, result, res, { res = safe_get(x) * safe_get(y); });
+ }
+
+int botan_mp_div(botan_mp_t quotient,
+ botan_mp_t remainder,
+ botan_mp_t x, botan_mp_t y)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, quotient, q, {
+ Botan::BigInt r;
+ Botan::divide(safe_get(x), safe_get(y), q, r);
+ safe_get(remainder) = r;
+ });
+ }
+
+int botan_mp_equal(botan_mp_t x_w, botan_mp_t y_w)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, x_w, x, { return x == safe_get(y_w); });
+ }
+
+int botan_mp_cmp(int* result, botan_mp_t x_w, botan_mp_t y_w)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, x_w, x, { *result = x.cmp(safe_get(y_w)); });
+ }
+
+int botan_mp_swap(botan_mp_t x_w, botan_mp_t y_w)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, x_w, x, { x.swap(safe_get(y_w)); });
+ }
+
+// Return (base^exponent) % modulus
+int botan_mp_powmod(botan_mp_t out, botan_mp_t base, botan_mp_t exponent, botan_mp_t modulus)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, out, o,
+ { o = Botan::power_mod(safe_get(base), safe_get(exponent), safe_get(modulus)); });
+ }
+
+int botan_mp_lshift(botan_mp_t out, botan_mp_t in, size_t shift)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, out, o, { o = safe_get(in) << shift; });
+ }
+
+int botan_mp_rshift(botan_mp_t out, botan_mp_t in, size_t shift)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, out, o, { o = safe_get(in) >> shift; });
+ }
+
+int botan_mp_mod_inverse(botan_mp_t out, botan_mp_t in, botan_mp_t modulus)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, out, o, { o = Botan::inverse_mod(safe_get(in), safe_get(modulus)); });
+ }
+
+int botan_mp_mod_mul(botan_mp_t out, botan_mp_t x, botan_mp_t y, botan_mp_t modulus)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, out, o, {
+ Botan::Modular_Reducer reducer(safe_get(modulus));
+ o = reducer.multiply(safe_get(x), safe_get(y));
+ });
+ }
+
+int botan_mp_rand_bits(botan_mp_t rand_out, botan_rng_t rng, size_t bits)
+ {
+ return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, {
+ safe_get(rand_out).randomize(r, bits); });
+ }
+
+int botan_mp_rand_range(botan_mp_t rand_out,
+ botan_rng_t rng,
+ botan_mp_t lower,
+ botan_mp_t upper)
+ {
+ return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, {
+ safe_get(rand_out) = Botan::BigInt::random_integer(r, safe_get(lower), safe_get(upper)); });
+ }
+
+int botan_mp_gcd(botan_mp_t out, botan_mp_t x, botan_mp_t y)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, out, o, {
+ o = Botan::gcd(safe_get(x), safe_get(y)); });
+ }
+
+int botan_mp_is_prime(botan_mp_t mp, botan_rng_t rng, size_t test_prob)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, n,
+ { return (Botan::is_prime(n, safe_get(rng), test_prob)) ? 1 : 0; });
+ }
+
+int botan_mp_bit_set(botan_mp_t mp, size_t bit)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, n, { return (n.get_bit(bit)); });
+ }
+
+int botan_mp_num_bits(botan_mp_t mp, size_t* bits)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, n, { *bits = n.bits(); });
+ }
+
+int botan_mp_num_bytes(botan_mp_t mp, size_t* bytes)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, n, { *bytes = n.bytes(); });
+ }
+
+
int botan_hash_init(botan_hash_t* hash, const char* hash_name, uint32_t flags)
{
try
@@ -802,7 +1021,7 @@ int botan_privkey_create_rsa(botan_privkey_t* key_obj, botan_rng_t rng_obj, size
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)
@@ -920,6 +1139,163 @@ int botan_privkey_load(botan_privkey_t* key, botan_rng_t rng_obj,
return -1;
}
+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)
+ try
+ {
+ *key = new botan_privkey_struct(new Botan::RSA_PrivateKey(safe_get(p),
+ safe_get(q),
+ safe_get(d)));
+ return 0;
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+ return -1;
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_pubkey_load_rsa(botan_pubkey_t* key,
+ botan_mp_t n, botan_mp_t e)
+ {
+ *key = nullptr;
+
+#if defined(BOTAN_HAS_RSA)
+ try
+ {
+ *key = new botan_pubkey_struct(new Botan::RSA_PublicKey(safe_get(n), safe_get(e)));
+ return 0;
+ }
+ catch(std::exception& exn)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, exn.what());
+ }
+
+ return -1;
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_privkey_rsa_get_p(botan_mp_t p, botan_privkey_t key)
+ {
+#if defined(BOTAN_HAS_RSA)
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+ if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<Botan::RSA_PrivateKey*>(&k))
+ {
+ safe_get(p) = rsa->get_p();
+ }
+ else
+ throw FFI_Error("Passed non-RSA key to botan_privkey_rsa_get_p");
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_privkey_rsa_get_q(botan_mp_t q, botan_privkey_t key)
+ {
+#if defined(BOTAN_HAS_RSA)
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+ if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<Botan::RSA_PrivateKey*>(&k))
+ {
+ safe_get(q) = rsa->get_q();
+ }
+ else
+ throw FFI_Error("Passed non-RSA key to botan_privkey_rsa_get_q");
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_privkey_rsa_get_n(botan_mp_t n, botan_privkey_t key)
+ {
+#if defined(BOTAN_HAS_RSA)
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+ if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<Botan::RSA_PrivateKey*>(&k))
+ {
+ safe_get(n) = rsa->get_n();
+ }
+ else
+ throw FFI_Error("Passed non-RSA key to botan_privkey_rsa_get_n");
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_privkey_rsa_get_e(botan_mp_t e, botan_privkey_t key)
+ {
+#if defined(BOTAN_HAS_RSA)
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+ if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<Botan::RSA_PrivateKey*>(&k))
+ {
+ safe_get(e) = rsa->get_e();
+ }
+ else
+ throw FFI_Error("Passed non-RSA key to botan_privkey_rsa_get_e");
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_privkey_rsa_get_d(botan_mp_t d, botan_privkey_t key)
+ {
+#if defined(BOTAN_HAS_RSA)
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+ if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<Botan::RSA_PrivateKey*>(&k))
+ {
+ safe_get(d) = rsa->get_e();
+ }
+ else
+ throw FFI_Error("Passed non-RSA key to botan_privkey_rsa_get_d");
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_pubkey_rsa_get_e(botan_mp_t e, botan_pubkey_t key)
+ {
+#if defined(BOTAN_HAS_RSA)
+ return BOTAN_FFI_DO(Botan::Public_Key, key, k, {
+ if(const Botan::RSA_PublicKey* rsa = dynamic_cast<Botan::RSA_PublicKey*>(&k))
+ {
+ safe_get(e) = rsa->get_e();
+ }
+ else
+ throw FFI_Error("Passed non-RSA key to botan_pubkey_rsa_get_e");
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_pubkey_rsa_get_n(botan_mp_t n, botan_pubkey_t key)
+ {
+#if defined(BOTAN_HAS_RSA)
+ return BOTAN_FFI_DO(Botan::Public_Key, key, k, {
+ if(const Botan::RSA_PublicKey* rsa = dynamic_cast<Botan::RSA_PublicKey*>(&k))
+ {
+ safe_get(n) = rsa->get_n();
+ }
+ else
+ throw FFI_Error("Passed non-RSA key to botan_pubkey_rsa_get_n");
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
int botan_privkey_destroy(botan_privkey_t key)
{
delete key;
@@ -955,6 +1331,21 @@ int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len)
return BOTAN_FFI_DO(Botan::Public_Key, key, k, { return write_str_output(out, out_len, k.algo_name()); });
}
+int botan_pubkey_check_key(botan_pubkey_t key, botan_rng_t rng, uint32_t flags)
+ {
+ const bool strong = (flags & BOTAN_CHECK_KEY_EXPENSIVE_TESTS);
+
+ return BOTAN_FFI_DO(Botan::Public_Key, key, k,
+ { return (k.check_key(safe_get(rng), strong) == true) ? 0 : -1; });
+ }
+
+int botan_privkey_check_key(botan_privkey_t key, botan_rng_t rng, uint32_t flags)
+ {
+ const bool strong = (flags & BOTAN_CHECK_KEY_EXPENSIVE_TESTS);
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k,
+ { return (k.check_key(safe_get(rng), strong) == true) ? 0 : -1; });
+ }
+
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, k, {
diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h
index 98792d4bd..661368c18 100644
--- a/src/lib/ffi/ffi.h
+++ b/src/lib/ffi/ffi.h
@@ -1,6 +1,6 @@
/*
* FFI (C89 API)
-* (C) 2015 Jack Lloyd
+* (C) 2015,2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -158,6 +158,7 @@ BOTAN_DLL int botan_same_mem(const uint8_t* x, const uint8_t* y, size_t len);
* @return 0 on success, 1 on failure
*/
BOTAN_DLL int botan_hex_encode(const uint8_t* x, size_t len, char* out, uint32_t flags);
+
// TODO: botan_hex_decode
// TODO: botan_base64_encode
// TODO: botan_base64_decode
@@ -171,7 +172,8 @@ typedef struct botan_rng_struct* botan_rng_t;
* Initialize a random number generator object
* @param rng rng object
* @param rng_type type of the rng, possible values:
-* "system" or nullptr: System_RNG, "user": AutoSeeded_RNG
+* "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?
*/
@@ -182,7 +184,7 @@ BOTAN_DLL int botan_rng_init(botan_rng_t* rng, const char* rng_type);
* @param rng rng object
* @param out output buffer of size out_len
* @param out_len number of requested bytes
-* @return 0 on success, -1 on failure
+* @return 0 on success, negative on failure
*
* TODO: better name
*/
@@ -279,7 +281,7 @@ typedef struct botan_mac_struct* botan_mac_t;
* @param mac mac object
* @param mac_name name of the hash function, e.g., "HMAC(SHA-384)"
* @param flags should be 0 in current API revision, all other uses are reserved
-* and return -1
+* and return a negative value (error code)
* @return 0 on success, a negative value on failure
*/
BOTAN_DLL int botan_mac_init(botan_mac_t* mac, const char* mac_name, uint32_t flags);
@@ -453,6 +455,92 @@ BOTAN_DLL int botan_bcrypt_generate(uint8_t* out, size_t* out_len,
size_t work_factor,
uint32_t flags);
+/*
+* Multiple precision integers
+*/
+typedef struct botan_mp_struct* botan_mp_t;
+
+BOTAN_DLL int botan_mp_init(botan_mp_t* mp);
+BOTAN_DLL int botan_mp_destroy(botan_mp_t mp);
+
+// writes botan_mp_num_bytes(mp)*2 + 1 bytes to out[]
+BOTAN_DLL int botan_mp_to_hex(botan_mp_t mp, char* out);
+BOTAN_DLL int botan_mp_to_str(botan_mp_t mp, uint8_t base, char* out, size_t* out_len);
+
+BOTAN_DLL int botan_mp_set_from_int(botan_mp_t mp, int initial_value);
+BOTAN_DLL int botan_mp_set_from_mp(botan_mp_t dest, botan_mp_t source);
+BOTAN_DLL int botan_mp_set_from_str(botan_mp_t dest, const char* str);
+
+BOTAN_DLL int botan_mp_num_bits(botan_mp_t n, size_t* bits);
+BOTAN_DLL int botan_mp_num_bytes(botan_mp_t n, size_t* bytes);
+
+// Writes botan_mp_num_bytes(mp) to vec
+BOTAN_DLL int botan_mp_to_bin(botan_mp_t mp, uint8_t vec[]);
+BOTAN_DLL int botan_mp_from_bin(botan_mp_t mp, const uint8_t vec[], size_t vec_len);
+
+BOTAN_DLL int botan_mp_is_negative(botan_mp_t mp);
+BOTAN_DLL int botan_mp_flip_sign(botan_mp_t mp);
+
+BOTAN_DLL int botan_mp_add(botan_mp_t result, botan_mp_t x, botan_mp_t y);
+BOTAN_DLL int botan_mp_sub(botan_mp_t result, botan_mp_t x, botan_mp_t y);
+BOTAN_DLL int botan_mp_mul(botan_mp_t result, botan_mp_t x, botan_mp_t y);
+
+BOTAN_DLL int botan_mp_div(botan_mp_t quotient,
+ botan_mp_t remainder,
+ botan_mp_t x, botan_mp_t y);
+
+BOTAN_DLL int botan_mp_mod_mul(botan_mp_t result, botan_mp_t x, botan_mp_t y, botan_mp_t mod);
+
+/*
+* Returns 0 if x != y
+* Returns 1 if x == y
+* Returns negative number on error
+*/
+BOTAN_DLL int botan_mp_equal(botan_mp_t x, botan_mp_t y);
+
+/*
+* Sets *result to comparison result:
+* -1 if x < y, 0 if x == y, 1 if x > y
+* Returns negative number on error or zero on success
+*/
+BOTAN_DLL int botan_mp_cmp(int* result, botan_mp_t x, botan_mp_t y);
+
+/*
+* Swap two botan_mp_t
+*/
+BOTAN_DLL int botan_mp_swap(botan_mp_t x, botan_mp_t y);
+
+// Return (base^exponent) % modulus
+BOTAN_DLL int botan_mp_powmod(botan_mp_t out, botan_mp_t base, botan_mp_t exponent, botan_mp_t modulus);
+
+BOTAN_DLL int botan_mp_lshift(botan_mp_t out, botan_mp_t in, size_t shift);
+BOTAN_DLL int botan_mp_rshift(botan_mp_t out, botan_mp_t in, size_t shift);
+
+BOTAN_DLL int botan_mp_mod_inverse(botan_mp_t out, botan_mp_t in, botan_mp_t modulus);
+
+BOTAN_DLL int botan_mp_rand_bits(botan_mp_t rand_out, botan_rng_t rng, size_t bits);
+
+BOTAN_DLL int botan_mp_rand_range(botan_mp_t rand_out, botan_rng_t rng,
+ botan_mp_t lower_bound, botan_mp_t upper_bound);
+
+BOTAN_DLL int botan_mp_gcd(botan_mp_t out, botan_mp_t x, botan_mp_t y);
+
+/**
+* Returns 0 if n is not prime
+* Returns 1 if n is prime
+* Returns negative number on error
+*/
+BOTAN_DLL int botan_mp_is_prime(botan_mp_t n, botan_rng_t rng, size_t test_prob);
+
+/**
+* Returns 0 if specified bit of n is not set
+* Returns 1 if specified bit of n is set
+* Returns negative number on error
+*/
+BOTAN_DLL int botan_mp_bit_set(botan_mp_t n, size_t bit);
+
+/* Bcrypt password hashing */
+
/**
* Check a previously created password hash
* @param pass the password to check against
@@ -473,12 +561,15 @@ BOTAN_DLL int botan_privkey_create(botan_privkey_t* key,
const char* algo_params,
botan_rng_t rng);
+#define BOTAN_CHECK_KEY_EXPENSIVE_TESTS 1
+
+BOTAN_DLL int botan_privkey_check_key(botan_privkey_t key, botan_rng_t rng, uint32_t flags);
+
BOTAN_DLL int botan_privkey_create_rsa(botan_privkey_t* key, botan_rng_t rng, size_t n_bits);
BOTAN_DLL int botan_privkey_create_ecdsa(botan_privkey_t* key, botan_rng_t rng, const char* params);
BOTAN_DLL int botan_privkey_create_ecdh(botan_privkey_t* key, botan_rng_t rng, const char* params);
BOTAN_DLL int botan_privkey_create_mceliece(botan_privkey_t* key, botan_rng_t rng, size_t n, size_t t);
-
/*
* Input currently assumed to be PKCS #8 structure;
* Set password to NULL to indicate no encryption expected
@@ -550,6 +641,11 @@ BOTAN_DLL int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out
BOTAN_DLL int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len);
+/**
+* Returns 0 if key is valid, negative if invalid key or some other error
+*/
+BOTAN_DLL int botan_pubkey_check_key(botan_pubkey_t key, botan_rng_t rng, uint32_t flags);
+
BOTAN_DLL int botan_pubkey_estimated_strength(botan_pubkey_t key, size_t* estimate);
BOTAN_DLL int botan_pubkey_fingerprint(botan_pubkey_t key, const char* hash,
@@ -559,6 +655,27 @@ BOTAN_DLL int botan_pubkey_destroy(botan_pubkey_t key);
/*
+* Algorithm specific key operations: RSA
+*/
+BOTAN_DLL int botan_privkey_load_rsa(botan_privkey_t* key,
+ botan_mp_t p,
+ botan_mp_t q,
+ botan_mp_t d);
+
+BOTAN_DLL int botan_privkey_rsa_get_p(botan_mp_t p, botan_privkey_t rsa_key);
+BOTAN_DLL int botan_privkey_rsa_get_q(botan_mp_t q, botan_privkey_t rsa_key);
+BOTAN_DLL int botan_privkey_rsa_get_d(botan_mp_t d, botan_privkey_t rsa_key);
+BOTAN_DLL int botan_privkey_rsa_get_n(botan_mp_t n, botan_privkey_t rsa_key);
+BOTAN_DLL int botan_privkey_rsa_get_e(botan_mp_t e, botan_privkey_t rsa_key);
+
+BOTAN_DLL int botan_pubkey_load_rsa(botan_pubkey_t* key,
+ botan_mp_t n,
+ botan_mp_t e);
+
+BOTAN_DLL int botan_pubkey_rsa_get_e(botan_mp_t e, botan_pubkey_t rsa_key);
+BOTAN_DLL int botan_pubkey_rsa_get_n(botan_mp_t n, botan_pubkey_t rsa_key);
+
+/*
* Public Key Encryption
*/
typedef struct botan_pk_op_encrypt_struct* botan_pk_op_encrypt_t;
diff --git a/src/lib/ffi/info.txt b/src/lib/ffi/info.txt
index 7b3068274..e1d29981f 100644
--- a/src/lib/ffi/info.txt
+++ b/src/lib/ffi/info.txt
@@ -1,10 +1,11 @@
-define FFI 20151015
+define FFI 20170327
<requires>
aead
kdf
pbkdf
pubkey
+bigint
x509
#tls
system_rng
diff --git a/src/lib/filters/threaded_fork.cpp b/src/lib/filters/threaded_fork.cpp
index 170264353..d379eacda 100644
--- a/src/lib/filters/threaded_fork.cpp
+++ b/src/lib/filters/threaded_fork.cpp
@@ -12,6 +12,7 @@
#include <botan/internal/semaphore.h>
#include <botan/internal/barrier.h>
+#include <functional>
namespace Botan {
diff --git a/src/lib/math/ec_gfp/point_gfp.cpp b/src/lib/math/ec_gfp/point_gfp.cpp
index bb446566e..5283b7352 100644
--- a/src/lib/math/ec_gfp/point_gfp.cpp
+++ b/src/lib/math/ec_gfp/point_gfp.cpp
@@ -314,8 +314,6 @@ Blinded_Point_Multiply::Blinded_Point_Multiply(const PointGFp& base, const BigIn
const CurveGFp& curve = base.get_curve();
-#if BOTAN_POINTGFP_BLINDED_MULTIPLY_USE_MONTGOMERY_LADDER
-
const PointGFp inv = -base;
m_U.resize(6*m_h + 3);
@@ -332,17 +330,6 @@ Blinded_Point_Multiply::Blinded_Point_Multiply(const PointGFp& base, const BigIn
m_U[3*m_h+1-i] = m_U[3*m_h+2-i];
m_U[3*m_h+1-i].add(inv, m_ws);
}
-#else
- m_U.resize(1 << m_h);
- m_U[0] = PointGFp::zero_of(curve);
- m_U[1] = base;
-
- for(size_t i = 2; i < m_U.size(); ++i)
- {
- m_U[i] = m_U[i-1];
- m_U[i].add(base, m_ws);
- }
-#endif
}
PointGFp Blinded_Point_Multiply::blinded_multiply(const BigInt& scalar_in,
@@ -351,9 +338,9 @@ PointGFp Blinded_Point_Multiply::blinded_multiply(const BigInt& scalar_in,
if(scalar_in.is_negative())
throw Invalid_Argument("Blinded_Point_Multiply scalar must be positive");
-#if BOTAN_POINTGFP_SCALAR_BLINDING_BITS > 0
+#if BOTAN_POINTGFP_USE_SCALAR_BLINDING
// Choose a small mask m and use k' = k + m*order (Coron's 1st countermeasure)
- const BigInt mask(rng, BOTAN_POINTGFP_SCALAR_BLINDING_BITS, false);
+ const BigInt mask(rng, (m_order.bits()+1)/2, false);
const BigInt scalar = scalar_in + m_order * mask;
#else
const BigInt& scalar = scalar_in;
@@ -365,7 +352,6 @@ PointGFp Blinded_Point_Multiply::blinded_multiply(const BigInt& scalar_in,
for(size_t i = 0; i != m_U.size(); ++i)
m_U[i].randomize_repr(rng);
-#if BOTAN_POINTGFP_BLINDED_MULTIPLY_USE_MONTGOMERY_LADDER
PointGFp R = m_U.at(3*m_h + 2); // base point
int32_t alpha = 0;
@@ -395,38 +381,6 @@ PointGFp Blinded_Point_Multiply::blinded_multiply(const BigInt& scalar_in,
const int32_t k0 = scalar.get_bit(0);
R.add(m_U[3*m_h + 1 - alpha - (k0 ^ 1)], m_ws);
-#else
-
- // N-bit windowing exponentiation:
-
- size_t windows = round_up(scalar_bits, m_h) / m_h;
-
- PointGFp R = m_U[0];
-
- if(windows > 0)
- {
- windows--;
- const uint32_t nibble = scalar.get_substring(windows*m_h, m_h);
- R.add(m_U[nibble], m_ws);
-
- /*
- Randomize after adding the first nibble as before the addition R
- is zero, and we cannot effectively randomize the point
- representation of the zero point.
- */
- R.randomize_repr(rng);
-
- while(windows)
- {
- for(size_t i = 0; i != m_h; ++i)
- R.mult2(m_ws);
-
- const uint32_t inner_nibble = scalar.get_substring((windows-1)*m_h, m_h);
- R.add(m_U[inner_nibble], m_ws);
- windows--;
- }
- }
-#endif
//BOTAN_ASSERT(R.on_the_curve(), "Output is on the curve");
diff --git a/src/lib/math/mp/mp_madd.h b/src/lib/math/mp/mp_madd.h
index 2fa1d88ce..e72c16ecb 100644
--- a/src/lib/math/mp/mp_madd.h
+++ b/src/lib/math/mp/mp_madd.h
@@ -40,7 +40,7 @@ namespace Botan {
#if defined(BOTAN_USE_GCC_INLINE_ASM)
#define BOTAN_MP_USE_X86_32_ASM
#define ASM(x) x "\n\t"
- #elif defined(BOTAN_TARGET_COMPILER_IS_MSVC)
+ #elif defined(BOTAN_BUILD_COMPILER_IS_MSVC)
#define BOTAN_MP_USE_X86_32_MSVC_ASM
#endif
diff --git a/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp b/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
index c4cdc6f35..ebe6f5fa7 100644
--- a/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
+++ b/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
@@ -27,7 +27,13 @@ secure_vector<uint8_t> emsa3_encoding(const secure_vector<uint8_t>& msg,
T[0] = 0x01;
set_mem(&T[1], P_LENGTH, 0xFF);
T[P_LENGTH+1] = 0x00;
- buffer_insert(T, P_LENGTH+2, hash_id, hash_id_length);
+
+ if(hash_id_length > 0)
+ {
+ BOTAN_ASSERT_NONNULL(hash_id);
+ buffer_insert(T, P_LENGTH+2, hash_id, hash_id_length);
+ }
+
buffer_insert(T, output_length-msg.size(), msg.data(), msg.size());
return T;
}
diff --git a/src/lib/pubkey/ec_group/ec_group.cpp b/src/lib/pubkey/ec_group/ec_group.cpp
index cbc628195..e8a9672ab 100644
--- a/src/lib/pubkey/ec_group/ec_group.cpp
+++ b/src/lib/pubkey/ec_group/ec_group.cpp
@@ -12,6 +12,7 @@
#include <botan/der_enc.h>
#include <botan/oids.h>
#include <botan/pem.h>
+#include <botan/reducer.h>
namespace Botan {
@@ -130,4 +131,44 @@ std::string EC_Group::PEM_encode() const
return PEM_Code::encode(der, "EC PARAMETERS");
}
+bool EC_Group::verify_group(RandomNumberGenerator& rng,
+ bool) const
+ {
+ //compute the discriminant
+ Modular_Reducer p(m_curve.get_p());
+ BigInt discriminant = p.multiply(4, m_curve.get_a());
+ discriminant += p.multiply(27, m_curve.get_b());
+ discriminant = p.reduce(discriminant);
+ //check the discriminant
+ if(discriminant == 0)
+ {
+ return false;
+ }
+ //check for valid cofactor
+ if(m_cofactor < 1)
+ {
+ return false;
+ }
+ //check if the base point is on the curve
+ if(!m_base_point.on_the_curve())
+ {
+ return false;
+ }
+ if((m_base_point * m_cofactor).is_zero())
+ {
+ return false;
+ }
+ //check if order is prime
+ if(!is_prime(m_order, rng, 128))
+ {
+ return false;
+ }
+ //check if order of the base point is correct
+ if(!(m_base_point * m_order).is_zero())
+ {
+ return false;
+ }
+ return true;
+ }
+
}
diff --git a/src/lib/pubkey/ec_group/ec_group.h b/src/lib/pubkey/ec_group/ec_group.h
index 15c09a54d..a2cd4d719 100644
--- a/src/lib/pubkey/ec_group/ec_group.h
+++ b/src/lib/pubkey/ec_group/ec_group.h
@@ -113,7 +113,14 @@ class BOTAN_DLL EC_Group
* @result the OID
*/
std::string get_oid() const { return m_oid; }
-
+
+ /**
+ * Verify EC_Group domain
+ * @returns true if group is valid. false otherwise
+ */
+ bool verify_group(RandomNumberGenerator& rng,
+ bool strong = false) const;
+
bool operator==(const EC_Group& other) const
{
return ((get_curve() == other.get_curve()) &&
diff --git a/src/lib/pubkey/ecc_key/ecc_key.cpp b/src/lib/pubkey/ecc_key/ecc_key.cpp
index cb0af42eb..0c59c75fd 100644
--- a/src/lib/pubkey/ecc_key/ecc_key.cpp
+++ b/src/lib/pubkey/ecc_key/ecc_key.cpp
@@ -44,12 +44,40 @@ EC_PublicKey::EC_PublicKey(const AlgorithmIdentifier& alg_id,
m_domain_encoding{EC_DOMPAR_ENC_EXPLICIT}
{}
-bool EC_PublicKey::check_key(RandomNumberGenerator&,
+bool EC_PublicKey::check_key(RandomNumberGenerator& rng,
bool) const
{
- return public_point().on_the_curve();
+ //verify domain parameters
+ if(!m_domain_params.verify_group(rng))
+ {
+ return false;
+ }
+ //check that public point is not at infinity
+ if(public_point().is_zero())
+ {
+ return false;
+ }
+ //check that public point is on the curve
+ if(!public_point().on_the_curve())
+ {
+ return false;
+ }
+ if(m_domain_params.get_cofactor() > 1)
+ {
+ if((public_point() * m_domain_params.get_cofactor()).is_zero())
+ {
+ return false;
+ }
+ //check that public point has order q
+ if(!(public_point() * m_domain_params.get_order()).is_zero())
+ {
+ return false;
+ }
+ }
+ return true;
}
+
AlgorithmIdentifier EC_PublicKey::algorithm_identifier() const
{
return AlgorithmIdentifier(get_oid(), DER_domain());
diff --git a/src/lib/utils/calendar.cpp b/src/lib/utils/calendar.cpp
index 4f6c835d1..ddd424e0e 100644
--- a/src/lib/utils/calendar.cpp
+++ b/src/lib/utils/calendar.cpp
@@ -121,10 +121,11 @@ std::chrono::system_clock::time_point calendar_point::to_std_timepoint() const
// 32 bit time_t ends at January 19, 2038
// https://msdn.microsoft.com/en-us/library/2093ets1.aspx
- // For consistency reasons, throw after 2037 as long as
- // no other implementation is available.
- if (year > 2037)
+ // Throw after 2037 if 32 bit time_t is used
+ if (year > 2037 && sizeof(std::time_t) == 4)
+ {
throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2037.");
+ }
// std::tm: struct without any timezone information
std::tm tm;
diff --git a/src/lib/utils/compiler.h b/src/lib/utils/compiler.h
index ed8fad9de..898412501 100644
--- a/src/lib/utils/compiler.h
+++ b/src/lib/utils/compiler.h
@@ -140,8 +140,8 @@
#define BOTAN_PARALLEL_SIMD_FOR _Pragma("simd") for
#elif defined(BOTAN_TARGET_HAS_OPENMP)
#define BOTAN_PARALLEL_SIMD_FOR _Pragma("omp simd") for
-#elif defined(BOTAN_TARGET_COMPILER_IS_GCC)
- #define BOTAN_PARALLEL_FOR _Pragma("GCC ivdep") for
+#elif defined(BOTAN_BUILD_COMPILER_IS_GCC)
+ #define BOTAN_PARALLEL_SIMD_FOR _Pragma("GCC ivdep") for
#else
#define BOTAN_PARALLEL_SIMD_FOR for
#endif
diff --git a/src/lib/utils/os_utils.cpp b/src/lib/utils/os_utils.cpp
index fc401c3c1..7bd9b842d 100644
--- a/src/lib/utils/os_utils.cpp
+++ b/src/lib/utils/os_utils.cpp
@@ -345,7 +345,7 @@ void botan_sigill_handler(int)
int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn)
{
- int probe_result = -3;
+ volatile int probe_result = -3;
#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
struct sigaction old_sigaction;
@@ -360,26 +360,17 @@ int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn)
if(rc != 0)
throw Exception("run_cpu_instruction_probe sigaction failed");
- try
- {
- rc = ::sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);
+ rc = ::sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);
- if(rc == 0)
- {
- // first call to sigsetjmp
- probe_result = probe_fn();
- }
- else if(rc == 1)
- {
- // non-local return from siglongjmp in signal handler: return error
- probe_result = -1;
- }
- else
- throw Exception("run_cpu_instruction_probe unexpected sigsetjmp return value");
+ if(rc == 0)
+ {
+ // first call to sigsetjmp
+ probe_result = probe_fn();
}
- catch(...)
+ else if(rc == 1)
{
- probe_result = -2;
+ // non-local return from siglongjmp in signal handler: return error
+ probe_result = -1;
}
// Restore old SIGILL handler, if any
@@ -392,14 +383,7 @@ int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn)
// Windows SEH
__try
{
- try
- {
- probe_result = probe_fn();
- }
- catch(...)
- {
- probe_result = -2;
- }
+ probe_result = probe_fn();
}
__except(::GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
diff --git a/src/lib/utils/os_utils.h b/src/lib/utils/os_utils.h
index a1693bcc5..cae1192f1 100644
--- a/src/lib/utils/os_utils.h
+++ b/src/lib/utils/os_utils.h
@@ -98,9 +98,10 @@ void free_locked_pages(void* ptr, size_t length);
* thread safe. It should only be called in a single-threaded context
* (ie, at static init time).
*
+* If probe_fn throws an exception the result is undefined.
+*
* Return codes:
* -1 illegal instruction detected
-* -2 exception thrown
*/
int BOTAN_DLL run_cpu_instruction_probe(std::function<int ()> probe_fn);
diff --git a/src/lib/x509/ocsp.cpp b/src/lib/x509/ocsp.cpp
index 964299f64..be8e6ed1d 100644
--- a/src/lib/x509/ocsp.cpp
+++ b/src/lib/x509/ocsp.cpp
@@ -53,13 +53,19 @@ void decode_optional_list(BER_Decoder& ber,
Request::Request(const X509_Certificate& issuer_cert,
const X509_Certificate& subject_cert) :
m_issuer(issuer_cert),
- m_subject(subject_cert),
- m_certid(m_issuer, m_subject)
+ m_certid(m_issuer, BigInt::decode(subject_cert.serial_number()))
{
if(subject_cert.issuer_dn() != issuer_cert.subject_dn())
throw Invalid_Argument("Invalid cert pair to OCSP::Request (mismatched issuer,subject args?)");
}
+Request::Request(const X509_Certificate& issuer_cert,
+ const BigInt& subject_serial) :
+ m_issuer(issuer_cert),
+ m_certid(m_issuer, subject_serial)
+ {
+ }
+
std::vector<uint8_t> Request::BER_encode() const
{
return DER_Encoder().start_cons(SEQUENCE)
@@ -275,17 +281,16 @@ Certificate_Status_Code Response::status_for(const X509_Certificate& issuer,
#if defined(BOTAN_HAS_HTTP_UTIL)
Response online_check(const X509_Certificate& issuer,
- const X509_Certificate& subject,
+ const BigInt& subject_serial,
+ const std::string& ocsp_responder,
Certificate_Store* trusted_roots)
{
- const std::string responder_url = subject.ocsp_responder();
+ if(ocsp_responder.empty())
+ throw Invalid_Argument("No OCSP responder specified");
- if(responder_url.empty())
- throw Exception("No OCSP responder specified");
+ OCSP::Request req(issuer, subject_serial);
- OCSP::Request req(issuer, subject);
-
- auto http = HTTP::POST_sync(responder_url,
+ auto http = HTTP::POST_sync(ocsp_responder,
"application/ocsp-request",
req.BER_encode());
@@ -304,6 +309,20 @@ Response online_check(const X509_Certificate& issuer,
return response;
}
+
+Response online_check(const X509_Certificate& issuer,
+ const X509_Certificate& subject,
+ Certificate_Store* trusted_roots)
+ {
+ if(subject.issuer_dn() != issuer.subject_dn())
+ throw Invalid_Argument("Invalid cert pair to OCSP::online_check (mismatched issuer,subject args?)");
+
+ return online_check(issuer,
+ BigInt::decode(subject.serial_number()),
+ subject.ocsp_responder(),
+ trusted_roots);
+ }
+
#endif
}
diff --git a/src/lib/x509/ocsp.h b/src/lib/x509/ocsp.h
index ff6a19567..881eee124 100644
--- a/src/lib/x509/ocsp.h
+++ b/src/lib/x509/ocsp.h
@@ -31,6 +31,9 @@ class BOTAN_DLL Request
Request(const X509_Certificate& issuer_cert,
const X509_Certificate& subject_cert);
+ Request(const X509_Certificate& issuer_cert,
+ const BigInt& subject_serial);
+
/**
* @return BER-encoded OCSP request
*/
@@ -49,12 +52,12 @@ class BOTAN_DLL Request
/**
* @return subject certificate
*/
- const X509_Certificate& subject() const { return m_subject; }
+ const X509_Certificate& subject() const { throw Not_Implemented("Method have been deprecated"); }
const std::vector<uint8_t>& issuer_key_hash() const
{ return m_certid.issuer_key_hash(); }
private:
- X509_Certificate m_issuer, m_subject;
+ X509_Certificate m_issuer;
CertID m_certid;
};
@@ -155,6 +158,11 @@ class BOTAN_DLL Response
#if defined(BOTAN_HAS_HTTP_UTIL)
+BOTAN_DLL Response online_check(const X509_Certificate& issuer,
+ const BigInt& subject_serial,
+ const std::string& ocsp_responder,
+ Certificate_Store* trusted_roots);
+
/**
* Makes an online OCSP request via HTTP and returns the OCSP response.
* @param issuer issuer certificate
diff --git a/src/lib/x509/ocsp_types.cpp b/src/lib/x509/ocsp_types.cpp
index c9d349a4b..470acffa6 100644
--- a/src/lib/x509/ocsp_types.cpp
+++ b/src/lib/x509/ocsp_types.cpp
@@ -17,7 +17,7 @@ namespace Botan {
namespace OCSP {
CertID::CertID(const X509_Certificate& issuer,
- const X509_Certificate& subject)
+ const BigInt& subject_serial)
{
/*
In practice it seems some responders, including, notably,
@@ -27,8 +27,8 @@ CertID::CertID(const X509_Certificate& issuer,
m_hash_id = AlgorithmIdentifier(hash->name(), AlgorithmIdentifier::USE_NULL_PARAM);
m_issuer_key_hash = unlock(hash->process(issuer.subject_public_key_bitstring()));
- m_issuer_dn_hash = unlock(hash->process(subject.raw_issuer_dn()));
- m_subject_serial = BigInt::decode(subject.serial_number());
+ m_issuer_dn_hash = unlock(hash->process(issuer.raw_subject_dn()));
+ m_subject_serial = subject_serial;
}
bool CertID::is_id_for(const X509_Certificate& issuer,
diff --git a/src/lib/x509/ocsp_types.h b/src/lib/x509/ocsp_types.h
index 1cbf207b8..be7ae716a 100644
--- a/src/lib/x509/ocsp_types.h
+++ b/src/lib/x509/ocsp_types.h
@@ -22,7 +22,7 @@ class BOTAN_DLL CertID final : public ASN1_Object
CertID() {}
CertID(const X509_Certificate& issuer,
- const X509_Certificate& subject);
+ const BigInt& subject_serial);
bool is_id_for(const X509_Certificate& issuer,
const X509_Certificate& subject) const;
diff --git a/src/lib/x509/x509path.cpp b/src/lib/x509/x509path.cpp
index c70ecae7a..eeb75b279 100644
--- a/src/lib/x509/x509path.cpp
+++ b/src/lib/x509/x509path.cpp
@@ -275,7 +275,7 @@ PKIX::check_ocsp_online(const std::vector<std::shared_ptr<const X509_Certificate
else
{
ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const OCSP::Response> {
- OCSP::Request req(*issuer, *subject);
+ OCSP::Request req(*issuer, BigInt::decode(subject->serial_number()));
auto http = HTTP::POST_sync(subject->ocsp_responder(),
"application/ocsp-request",
diff --git a/src/lib/x509/x509path.h b/src/lib/x509/x509path.h
index a193ebe55..6dd3c21f7 100644
--- a/src/lib/x509/x509path.h
+++ b/src/lib/x509/x509path.h
@@ -229,7 +229,7 @@ Path_Validation_Result BOTAN_DLL x509_path_validate(
* @param hostname if not empty, compared against the DNS name in end_cert
* @param usage if not set to UNSPECIFIED, compared against the key usage in end_cert
* @param validation_time what reference time to use for validation
-* @param ocsp_timeout timeoutput for OCSP operations, 0 disables OCSP check
+* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check
* @param ocsp_resp additional OCSP responses to consider (eg from peer)
* @return result of the path validation
*/
diff --git a/src/python/botan2.py b/src/python/botan2.py
index 399680b86..9d04b83db 100755
--- a/src/python/botan2.py
+++ b/src/python/botan2.py
@@ -3,7 +3,7 @@
"""Python wrapper of the botan crypto library
http://botan.randombit.net
-(C) 2015 Jack Lloyd
+(C) 2015,2017 Jack Lloyd
(C) 2015 Uri Blumenthal (extensions and patches)
Botan is released under the Simplified BSD License (see license.txt)
@@ -11,10 +11,8 @@ Botan is released under the Simplified BSD License (see license.txt)
This module uses the ctypes module and is usable by programs running
under at least CPython 2.7, CPython 3.4 and 3.5, or PyPy.
-It uses botan's ffi module, which exposes a C API. Right now the C API
-is versioned but as it is still in evolution, no provisions are made
-for handling more than a single API version in this module. So this
-module should be used only with the library version it accompanied.
+It uses botan's ffi module, which exposes a C API. It suppports all
+versions of Botan >= 2.0
"""
import sys
@@ -31,11 +29,8 @@ if sys.platform == 'darwin':
else:
botan = CDLL('libbotan-2.so')
-expected_api_rev = 20151015
-botan_api_rev = botan.botan_ffi_api_version()
-
-if botan_api_rev != expected_api_rev:
- raise Exception("Bad botan API rev got %d expected %d" % (botan_api_rev, expected_api_rev))
+if botan.botan_ffi_supports_api(20151015) == False:
+ raise Exception("The Botan library does not support the FFI API expected by this version of the Python module")
# Internal utilities
def _call_fn_returning_vec(guess, fn):
diff --git a/src/scripts/ci/appveyor.yml b/src/scripts/ci/appveyor.yml
index bcc5b118a..eef1e758c 100644
--- a/src/scripts/ci/appveyor.yml
+++ b/src/scripts/ci/appveyor.yml
@@ -1,10 +1,8 @@
-os: Visual Studio 2015
-
# Let's call MSVS 2015 the default compiler, 64 bit the default architecture,
# release the default configuration and --enable-shared the default mode.
#
# Build jobs
-# 1. four basic builds: 32/64bit on MSVS2013/2015
+# 1. six basic builds: 32/64bit on MSVS2013/2015/2017
# 2. add static lib and amalgamation
# 3. add a debug build on MSVS2013/2015
#
@@ -18,36 +16,54 @@ environment:
PLATFORM: x86
CONFIG:
MODE:
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- MSVS: 2013
PLATFORM: x86_amd64
CONFIG:
MODE:
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- MSVS: 2015
PLATFORM: x86
CONFIG:
MODE:
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- MSVS: 2015
PLATFORM: x86_amd64
CONFIG:
MODE:
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ - MSVS: 2017
+ PLATFORM: x86
+ CONFIG:
+ MODE:
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+ - MSVS: 2017
+ PLATFORM: x86_amd64
+ CONFIG:
+ MODE:
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
# 2
- MSVS:
PLATFORM:
CONFIG:
MODE: --disable-shared
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- MSVS:
PLATFORM:
CONFIG:
MODE: --amalgamation
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
# 3
- MSVS: 2013
PLATFORM:
CONFIG: Debug
MODE:
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- MSVS: 2015
PLATFORM:
CONFIG: Debug
MODE:
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
install:
# Set defaults
@@ -66,6 +82,9 @@ install:
- if %MSVS% == 2015 (
call "%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %PLATFORM%
)
+ - if %MSVS% == 2017 (
+ call "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" %PLATFORM%
+ )
- cl # check compiler version
- appveyor DownloadFile http://download.qt.io/official_releases/jom/jom.zip -FileName jom.zip
diff --git a/src/scripts/install.py b/src/scripts/install.py
index 3384534d6..a92cd43eb 100755
--- a/src/scripts/install.py
+++ b/src/scripts/install.py
@@ -9,6 +9,7 @@ Botan is released under the Simplified BSD License (see license.txt)
"""
import errno
+import json
import logging
import optparse
import os
@@ -105,7 +106,8 @@ def main(args = None):
copy_file(src, dst)
os.chmod(dst, exe_mode)
- cfg = eval(open(os.path.join(options.build_dir, 'build_config.py')).read())
+ with open(os.path.join(options.build_dir, 'build_config.json')) as f:
+ cfg = json.load(f)
def process_template(template_str):
class PercentSignTemplate(string.Template):
@@ -176,14 +178,14 @@ def main(args = None):
copy_executable(os.path.join(out_dir, soname_patch),
os.path.join(lib_dir, soname_patch))
- prev_cwd = os.getcwd()
-
- try:
- os.chdir(lib_dir)
- force_symlink(soname_patch, soname_abi)
- force_symlink(soname_patch, soname_base)
- finally:
- os.chdir(prev_cwd)
+ if target_os != "openbsd":
+ prev_cwd = os.getcwd()
+ try:
+ os.chdir(lib_dir)
+ force_symlink(soname_patch, soname_abi)
+ force_symlink(soname_patch, soname_base)
+ finally:
+ os.chdir(prev_cwd)
copy_executable(os.path.join(out_dir, app_exe), os.path.join(bin_dir, app_exe))
diff --git a/src/scripts/website.sh b/src/scripts/website.sh
index bb6e69df7..7018ecd7a 100755
--- a/src/scripts/website.sh
+++ b/src/scripts/website.sh
@@ -3,7 +3,6 @@ set -e
which shellcheck > /dev/null && shellcheck "$0" # Run shellcheck on this if available
SPHINX_CONFIG=./src/build-data/sphinx
-SPHINX_BUILDER="html"
WEBSITE_DIR=./www-botan
WEBSITE_SRC_DIR=./www-src
@@ -11,17 +10,24 @@ WEBSITE_SRC_DIR=./www-src
rm -rf $WEBSITE_SRC_DIR $WEBSITE_DIR
mkdir -p $WEBSITE_SRC_DIR
+# build online manual
cp readme.rst $WEBSITE_SRC_DIR/index.rst
cp -r news.rst doc/security.rst $WEBSITE_SRC_DIR
echo -e ".. toctree::\n\n index\n news\n security\n" > $WEBSITE_SRC_DIR/contents.rst
-sphinx-build -t website -c "$SPHINX_CONFIG" -b "$SPHINX_BUILDER" $WEBSITE_SRC_DIR $WEBSITE_DIR
-sphinx-build -t website -c "$SPHINX_CONFIG" -b "$SPHINX_BUILDER" doc/manual $WEBSITE_DIR/manual
+sphinx-build -t website -c "$SPHINX_CONFIG" -b "html" $WEBSITE_SRC_DIR $WEBSITE_DIR
+sphinx-build -t website -c "$SPHINX_CONFIG" -b "html" doc/manual $WEBSITE_DIR/manual
rm -rf $WEBSITE_DIR/.doctrees
rm -f $WEBSITE_DIR/.buildinfo
rm -rf $WEBSITE_DIR/manual/.doctrees
rm -f $WEBSITE_DIR/manual/.buildinfo
cp license.txt doc/pgpkey.txt $WEBSITE_DIR
+# build manual as pdf for download
+sphinx-build -t website -c "$SPHINX_CONFIG" -b "latex" doc/manual handbook-latex
+(cd handbook-latex && pdflatex botan.tex)
+cp handbook-latex/botan.pdf $WEBSITE_DIR/manual/botan.pdf
+
+# build doxygen
doxygen build/botan.doxy
mv build/docs/doxygen $WEBSITE_DIR/doxygen
diff --git a/src/tests/data/bcrypt.vec b/src/tests/data/bcrypt.vec
index c78ab970a..de0eefbd2 100644
--- a/src/tests/data/bcrypt.vec
+++ b/src/tests/data/bcrypt.vec
@@ -1,5 +1,4 @@
-
# Generated by jBCrypt 0.3
Password = 616263
Passhash = $2a$05$DfPyLs.G6.To9fXEFgUL1O6HpYw3jIXgPcl/L3Qt3jESuWmhxtmpS
@@ -7,3 +6,274 @@ Passhash = $2a$05$DfPyLs.G6.To9fXEFgUL1O6HpYw3jIXgPcl/L3Qt3jESuWmhxtmpS
# http://www.openwall.com/lists/john-dev/2011/06/19/2
Password = A3
Passhash = $2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq
+
+# Following values from http://download.openwall.net/pub/projects/crypt/bcrypt-tester-1.0.tar.gz
+Password =
+Passhash = $2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy
+
+Password = 552A55
+Passhash = $2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW
+
+Password = 552A552A
+Passhash = $2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK
+
+Password = 552A552A55
+Passhash = $2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a
+
+Password = 303132333435363738396162636465666768696A6B6C6D6E6F707172737475767778797A4142434445464748494A4B4C4D4E4F505152535455565758595A303132333435363738396368617273206166746572203732206172652069676E6F726564
+Passhash = $2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui
+
+Password = A3
+Passhash = $2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq
+
+Password = FFFFA3
+Passhash = $2a$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e
+
+Password = FFA33334FFFFFFA3333435
+Passhash = $2a$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi
+
+Password = FFA3333435
+Passhash = $2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e
+
+Password = A36162
+Passhash = $2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS
+
+Password = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6368617273206166746572203732206172652069676E6F72656420617320757375616C
+Passhash = $2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6
+
+Password = AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55
+Passhash = $2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy
+
+Password = 55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF55AAFF
+Passhash = $2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe
+
+# Test very long (> 256 char) password
+# Generated by https://www.dailycred.com/article/bcrypt-calculator
+Password = 4142434445464748494a4b4c4d4e4f505152535455565758595a4142434445464748494a4b4c4d4e4f505152535455565758595a4142434445464748494a4b4c4d4e4f505152535455565758595a4142434445464748494a4b4c4d4e4f505152535455565758595a4142434445464748494a4b4c4d4e4f505152535455565758595a4142434445464748494a4b4c4d4e4f505152535455565758595a4142434445464748494a4b4c4d4e4f505152535455565758595a4142434445464748494a4b4c4d4e4f505152535455565758595a4142434445464748494a4b4c4d4e4f505152535455565758595a4142434445464748494a4b4c4d4e4f505152535455565758595b
+Passhash = $2a$04$nP0HWhorPRGl309OF27N0Oluj0wfAKWClP9gDcqOU1D.VF4x6bHTi
+
+# Generated by OpenBSD's bcrypt code
+
+Password =
+Passhash = $2a$04$......................w74bL5gU7LSJClZClCa.Pkz14aTv/XO
+
+Password = 41
+Passhash = $2a$04$......................1ylfG1rzUcx/p4E2WWXbK1hNBjulV/e
+
+Password = 4142
+Passhash = $2a$04$......................eLmsldq6Kef4lNzfsH3GLn5XEr9/dky
+
+Password = 414243
+Passhash = $2a$04$......................SeoxjjNIHEA7B01Yt2Fq2YNSKittPem
+
+Password = 41424344
+Passhash = $2a$04$......................5UWApRNcV8gDN6km6KdMC7MJRnFLJLi
+
+Password = 4142434445
+Passhash = $2a$04$......................aqTIOQ1wy6xo2DKiG2jtVHuLfBC.Cte
+
+Password = 414243444546
+Passhash = $2a$04$......................3eUc6EVdIHLPSHsfENd73y1qHUgeET6
+
+Password = 41424344454647
+Passhash = $2a$04$......................FuV659LudNIL0yJfqrb.JB0ab1eCXCy
+
+Password = 4142434445464748
+Passhash = $2a$04$......................1WDT31a/PBuYi4hmam2gvmgA54t9HUO
+
+Password = 414243444546474849
+Passhash = $2a$04$......................aHMfyD101pOa19Avcj8wFk7x8JyP/Oi
+
+Password = 4142434445464748494A
+Passhash = $2a$04$......................MQooKLE8.P36GbWDUbrk2NT3PYZsXOG
+
+Password = 4142434445464748494A4B
+Passhash = $2a$04$......................eprYLc9vXOwDMCyqLAGvznMqJmzEEzu
+
+Password = 4142434445464748494A4B4C
+Passhash = $2a$04$......................wg1/merzamWae4FzZdzXVFCzeXcdRBe
+
+Password = 4142434445464748494A4B4C4D
+Passhash = $2a$04$......................F45.kWUMV0S88ts9G8w1ySdc0eL1QsO
+
+Password = 4142434445464748494A4B4C4D4E
+Passhash = $2a$04$......................b5f2SSP91ThKR9rHlejbqwpgGTKgEPW
+
+Password = 4142434445464748494A4B4C4D4E4F
+Passhash = $2a$04$......................GAUIFe599t9404.M92TX4qg.qlyozvu
+
+Password = 4142434445464748494A4B4C4D4E4F50
+Passhash = $2a$04$......................hGf/sBFCzxtt7aLth.CkSiqMLfMNkky
+
+Password = 4142434445464748494A4B4C4D4E4F5051
+Passhash = $2a$04$......................wxi.5vCbqbfYRB4Ptb9YMVEedkzkg2G
+
+Password = 4142434445464748494A4B4C4D4E4F505152
+Passhash = $2a$04$......................fEQhKOa7gmjs2X6pPxkAG4Ua1CK3/MG
+
+Password = 4142434445464748494A4B4C4D4E4F50515253
+Passhash = $2a$04$......................GeXtzTa.p8bAbpHdSzLDDvcT5z8rd2G
+
+Password = 4142434445464748494A4B4C4D4E4F5051525354
+Passhash = $2a$04$......................D.nuIHnFpRTTietbZXCpTqMLktmgvOG
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455
+Passhash = $2a$04$......................0rP0aIe6CiaXgLviO7CrReRMoq4Z76u
+
+Password = 4142434445464748494A4B4C4D4E4F50515253545556
+Passhash = $2a$04$......................2nuK3e9hYlR3ZpUulSwuH9XEFf/TwB.
+
+Password = 4142434445464748494A4B4C4D4E4F5051525354555657
+Passhash = $2a$04$......................pFb7ADMM2CyyVheTwTO4ljTOaAd.SO2
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758
+Passhash = $2a$04$......................Hb6CQJzZLi0jF2iRoWF/LytXl0UIlGi
+
+Password = 4142434445464748494A4B4C4D4E4F50515253545556575859
+Passhash = $2a$04$......................hDkGLLxi1xlzQ2l8yCVZW8STSQwY8ca
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A
+Passhash = $2a$04$......................rEzQdCIvx3710X.o8rPHje0DJNW7nby
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B
+Passhash = $2a$04$......................YRx250oXqZ8PAF9VCwDd3tMHvNG/EYS
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C
+Passhash = $2a$04$......................8VVYuFwpyz50KoSQSww6HtAcM.puvFK
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D
+Passhash = $2a$04$......................U/uWNlMQ1nCojM9KJYXst0H..Vr3K7e
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E
+Passhash = $2a$04$......................JKICUBStZDD90QJxUirvbW6XhMN3k0i
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F
+Passhash = $2a$04$......................XQ1Pz.WGSIGPbun4umJ/uKtAi5mImNK
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60
+Passhash = $2a$04$......................zK87xeHJykwKa7B3WqaREFx8LkTw7w.
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061
+Passhash = $2a$04$......................DRU9poimCmGTetm8ijeul2OZ7Ghgyn.
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162
+Passhash = $2a$04$......................cOoYOK6c366gK6BBmcYlCPqGGy7/Yce
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263
+Passhash = $2a$04$......................f31Ni98iYpbJzxJoqhWuxvB8PPUOc5G
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364
+Passhash = $2a$04$......................uZLqllZzmsIDcmdihrBslz0A.WJp5lC
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465
+Passhash = $2a$04$......................Mv6hK0wgc5CxkxPVhyit7DjpOLHCRme
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263646566
+Passhash = $2a$04$......................40cOEgw5sV8TKGjzB4JF.yiTJCBJGsq
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364656667
+Passhash = $2a$04$......................DR/BFDmrsl0CqGeCo5EYawLtrLL2PF6
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768
+Passhash = $2a$04$......................7VtgAnmfpC6qryaX7qsvlfCvk2ooW4S
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263646566676869
+Passhash = $2a$04$......................Eo0q.nfaVXn4NIfoyveRiLRKHSMDAHW
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A
+Passhash = $2a$04$......................kfxex5FfUJhJQYogm.8FloXjQjvcl..
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B
+Passhash = $2a$04$......................fmpuNlu0eW7fsRBEbIlZs/ZIP0a9Dby
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C
+Passhash = $2a$04$......................mI0Rbwk/yFUkA/TLKmMfSMu4KqSGzZq
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D
+Passhash = $2a$04$......................jqCcJxoUtwRpIFnCPZtrn2zpFY6tU6a
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E
+Passhash = $2a$04$......................FGzij2Dvl2qbVhtOfDhvGodD0BaH1zO
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F
+Passhash = $2a$04$......................8gWGyOqSrT/N84xajt5y1cc7kdYTS1C
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70
+Passhash = $2a$04$......................n0uapt/O8ZGLLoTYi8RVz5gtLzcH9OG
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071
+Passhash = $2a$04$......................snIIPVZopm0TC4WLrpTNtW136us.66S
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172
+Passhash = $2a$04$......................xJlz/E02Am2/sxO97jDYuFkxKMCNPuC
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273
+Passhash = $2a$04$......................ci6b5BSX.Gt1z2O5on9.k9Po1q6nJMe
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071727374
+Passhash = $2a$04$......................JGYY7FRsBznRZJfr8gNUgmRIekndoEu
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475
+Passhash = $2a$04$......................c.8sucK9TMUPlwbux0u2EjoWeS7exm.
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576
+Passhash = $2a$04$......................2iQopzqprPYwjRJTAJnVPO00t8/HyT.
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071727374757677
+Passhash = $2a$04$......................COrfw5/Mj6Js8CePzOVuowO57dzDlXq
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778
+Passhash = $2a$04$......................M/SrGocfiSlHaOnFqqV0RGjlcuPM2xO
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576777879
+Passhash = $2a$04$......................gPjyepv.g7qMZXDTwcEjfks0xFwQDsW
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A
+Passhash = $2a$04$......................YCdU1yffMxpqGnbkhm4j11QBUQzr6vW
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B
+Passhash = $2a$04$......................PFZW7x0F2WdDhhfr9IbRIJhaUrlT/4.
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C
+Passhash = $2a$04$......................2gd/Za4BdTci7v1rdamN0XZ5lk5PnuO
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D
+Passhash = $2a$04$......................fbhaW8SPcJjDFtC9ruTvwIZhbhoT6Ve
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E
+Passhash = $2a$04$......................yB12tMsxREReDqkSHzR6G890abKjKHi
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F
+Passhash = $2a$04$......................yyR3U//QnDydsWfSIZwCsrzXqwdED7e
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80
+Passhash = $2a$04$......................IWK3CyxBeu3hZXP./rSl1gS.CHOl51q
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081
+Passhash = $2a$04$......................OrN52h3sUOH7u7aUFZLLPecPAC6pDUy
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182
+Passhash = $2a$04$......................7czL69h9T6Z84Yen8wrtzeNUPZIksLq
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80818283
+Passhash = $2a$04$......................hlFxmtvTDFEJ/W7ViRXVzIBmwELyxde
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081828384
+Passhash = $2a$04$......................wtpFiSjRvlfidwkUDR2EefHBYOStMyO
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485
+Passhash = $2a$04$......................fFhiRdC6u8ZnZNqxK5vIyMinSFC4HjG
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80818283848586
+Passhash = $2a$04$......................FCJRl4rapF1jLog3AjcYUtLupr62MHW
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081828384858687
+Passhash = $2a$04$......................qt4eTaEVpLnPbEit4noon6YMRxjO8kq
+
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788
+Passhash = $2a$04$....................../VvYrJip/blbJEy92Sih8t0k26f242.
+
+# This demonstrates truncation of passwords > 72 chars, identical to previous hash
+Password = 4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80818283848586878889
+Passhash = $2a$04$....................../VvYrJip/blbJEy92Sih8t0k26f242.
+
diff --git a/src/tests/data/dates.vec b/src/tests/data/dates.vec
index 46db7f71a..ceb8ed3cc 100644
--- a/src/tests/data/dates.vec
+++ b/src/tests/data/dates.vec
@@ -9,6 +9,9 @@ Date = 2037,12,31,23,59,59
Date = 1800,01,01,0,0,0
Date = 1969,12,31,23,59,58
Date = 1969,12,31,23,59,59
+
+[valid.64_bit_time_t]
+# only valid if 64 bit std::time_t is used
Date = 2038,01,01,0,0,0
Date = 2083,05,20,8,30,9
diff --git a/src/tests/data/pubkey/dh_invalid.vec b/src/tests/data/pubkey/dh_invalid.vec
index 8911cdff3..2a5de3dd7 100644
--- a/src/tests/data/pubkey/dh_invalid.vec
+++ b/src/tests/data/pubkey/dh_invalid.vec
@@ -1,4 +1,5 @@
-# public keys failing checks from NIST CAVS file 20.1 (Generated on Mon Jun 20 09:02:25 2016)
+
+# Public keys failing checks from NIST CAVS file 20.1 (Generated on Mon Jun 20 09:02:25 2016)
# http://csrc.nist.gov/groups/STM/cavp/documents/keymgmt/KASTestVectorsFFC2016.zip
G = 0x1e2b67448a1869df1ce57517dc5e797b62c5d2c832e23f954bef8bcca74489db6caed2ea496b52a52cb664a168374cb176ddc4bc0068c6eef3a746e561f8dc65195fdaf12b363e90cfffdac18ab3ffefa4b2ad1904b45dd9f6b76b477ef8816802c7bd7cb0c0ab25d378098f5625e7ff737341af63f67cbd00509efbc6470ec38c17b7878a463cebda80053f36558a308923e6b41f465385a4f24fdb303c37fb998fc1e49e3c09ce345ff7cea18e9cd1457eb93daa87dba8a31508fa5695c32ce485962eb1834144413b41ef936db71b79d6fe985c018ac396e3af25054dbbc95e56ab5d4d4b7b61a70670e789c336b46b9f7be43cf6eb0e68b40e33a55d55cc
diff --git a/src/tests/data/pubkey/ecc_invalid.vec b/src/tests/data/pubkey/ecc_invalid.vec
new file mode 100644
index 000000000..887658f26
--- /dev/null
+++ b/src/tests/data/pubkey/ecc_invalid.vec
@@ -0,0 +1,18 @@
+
+# Public keys failing checks from Google's Wycheproof
+# https://github.com/google/wycheproof/blob/master/java/com/google/security/wycheproof/testcases/EcKeyTest.java
+
+# order = -115792089210356248762697446949407573529996955224135760342422259061068512044369
+SubjectPublicKey = 308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d0101022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff30440420ffffffff00000001000000000000000000000000fffffffffffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f50221ff00000000ffffffff00000000000000004319055258e8617b0c46353d039cdaaf02010103420004cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8
+
+# order = 0
+SubjectPublicKey = 308201123081cb06072a8648ce3d02013081bf020101302c06072a8648ce3d0101022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff30440420ffffffff00000001000000000000000000000000fffffffffffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5020002010103420004cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8
+
+# cofactor = -1
+SubjectPublicKey = 308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d0101022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff30440420ffffffff00000001000000000000000000000000fffffffffffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325510201ff03420004cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8
+
+# cofactor = 0
+SubjectPublicKey = 308201323081eb06072a8648ce3d02013081df020101302c06072a8648ce3d0101022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff30440420ffffffff00000001000000000000000000000000fffffffffffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551020003420004cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8
+
+# cofactor = 115792089210356248762697446949407573529996955224135760342422259061068512044369
+SubjectPublicKey = 308201553082010d06072a8648ce3d020130820100020101302c06072a8648ce3d0101022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff30440420ffffffff00000001000000000000000000000000fffffffffffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63255103420004cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8
diff --git a/src/tests/data/pubkey/ecdsa_invalid.vec b/src/tests/data/pubkey/ecdsa_invalid.vec
new file mode 100644
index 000000000..db87a38f7
--- /dev/null
+++ b/src/tests/data/pubkey/ecdsa_invalid.vec
@@ -0,0 +1,368 @@
+
+# Public keys failing checks from NIST CAVS file 11.0 (Generated on Tue Mar 01 23:36:01 2011)
+# http://csrc.nist.gov/groups/STM/cavp/documents/dss/186-2ecdsatestvectors.zip
+
+# [P-192]
+
+Group = secp192r1
+
+# Point not on curve
+InvalidKeyX = 0x491c0c4761b0a4a147b5e4ce03a531546644f5d1e3d05e57
+InvalidKeyY = 0x6fa5addd47c5d6be3933fbff88f57a6c8ca0232c471965de
+
+# Point not on curve
+InvalidKeyX = 0x4c6b9ea0dec92ecfff7799470be6a2277b9169daf45d54bb
+InvalidKeyY = 0xf0eab42826704f51b26ae98036e83230becb639dd1964627
+
+# Point not on curve
+InvalidKeyX = 0x82c949295156192df0b52480e38c810751ac570daec460a3
+InvalidKeyY = 0x200057ada615c80b8ff256ce8d47f2562b74a438f1921ac3
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x1b574acd4fb0f60dde3e3b5f3f0e94211f95112e43cba6fd2
+InvalidKeyY = 0xbcc1b8a770f01a22e84d7f14e44932ffe094d8e3b1e6ac26
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x16ba109f1f1bb44e0d05b80181c03412ea764a59601d17e9f
+InvalidKeyY = 0x0569a843dbb4e287db420d6b9fe30cd7b5d578b052315f56
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x1333308a7c833ede5189d25ea3525919c9bd16370d904938d
+InvalidKeyY = 0xb10fd01d67df75ff9b726c700c1b50596c9f0766ea56f80e
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x158e8b6f0b14216bc52fe8897b4305d870ede70436a96741d
+InvalidKeyY = 0xfb3f970b19a313571a1a23be310923f85acc1cab0a157cbd
+
+# Point not on curve
+InvalidKeyX = 0xace95b650c08f73dbb4fa7b4bbdebd6b809a25b28ed135ef
+InvalidKeyY = 0xe9b8679404166d1329dd539ad52aad9a1b6681f5f26bb9aa
+
+# [P-224]
+
+Group = secp224r1
+
+# Point not on curve
+InvalidKeyX = 0x3913b7c347f0d56bdda1244a973378ae1a23b6c05f6ea276491e75d8
+InvalidKeyY = 0xc5c9086cb4704540d566a9f2cc461488fb80b7dd7384cefea4616c15
+
+# Point not on curve
+InvalidKeyX = 0x2b27eeb74e93b92f423e8d1bdb6869811746af14c2887a54338f3982
+InvalidKeyY = 0xca92f56341ce049bf0300a1cc5f57be96cdc1703512c28b1e07ab6c4
+
+# Point not on curve
+InvalidKeyX = 0xc32bc4bee87df6478f76cc74552c337fbc00026d74f22068e6a98e2a
+InvalidKeyY = 0x9c618bec3f89628a61638d69d61824d36070379fa0d2c6d7a63a62e7
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0xc0d570b903d8f1743b3235af72c0772abd5209e96b7d6d43f305d3f8
+InvalidKeyY = 0x11fe6013787c8a8dcc19ca6be51aec3dddc5b92d9540a047af860e76c
+
+# Point not on curve
+InvalidKeyX = 0x7ed8ceb65fc7d06dc6f4976b33f2611ef0da9913900c1073cabd3836
+InvalidKeyY = 0xe2594a63469d0b84fdd3e29cec8a08427e71c585d9653ab1322dfad1
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x56579986c148519adb29e8d2d374e7ceddafc85448612a297f0f0f46
+InvalidKeyY = 0x1d6ac5f9a38354875f1ed2973aa44d7b8ca5e5ad7249ee3bc648b20b3
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x1e2412382d3c1b0683bdd152a64a0e1ee06359146872a6fc26584b666
+InvalidKeyY = 0x125591c446520d0dcae5c9287c2ce4fb69a1f82827d41f9fe4f29744
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x847ac5c23e0f100fcd9451ab948eaf78eb38aab98060d1539cb485d0
+InvalidKeyY = 0x170ca182475dd56eda14e3eaf3f2fbd17926d41175ea272e475e8732d
+
+# [P-256]
+
+Group = secp256r1
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0xd2b419e62dc101b395401208b9868a3b3fd007ad92adb18921c068d416aa22e7
+InvalidKeyY = 0x17952007e021b46a2ab12f14115aafb70608a37f0c3366e7e3921414b904d395a
+
+# Point not on curve
+InvalidKeyX = 0x6f969d90fd494b04913eda9e0cf23f66eea5a70dfd5fb3e48f393397421c2b02
+InvalidKeyY = 0xc19ad66d7d6993b792b608879e1d861026805cf6fde1f5d8bb4f790ad1cee456
+
+# Point not on curve
+InvalidKeyX = 0x8332d9d42b5f48f08b3dd969dbbb28d2be9de30adf560727068e670444f5976f
+InvalidKeyY = 0x58880380a26a9b3881d189da6b48a11a531c31cefebe696efbd5eaec5917382d
+
+# Point not on curve
+InvalidKeyX = 0xd45779fb33629e21abe0d4a5f8b99f12c71952e53aa4ca065cc393e6300d0f2e
+InvalidKeyY = 0x6d1a3d5666c7ee9c84c03f02ca2834f5eaf924d6c15536a7b4877481be3fcd2c
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x18c60b77ce23fb3210073ba7dad451ca25bf16c3c1d2d67b2e6eca51f1c77e56
+InvalidKeyY = 0x19b47a0fea8c46dc3bd9f65506b281a9dc872d16ed90fd20e94f2fbc94c68d0ba
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x5772caf0d1641479a79aa443fefb222a385b3c481ff51e9fce76ccb513a9bfad
+InvalidKeyY = 0x110906da47200cb4a536f2c4f601c4ecf82d8dc18405a1cd4746b25a572b46b2c
+
+# Point not on curve
+InvalidKeyX = 0x9ba790614fa1c43816b77729ce03f2cc9666e25f27488886a270b22a36636f11
+InvalidKeyY = 0x0a0d36ae87c44c2671a2684793fe8ef2bf6c17411f3fb972b695e30b101b1184
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0xdde744307e7b64098581aa52db5de8c22cdbd46959922e15a714192b7597ecf4
+InvalidKeyY = 0x111c5393185604be8cbd53b28d2c3fcb4e853291a5bd82660564e4eeda10924c8
+
+# [P-384]
+
+Group = secp384r1
+
+# Point not on curve
+InvalidKeyX = 0x58fd70ca9e315b2c99152fd3f1071889fcfbe1387c02e6169850e2cba3bb3d21c9f295def8b7680fbca9c43956cfc190
+InvalidKeyY = 0xbf7a80d161e429445fb613aeffb71840e1cd7fa9139be4c535c33ae32790f48f6ce4de4b275a55d0b433e86af00766a9
+
+# Point not on curve
+InvalidKeyX = 0x06a05a2f6a05c7e8684b6df74392faea822f89eecad01e791b0559f6ac650abd85084ceeddfbee85391d5809adb73fef
+InvalidKeyY = 0x3191a5af6ef4bbad57fe2748ebfe98a4f71dd7b580349d853b9052f326d7a8a42a45ee6e6fd67a49ddbd23b53b92b6d6
+
+# Point not on curve
+InvalidKeyX = 0x432989eeadbb65d0b11ce46e6049b9c871941a7f4349b30ac0a4e5494bd43cc21608a1e61211f3071c8af12a90475792
+InvalidKeyY = 0xbc68ef58e930b4e5a4668d682582c2a1ea1de54589eebe1b754588ba6f14dbcd49b2265a584b7bcb8f7013c6cf7e7880
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x1a9e6542ec66d46262c75bd60d9767e114eab403bb3e7576261e5b9a243091b9784c863bbba63cfd3a325d51bdf19eb0d
+InvalidKeyY = 0x227e025dc40c7025e0961e7841d765d2b59a77fba8dab2150ec9f3153e4fdd2194f775c1b98abb2af9369e4a459cf205
+
+# Point not on curve
+InvalidKeyX = 0x494c8ecf87e9b7964a1a5736a999828da7904c4345d2c9a521a3d0ae07067bab548a0bca0f90828b678fc182572ea067
+InvalidKeyY = 0x15742dd1305079f2b2a8c7f75d191bbca147a7bf111b8d619fb00ae53cf9988084e7c2a90c516c93b06e4ef525e42fde
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x19c0a1c05c945ecadfd00377c81eb4902f40bc3a411c81ab94a444fde37507ee0b341be268da10b6259ac2b5a0965a08e
+InvalidKeyY = 0xdb9e823dfffde66128955fbd322bac382ce2c2ceb8e11f32f98f09dec41631c22df7ea8838b160adb21685847591959b
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0xc4059321bfc49437084eb6b5c746b52c03aceb7959d72f620b9d668ef2b314ea63fec712262d516ae7d839592f8ed118
+InvalidKeyY = 0x165aa703ba328334c9938e61d660b328a6dc672e17b62b550634b862c26f63ee4856bdcc822237c6d598a55175937affd
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x14f96eb9c3623e8ca98edc297f82501d5c6c7c01c11bda6e5e5e166b8525c22c773a5e55fb5a7d04cae08f6a190ad3ac8
+InvalidKeyY = 0x47f665e4fb9e9f9e592ac8441e41aeb1c0ed240a05bbd93d6ec260a0644a4d9202aa5a5f28cefde4362878d5aae68222
+
+# [P-521]
+
+Group = secp521r1
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x07573e6115674bbffb65097f3dcb05597a35193bf0ed1a4b90a86006eabe5ed638d1e11adb769cd6ed7fba181dea42ffc38a611a6f162fe10b925b80ce9c419ac80
+InvalidKeyY = 0x2dbe133e4ff21af1aa50742fc1f7c74cbe8342fe5037c33b961f65b218f947a4acada6d53b1e0bf9ada5be979652275bce77194c8fae2066c5531196aa9997dd2bc
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x014bc514f2b5664a35d1728cc096cc7de2b0391089c014f5f3e4d1048ec5d8497fbe6cfe008b634d708d12bac6aa616c78af1576c0a8676a17a16773ee5a827e3b5
+InvalidKeyY = 0x2c027dade98d1a7b433368075e19e8269d8465bbc91c4c3aebfdb2418115d331ea946663713d67f9226f4920c85b0d78c312e87826672f107bcd6c19ea17fff4331
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x259d237214e071f725c77674b38b8b6cc590bed12b7cfdb3d177162afb2a40ab0540f77386ab98ac049711287a5b80891ece13ea34c545c19593e87a15237e2a108
+InvalidKeyY = 0x1f33c8963f0981350d77615f0e04e24515a76efc690140272efddaa8cb85b140acad7fc42cf6da7ff5f3ce47183f46e7272b06eca4c74200536223fef2d74e095b1
+
+# Point not on curve
+InvalidKeyX = 0x089eb672b023d7098bb864bd7789b9f6f4ee268aa9dca6ba3268023b2119be34ee035699d7f1f776ff6028a91824fbfefa22671ed2ce7ffb46ddcece33d1087985d
+InvalidKeyY = 0x07b7ea0588de385a35a5c0e7dba9cd86fd13d91f71a97a9769e483c2fb823cc3ae9fb9800a05814a25af676f780de1d805a174b70703b51e46455d0eae78b5d7e1d
+
+# Point not on curve
+InvalidKeyX = 0x1859642073e648dd5580346ad5ba9daec8b60d5b574938b2f16ef2a48128ffcadaa46be1fa10ded234d72ec3c38d7cb898281d25264c00d83c2a14bab175ddb9d2b
+InvalidKeyY = 0x11ad23cb933fa28e00d7d9b0faf78297e4f2b026e6fff74e456b8f2df938e52ceaf98760070c8d22c7f742728434eb6ae6afe7193ee81bc730f5549eebacf7f9952
+
+# Point not on curve
+InvalidKeyX = 0x176b36128e5a294876c57fac275f388155eb5715c8ace3d90ee4c31b755c8f867327b7e037e7be8f6b521b2674e1786d67294c1f5b098be16102ddd361d92505fd0
+InvalidKeyY = 0x0d01b3f053aecde9e0c534d6a518fe24c68ef246b4cff071a3ebbc742152c9d4e872b1acd5a76a42847fe98e9360e7c33ba8575cf75218e89564839ac9f13e6ef14
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x01f26ff28f769521f232fb83e697c9cde606d11383115deef0af16fc05e4631850b57975ae91299b87133fc53bdb424fd8f21b28c7636055eca88f3417d80a3bde1
+InvalidKeyY = 0x2748bee6ea7c3b9790fb927c0e691436b946d35b7d52b98398cbcf433683138f48ebe93a007e611f00a73dcfadfd2b0bedbc48bf2de204969df04cebfc1018072f1
+
+# Point not on curve
+InvalidKeyX = 0x0fcacf322f6be9da5342dae87cbc8cdcb22bc489ca6e97b186b97d2ac02610518b5ee72be37f22825278fb205895f2f823540b91b313abb54a6b41506152e0deec3
+InvalidKeyY = 0x187333ce6fe5e6dea5d08d8f5950b5207cb8eb34fa0de2cae5acad8bc8436ff617b45bd8f2975f2762982219b3136bffec3f6c58f8f2cd0d6eb2ebd46467219126f
+
+# TODO check FIPS 186-4
+
+# Public keys failing checks from NIST CAVS file 11.0 (Generated on Wed Mar 16 16:16:42 2011)
+# http://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip
+
+# [P-192]
+
+Group = secp192r1
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x472a620598e6715eff9cc022805d8cc8e8219f0e32042538
+InvalidKeyY = 0x1971ca86edb3471b2a16b9aae9de90f366f371b26385027e6
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x192a2b854bf4e70d5a8fecc98f43b4a744b26808f8cf4c60d
+InvalidKeyY = 0x2c0b29190588eabf08dfe160ac8d3ab6f5d5cc73678ebae8
+
+# Point not on curve
+InvalidKeyX = 0xc07ce28e4c846d7327f0554119ddb7e865fa1dd448ba2b40
+InvalidKeyY = 0x33aefa3177b99901d9ab6c12eb0749197420296ccb9d4e4a
+
+# Point not on curve
+InvalidKeyX = 0xf77c2e5946d99932b2a01c1c73a296ecde568978103d8e2b
+InvalidKeyY = 0xde46b2d5c94dc11b53578eafaa23f96de9747b086979416c
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x1016451af3e7a7fa2ce3cf2acbe07fa7fa19a5f14455bf2ec
+InvalidKeyY = 0xc074630aea063e00bb41e6fbf752dd4f8e5bc742bf3363eb
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x18eea61787fbcd90f73f947346cdf13f05b4170e3e7456165
+InvalidKeyY = 0x5514c7b6e0eecc4e9c1ad99710f009a550bf3f952bb16593
+
+# Point not on curve
+InvalidKeyX = 0x39ed11c88869f6c4705125d9d5fc7c6b1e3d22b2fa7a6b57
+InvalidKeyY = 0xd0cf50208f6b1a61ba346a3f3f8f58128c8199e5405a6f11
+
+# Point not on curve
+InvalidKeyX = 0x87d67f9b7cced918d827ffc086cfd6a181fc61b2f56e000b
+InvalidKeyY = 0xc6c8d686c61a816d25c085db665f018e31ad6f71ee24d895
+
+[P-224]
+
+Group = secp224r1
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x7a9369e2173bbf29589bf47e3ae0ccf47df6d2268c2292f906cc9261
+InvalidKeyY = 0x11afc53c7c1b085029f53b41fcd5a336bafb35b89d302f2bd04df44e6
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x1803faeef9b40957f59ab97d543f86690afd7471dfb8b04b84ea31085
+InvalidKeyY = 0x738cc29474ca048930b7f1a29db3773d11839ed83a6993e3f23692d7
+
+# Point not on curve
+InvalidKeyX = 0xc01795a001b6b8a5b3db9acbdb55c2f97f4a50aa0a0cfed1d50a4c28
+InvalidKeyY = 0xb79dbe52a47a4640100cc939b435377f0bcb8db4ec52ecaadac5d919
+
+# Point not on curve
+InvalidKeyX = 0xfbe3bff58dc58ca1ef9dc942fd43cdadbd060d70e0b1e6b9583a2228
+InvalidKeyY = 0xca844b43c237d497c34b986c681bf3cc54f968c0db74b2e1d9fe9d94
+
+# Point not on curve
+InvalidKeyX = 0xcbe83c33848dd5a89ea8c45d23b99f23254e2077bd9ab26f6b5bed9f
+InvalidKeyY = 0xc0d09533d78a96e39028162534d74b097364095e2dc60776938af83b
+
+# Point not on curve
+InvalidKeyX = 0x491e8d6c73708104c9530878f866e585cba008ef70baa46a809a2c03
+InvalidKeyY = 0x924a28ace8db9a88f7f874a1f24ac7f0bf56484f2130d5be5a8a1721
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x1a89dc6a91002c9d25a3c4621fb5606b52531fd8e48a44119f442f749
+InvalidKeyX = 0x62f556641faa83059425026ca18ecbd219fe6d5df3b7713ce8b168cd
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x182a4cee32c06292556f4e29950f5b2db9ad627a56e92680358d6cac4
+InvalidKeyY = 0xfa2a87aa3757ae9fa00d11db57089632c4f9e33fb214b9324cf75bd9
+
+[P-256]
+
+Group = secp256r1
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0xd17c446237d9df87266ba3a91ff27f45abfdcb77bfd83536e92903efb861a9a9
+InvalidKeyY = 0x1eabb6a349ce2cd447d777b6739c5fc066add2002d2029052c408d0701066231c
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x17875397ae87369365656d490e8ce956911bd97607f2aff41b56f6f3a61989826
+InvalidKeyY = 0x980a3c4f61b9692633fbba5ef04c9cb546dd05cdec9fa8428b8849670e2fba92
+
+# Point not on curve
+InvalidKeyX = 0xf2d1c0dc0852c3d8a2a2500a23a44813ccce1ac4e58444175b440469ffc12273
+InvalidKeyY = 0x32bfe992831b305d8c37b9672df5d29fcb5c29b4a40534683e3ace23d24647dd
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x10b0ca230fff7c04768f4b3d5c75fa9f6c539bea644dffbec5dc796a213061b58
+InvalidKeyY = 0x5edf37c11052b75f771b7f9fa050e353e464221fec916684ed45b6fead38205
+
+# Point not on curve
+InvalidKeyX = 0xa40d077a87dae157d93dcccf3fe3aca9c6479a75aa2669509d2ef05c7de6782f
+InvalidKeyY = 0x503d86b87d743ba20804fd7e7884aa017414a7b5b5963e0d46e3a9611419ddf3
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x14bf57f76c260b51ec6bbc72dbd49f02a56eaed070b774dc4bad75a54653c3d56
+InvalidKeyY = 0x7a231a23bf8b3aa31d9600d888a0678677a30e573decd3dc56b33f365cc11236
+
+# Point not on curve
+InvalidKeyX = 0xf8c6dd3181a76aa0e36c2790bba47041acbe7b1e473ff71eee39a824dc595ff0
+InvalidKeyY = 0x9c965f227f281b3072b95b8daf29e88b35284f3574462e268e529bbdc50e9e52
+
+# Point not on curve
+InvalidKeyX = 0x7a81a7e0b015252928d8b36e4ca37e92fdc328eb25c774b4f872693028c4be38
+InvalidKeyX = 0x08862f7335147261e7b1c3d055f9a316e4cab7daf99cc09d1c647f5dd6e7d5bb
+
+[P-384]
+
+Group = secp384r1
+
+# Point not on curve
+InvalidKeyX = 0xe87cc868cdf196471d3fc78c324be2c4a0de8dbde182afea88baa51666f3cc9993eae5f1d60d4aec58894f0357273c48
+InvalidKeyY = 0x187219b0adc398c835791798053cc6a0bcc6e43228ac23101ee93dfce0e508be988a55fa495eb93b832064dc035e7720
+
+# Point not on curve
+InvalidKeyX = 0x25e5509a54f5fa62f94551dff3dfe210db1bb2bbc8fd4e672fbd5a211f9fd2f7eadc2b83fcd4198b7f857d9a2dc39c11
+InvalidKeyY = 0x98a4a13bc2f2d04bebd6d4e04412a9d306e57b90364583a6ec25bf6f0175bb5b397b8cfea83fd5d1e0ad052852b4aba7
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x11a14be72dd023667047c260dd1960dd16555289d9570001d53ea3e494c1c107800dc5b24dd4de8490a071658702a0962
+InvalidKeyY = 0x78d65f6975d10df838b96a16cba873b59c28f2c7d05654b8c8b78bd193694ae45d6c6e046a20b984c3467c72d49395fe
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x1bf2238026a2489fb6ac1a8d6b82fdb33b05e8d01f1e2671eb22e61734031cc63efbf7e14d23e81fd432fc9935c627cdd
+InvalidKeyY = 0x6b377c8b187d568b782a28b38a7861b69e3d016f9f9ebb7eff2e7732a5132785b5a32e069dcef12875a995908a8b72f1
+
+# Point not on curve
+InvalidKeyX = 0xa999b80932ea62b4689769225b3ff34b0709c4e32342a824799ca63dcce1f3ed8819e080fc7fa130c1881c8131f4bcb5
+InvalidKeyY = 0xb8c77d0868c2c159e1be6bcd60ec488ab31531c21e1cb8fe2493ed26ac848fde7d27823a9a4912650511a3d460e25ef2
+
+# Point not on curve
+InvalidKeyX = 0x5cbaa8088b0804fe14c2a6fa54b1adee1690fd17a682ea9ec5202ba7575e217c37e98565b7e95e7c882bb6eef76a3df1
+InvalidKeyY = 0x79d8c7e96ae7a7668496317c596b24ebe56e6ea5bc64b74c38867eb2c419d8277d20b9c27a2d5c75d1c7a47885d38d0e
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0xcfb4dbdcb1a8c6e8c6b4a9dd091eed015476ebd20837de1f6261a27999a08cff345f0d4627eb7778fc3495916a6d017b
+InvalidKeyY = 0x1c08f7a421bc0731321374f9b31ecf5ca820c006180da4c496f29f0d0e4947f368808fd3052ee4f1afb8c2005fd0c0ee8
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x1adaff25f37c8dfd33ecf216691a2107e522c21c99e29a76d8c1757ef84cc37c73ec5c2aa3be2fb0d5f1d372e08fbf9e
+InvalidKeyY = 0x1f39c8f86a20c130c34f767e085217232599541516e2d79d8e526fa03082bed2a5dc5fde6fd410c30245212e7816dd014
+
+# [P-521]
+
+Group = secp521r1
+
+# Point not on curve
+InvalidKeyX = 0x165252970b786685babd0463f7314275c44ac1b558ab5a8e4bde60a441623b204982dcba2d3c0e7d379d5b637fd3edc0b0d2e0b7a33f7b36c03bb8bf3c6c5469ebe
+InvalidKeyY = 0x1300db0f0bc9b21ecff15eff4ed3bbe3dc1ac403dc96c89344d0030304da7ce57f1dc757af6816279464c61a0ab33645c3cd6583842cff0928081660b30775f594f
+
+# Point not on curve
+InvalidKeyX = 0x1a39c4c5d5f6af8285931694b030f6b8bbc0a012ab73c3947c72a6210643cc63673947f5847f2503bb81ae1c8b6a0d7cb0ee5675f9027ca75445aee2b6d7beb78ea
+InvalidKeyY = 0x148beebbe6e298779e59d8fc88cfc28f4aa784d927e5127813894b6d593760608539d2eb9db9cc8b39813a5e5e03a7d39be67c9c8a566fa8d65ff25b5bee83b0a9b
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x0bec1326722dd943f750fda964cb485f31469095e5b4b5c4fa02041e2f443693ac36c09f371aea7d501881fc4e369f68e071bb14c5003b31dce504bd14338eb8104
+InvalidKeyY = 0x36cd502b0a4e91e1080a5b94886a819a823687693ce878408b59183730c69e5ab2d6e05ea5f3d32855cf8c7c20da59a28913025a1fa6835a3751ec6da69502f0547
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x3e064e446ce29891240b02948288bedc37a4e4163a78702f942728e2d530cfecdc0362cf2209a706a9d4db24c1dd6aba7ad81d6ddecdf6e12073a1c31e2dacd185d
+InvalidKeyY = 0x12d0363dbdc4d157afd517beaecf2e6c93896a288c7cec5f9ba9394524fb6d4f647a9937fe440fda73f2e31410517ed5a814eed038356699085f9983f2ea5faccd0
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x164ce2e2fa873f5648c22ed37f26c13d3da3180a0f6c3aa4b68d0a13293784a5f1356fc2495217065de4f3b504ee2248747ef96180e102879363fa5393fe6fc5fbe
+InvalidKeyY = 0x23126d6903cbd7735291d77599cfe7f5e45056250c37deba2642dc0b7163ce0cf763d0d353bb9974cf15195c4bc4421bdae274492cfca739a8b8341235cc2268bc0
+
+# Point not on curve
+InvalidKeyX = 0x0dc2c4a23433293a771300ec79a3cd0f2e627110a97da85a82f4f85e7be9c280213048a3ad01b3e72bf54555a1b5da9945adcfed94ed8f6ed405c77506b5e00f45a
+InvalidKeyY = 0x18f746aacd6ed4eaaf9b038789927a30125691bc525b29592abb13cf98f64c03cb36a477dc53971563ee74f3a7614677ab6817f6e5f22ceb02c90826a33fe7c94cd
+
+# Point not on curve
+InvalidKeyX = 0x16e0383adc2986d01c18d7bde3b89eb5f732b56a6424c9394ec556a4660c3b88ddbc8654345ba6cff94bb002d16bc92e5907035f933785f633698e711738160d842
+InvalidKeyY = 0x1cf24be44e919e1576ecf51abdea113f8bb7121d670b86d8ee93ce1e6f79b17a6394987d74e6787facef5ca655196603468afd76e5cdf54ebb1331ce183cfe28c9e
+
+# InvalidKeyX or InvalidKeyY out of range
+InvalidKeyX = 0x3d68ed9ce2bcb68f12ac37385ccdb6ee445f7b0a8f257593735abdf8bc0b98bc5ab5c5750e2e111fec2ecde6be321522ddc90d2b54634d30d28f43024f76e4653a7
+InvalidKeyY = 0x03f6f5f224d6aee43d781e3ad723062a61729a6ed959cd18c75d4982961ba8033767ed1168674a545b0a693d3587fbeaebc9b116143dbe1155ead48de89d980d617
diff --git a/src/extra_tests/timing/timing-tests/data/bleichenbacher.vec b/src/tests/data/timing/bleichenbacher.vec
index 76df90e68..76df90e68 100644
--- a/src/extra_tests/timing/timing-tests/data/bleichenbacher.vec
+++ b/src/tests/data/timing/bleichenbacher.vec
diff --git a/src/extra_tests/timing/timing-tests/data/ecdsa.vec b/src/tests/data/timing/ecdsa.vec
index 579fe0d1b..579fe0d1b 100644
--- a/src/extra_tests/timing/timing-tests/data/ecdsa.vec
+++ b/src/tests/data/timing/ecdsa.vec
diff --git a/src/extra_tests/timing/timing-tests/data/lucky13sec3.vec b/src/tests/data/timing/lucky13sec3.vec
index 97b7548bc..97b7548bc 100644
--- a/src/extra_tests/timing/timing-tests/data/lucky13sec3.vec
+++ b/src/tests/data/timing/lucky13sec3.vec
diff --git a/src/extra_tests/timing/timing-tests/data/lucky13sec4sha1.vec b/src/tests/data/timing/lucky13sec4sha1.vec
index ee11ec5db..ee11ec5db 100644
--- a/src/extra_tests/timing/timing-tests/data/lucky13sec4sha1.vec
+++ b/src/tests/data/timing/lucky13sec4sha1.vec
diff --git a/src/extra_tests/timing/timing-tests/data/lucky13sec4sha256.vec b/src/tests/data/timing/lucky13sec4sha256.vec
index d542238f6..d542238f6 100644
--- a/src/extra_tests/timing/timing-tests/data/lucky13sec4sha256.vec
+++ b/src/tests/data/timing/lucky13sec4sha256.vec
diff --git a/src/extra_tests/timing/timing-tests/data/lucky13sha384.vec b/src/tests/data/timing/lucky13sha384.vec
index b5a0e2441..b5a0e2441 100644
--- a/src/extra_tests/timing/timing-tests/data/lucky13sha384.vec
+++ b/src/tests/data/timing/lucky13sha384.vec
diff --git a/src/extra_tests/timing/timing-tests/data/manger.vec b/src/tests/data/timing/manger.vec
index 1c370e0e5..1c370e0e5 100644
--- a/src/extra_tests/timing/timing-tests/data/manger.vec
+++ b/src/tests/data/timing/manger.vec
diff --git a/src/tests/test_ecdsa.cpp b/src/tests/test_ecdsa.cpp
index 90899e183..148abaacc 100644
--- a/src/tests/test_ecdsa.cpp
+++ b/src/tests/test_ecdsa.cpp
@@ -1,5 +1,6 @@
/*
* (C) 2014,2015 Jack Lloyd
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -68,8 +69,47 @@ class ECDSA_Keygen_Tests : public PK_Key_Generation_Test
std::string algo_name() const override { return "ECDSA"; }
};
+class ECDSA_Invalid_Key_Tests : public Text_Based_Test
+ {
+ public:
+ ECDSA_Invalid_Key_Tests() :
+ Text_Based_Test("pubkey/ecdsa_invalid.vec", "Group,InvalidKeyX,InvalidKeyY") {}
+
+ bool clear_between_callbacks() const override { return false; }
+
+ Test::Result run_one_test(const std::string&, const VarMap& vars) override
+ {
+ Test::Result result("ECDSA invalid keys");
+
+ const std::string group_id = get_req_str(vars, "Group");
+ Botan::EC_Group group(Botan::OIDS::lookup(group_id));
+ const Botan::BigInt x = get_req_bn(vars, "InvalidKeyX");
+ const Botan::BigInt y = get_req_bn(vars, "InvalidKeyY");
+
+ std::unique_ptr<Botan::PointGFp> public_point;
+
+ try
+ {
+ public_point.reset(new Botan::PointGFp(group.get_curve(), x, y));
+ }
+ catch(Botan::Invalid_Argument&)
+ {
+ // PointGFp() performs a range check on x, y in [0, p−1],
+ // which is also part of the EC public key checks, e.g.,
+ // in NIST SP800-56A rev2, sec. 5.6.2.3.2
+ result.test_success("public key fails check");
+ return result;
+ }
+
+ std::unique_ptr<Botan::Public_Key> key(new Botan::ECDSA_PublicKey(group, *public_point));
+ result.test_eq("public key fails check", key->check_key(Test::rng(), false), false);
+ return result;
+ }
+ };
+
BOTAN_REGISTER_TEST("ecdsa_sign", ECDSA_Signature_KAT_Tests);
BOTAN_REGISTER_TEST("ecdsa_keygen", ECDSA_Keygen_Tests);
+BOTAN_REGISTER_TEST("ecdsa_invalid", ECDSA_Invalid_Key_Tests);
#endif
diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp
index 455318ce8..bd5c38c06 100644
--- a/src/tests/test_ffi.cpp
+++ b/src/tests/test_ffi.cpp
@@ -224,7 +224,7 @@ class FFI_Unit_Tests : public Test
outstr.resize(out_len);
int rc = botan_bcrypt_generate(reinterpret_cast<uint8_t*>(&outstr[0]),
- &out_len, passphrase.c_str(), rng, 3, 0);
+ &out_len, passphrase.c_str(), rng, 4, 0);
if(rc == 0)
{
@@ -386,6 +386,7 @@ class FFI_Unit_Tests : public Test
}
std::vector<Test::Result> results;
+ results.push_back(ffi_test_mp(rng));
results.push_back(ffi_test_rsa(rng));
results.push_back(ffi_test_ecdsa(rng));
results.push_back(ffi_test_ecdh(rng));
@@ -398,6 +399,162 @@ class FFI_Unit_Tests : public Test
}
private:
+ Test::Result ffi_test_mp(botan_rng_t rng)
+ {
+ Test::Result result("FFI MP");
+
+ botan_mp_t x;
+ botan_mp_init(&x);
+ botan_mp_destroy(x);
+
+ botan_mp_init(&x);
+ size_t bn_bytes = 0;
+ TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes));
+ result.test_eq("Expected size for MP 0", bn_bytes, 0);
+
+ botan_mp_set_from_int(x, 5);
+ TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes));
+ result.test_eq("Expected size for MP 5", bn_bytes, 1);
+
+ botan_mp_set_from_int(x, 259);
+ TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes));
+ result.test_eq("Expected size for MP 259", bn_bytes, 2);
+
+ {
+ botan_mp_t zero;
+ botan_mp_init(&zero);
+ int cmp;
+ TEST_FFI_OK(botan_mp_cmp, (&cmp, x, zero));
+ result.confirm("bigint_mp_cmp(+, 0)", cmp == 1);
+
+ TEST_FFI_OK(botan_mp_cmp, (&cmp, zero, x));
+ result.confirm("bigint_mp_cmp(0, +)", cmp == -1);
+
+ TEST_FFI_OK(botan_mp_flip_sign, (x));
+
+ TEST_FFI_OK(botan_mp_cmp, (&cmp, x, zero));
+ result.confirm("bigint_mp_cmp(-, 0)", cmp == -1);
+
+ TEST_FFI_OK(botan_mp_cmp, (&cmp, zero, x));
+ result.confirm("bigint_mp_cmp(0, -)", cmp == 1);
+
+ TEST_FFI_OK(botan_mp_cmp, (&cmp, zero, zero));
+ result.confirm("bigint_mp_cmp(0, 0)", cmp == 0);
+
+ TEST_FFI_OK(botan_mp_cmp, (&cmp, x, x));
+ result.confirm("bigint_mp_cmp(x, x)", cmp == 0);
+
+ TEST_FFI_OK(botan_mp_flip_sign, (x));
+
+ botan_mp_destroy(zero);
+ }
+
+ size_t x_bits = 0;
+ TEST_FFI_OK(botan_mp_num_bits, (x, &x_bits));
+ result.test_eq("botan_mp_num_bits", x_bits, 9);
+
+ char str_buf[1024] = { 0 };
+ size_t str_len = 0;
+
+ TEST_FFI_OK(botan_mp_to_hex, (x, str_buf));
+ result.test_eq("botan_mp_to_hex", std::string(str_buf), "0103");
+
+ botan_mp_t y;
+ TEST_FFI_OK(botan_mp_init, (&y));
+ TEST_FFI_OK(botan_mp_set_from_int, (y, 0x1234567));
+
+ botan_mp_t r;
+ botan_mp_init(&r);
+
+ TEST_FFI_OK(botan_mp_add, (r, x, y));
+ str_len = sizeof(str_buf);
+ TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+ result.test_eq("botan_mp_add", std::string(str_buf), "19089002");
+
+ TEST_FFI_OK(botan_mp_mul, (r, x, y));
+ str_len = sizeof(str_buf);
+ TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+ result.test_eq("botan_mp_mul", std::string(str_buf), "4943984437");
+ TEST_FFI_RC(0, botan_mp_is_negative, (r));
+
+ botan_mp_t q;
+ botan_mp_init(&q);
+ TEST_FFI_OK(botan_mp_div, (q, r, y, x));
+
+ str_len = sizeof(str_buf);
+ TEST_FFI_OK(botan_mp_to_str, (q, 10, str_buf, &str_len));
+ result.test_eq("botan_mp_div_q", std::string(str_buf), "073701");
+
+ str_len = sizeof(str_buf);
+ TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+ result.test_eq("botan_mp_div_r", std::string(str_buf), "184");
+
+ TEST_FFI_OK(botan_mp_set_from_str, (y, "4943984437"));
+ TEST_FFI_OK(botan_mp_sub, (r, x, y));
+ str_len = sizeof(str_buf);
+ TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+ result.test_eq("botan_mp_sub", std::string(str_buf), "4943984178");
+ TEST_FFI_RC(1, botan_mp_is_negative, (r));
+
+ TEST_FFI_OK(botan_mp_lshift, (r, x, 39));
+ str_len = sizeof(str_buf);
+ TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+ result.test_eq("botan_mp_lshift", std::string(str_buf), "142386755796992");
+
+ TEST_FFI_OK(botan_mp_rshift, (r, r, 3));
+ str_len = sizeof(str_buf);
+ TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+ result.test_eq("botan_mp_rshift", std::string(str_buf), "17798344474624");
+
+ TEST_FFI_OK(botan_mp_gcd, (r, x, y));
+ str_len = sizeof(str_buf);
+ TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+ result.test_eq("botan_mp_gcd", std::string(str_buf), "259");
+
+ botan_mp_t p;
+ botan_mp_init(&p);
+ const uint8_t M127[] = { 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ TEST_FFI_OK(botan_mp_from_bin, (p, M127, sizeof(M127)));
+ TEST_FFI_RC(1, botan_mp_is_prime, (p, rng, 64));
+
+ size_t p_bits = 0;
+ TEST_FFI_OK(botan_mp_num_bits, (p, &p_bits));
+ result.test_eq("botan_mp_num_bits", p_bits, 127);
+
+ TEST_FFI_OK(botan_mp_mod_inverse, (r, x, p));
+ str_len = sizeof(str_buf);
+ TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+ result.test_eq("botan_mp_mod_inverse", std::string(str_buf), "40728777507911553541948312086427855425");
+
+ TEST_FFI_OK(botan_mp_powmod, (r, x, r, p));
+ str_len = sizeof(str_buf);
+ TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+ result.test_eq("botan_mp_powmod", std::string(str_buf), "40550417419160441638948180641668117560");
+
+ TEST_FFI_OK(botan_mp_num_bytes, (r, &bn_bytes));
+ result.test_eq("botan_mp_num_bytes", bn_bytes, 16);
+
+ std::vector<uint8_t> bn_buf;
+ bn_buf.resize(bn_bytes);
+ botan_mp_to_bin(r, bn_buf.data());
+ result.test_eq("botan_mp_to_bin", bn_buf, "1E81B9EFE0BE1902F6D03F9F5E5FB438");
+
+ TEST_FFI_OK(botan_mp_set_from_mp, (y, r));
+ TEST_FFI_OK(botan_mp_mod_mul, (r, x, y, p));
+ str_len = sizeof(str_buf);
+ 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");
+
+
+ botan_mp_destroy(p);
+ botan_mp_destroy(x);
+ botan_mp_destroy(y);
+ botan_mp_destroy(r);
+ botan_mp_destroy(q);
+
+ return result;
+ }
+
void ffi_test_pubkey_export(Test::Result& result, botan_pubkey_t pub, botan_privkey_t priv, botan_rng_t rng)
{
const size_t pbkdf_iter = 1000;
@@ -468,26 +625,89 @@ class FFI_Unit_Tests : public Test
Test::Result ffi_test_rsa(botan_rng_t rng)
{
- Test::Result result("FFI");
+ Test::Result result("FFI RSA");
botan_privkey_t priv;
if(TEST_FFI_OK(botan_privkey_create_rsa, (&priv, rng, 1024)))
{
+ 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, q, d, n, e;
+ botan_mp_init(&p);
+ botan_mp_init(&q);
+ botan_mp_init(&d);
+ botan_mp_init(&n);
+ botan_mp_init(&e);
+
+ TEST_FFI_OK(botan_privkey_rsa_get_p, (p, priv));
+ TEST_FFI_OK(botan_privkey_rsa_get_q, (q, priv));
+ TEST_FFI_OK(botan_privkey_rsa_get_d, (d, priv));
+ TEST_FFI_OK(botan_privkey_rsa_get_e, (e, priv));
+ TEST_FFI_OK(botan_privkey_rsa_get_n, (n, priv));
+
+ // Confirm same (e,n) values in public key
+ {
+ botan_mp_t pub_e, pub_n;
+ botan_mp_init(&pub_e);
+ botan_mp_init(&pub_n);
+ TEST_FFI_OK(botan_pubkey_rsa_get_e, (pub_e, pub));
+ TEST_FFI_OK(botan_pubkey_rsa_get_n, (pub_n, pub));
+
+ TEST_FFI_RC(1, botan_mp_equal, (pub_e, e));
+ TEST_FFI_RC(1, botan_mp_equal, (pub_n, n));
+ botan_mp_destroy(pub_e);
+ botan_mp_destroy(pub_n);
+ }
+
+ TEST_FFI_RC(1, botan_mp_is_prime, (p, rng, 64));
+ TEST_FFI_RC(1, botan_mp_is_prime, (q, rng, 64));
+
+ // Test p != q
+ TEST_FFI_RC(0, botan_mp_equal, (p, q));
+
+ // Test p * q == n
+ botan_mp_t x;
+ botan_mp_init(&x);
+ TEST_FFI_OK(botan_mp_mul, (x, p, q));
+
+ TEST_FFI_RC(1, botan_mp_equal, (x, n));
+ botan_mp_destroy(x);
+
+ botan_privkey_t loaded_privkey;
+ // First try loading a bogus key and verify check_key fails
+ TEST_FFI_OK(botan_privkey_load_rsa, (&loaded_privkey, n, d, q));
+ TEST_FFI_RC(-1, botan_privkey_check_key, (loaded_privkey, rng, 0));
+ botan_privkey_destroy(loaded_privkey);
+
+ TEST_FFI_OK(botan_privkey_load_rsa, (&loaded_privkey, p, q, d));
+ TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0));
+
+ botan_pubkey_t loaded_pubkey;
+ TEST_FFI_OK(botan_pubkey_load_rsa, (&loaded_pubkey, n, e));
+ TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0));
+
+ botan_mp_destroy(p);
+ botan_mp_destroy(q);
+ botan_mp_destroy(d);
+ botan_mp_destroy(e);
+ botan_mp_destroy(n);
+
char namebuf[32] = { 0 };
size_t name_len = sizeof(namebuf);
- if(TEST_FFI_OK(botan_pubkey_algo_name, (pub, namebuf, &name_len)))
+ if(TEST_FFI_OK(botan_pubkey_algo_name, (loaded_pubkey, namebuf, &name_len)))
{
result.test_eq("algo name", std::string(namebuf), "RSA");
}
botan_pk_op_encrypt_t encrypt;
- if(TEST_FFI_OK(botan_pk_op_encrypt_create, (&encrypt, pub, "OAEP(SHA-256)", 0)))
+ if(TEST_FFI_OK(botan_pk_op_encrypt_create, (&encrypt, loaded_pubkey, "OAEP(SHA-256)", 0)))
{
std::vector<uint8_t> plaintext(32);
TEST_FFI_OK(botan_rng_get, (rng, plaintext.data(), plaintext.size()));
@@ -519,7 +739,9 @@ class FFI_Unit_Tests : public Test
TEST_FFI_OK(botan_pk_op_encrypt_destroy, (encrypt));
}
+ 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));
}
@@ -528,7 +750,7 @@ class FFI_Unit_Tests : public Test
Test::Result ffi_test_ecdsa(botan_rng_t rng)
{
- Test::Result result("FFI");
+ Test::Result result("FFI ECDSA");
botan_privkey_t priv;
@@ -599,7 +821,7 @@ class FFI_Unit_Tests : public Test
Test::Result ffi_test_ecdh(botan_rng_t rng)
{
- Test::Result result("FFI");
+ Test::Result result("FFI ECDH");
botan_privkey_t priv1;
REQUIRE_FFI_OK(botan_privkey_create_ecdh, (&priv1, rng, "secp256r1"));
@@ -662,7 +884,7 @@ class FFI_Unit_Tests : public Test
Test::Result ffi_test_mceliece(botan_rng_t rng)
{
- Test::Result result("FFI");
+ Test::Result result("FFI McEliece");
botan_privkey_t priv;
#if defined(BOTAN_HAS_MCELIECE)
diff --git a/src/tests/test_filters.cpp b/src/tests/test_filters.cpp
index 8fe851c7a..ee3e68c1a 100644
--- a/src/tests/test_filters.cpp
+++ b/src/tests/test_filters.cpp
@@ -1,6 +1,7 @@
/*
* (C) 2016 Daniel Neus
* 2016 Jack Lloyd
+* 2017 René Korthaus
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -16,6 +17,11 @@
#include <botan/cipher_filter.h>
#endif
+#if defined(BOTAN_HAS_CODEC_FILTERS)
+ #include <botan/hex_filt.h>
+ #include <botan/b64_filt.h>
+#endif
+
namespace Botan_Tests {
#if defined(BOTAN_HAS_FILTERS)
@@ -39,10 +45,7 @@ class Filter_Tests : public Test
results.push_back(test_pipe_codec());
results.push_back(test_fork());
results.push_back(test_chain());
-
-#if defined(BOTAN_TARGET_OS_HAS_THREADS)
results.push_back(test_threaded_fork());
-#endif
return results;
}
@@ -86,6 +89,8 @@ class Filter_Tests : public Test
Test::Result test_data_src_sink()
{
Test::Result result("DataSink");
+
+#if defined(BOTAN_HAS_CODEC_FILTERS)
std::ostringstream oss;
Botan::Pipe pipe(new Botan::Hex_Decoder, new Botan::DataSink_Stream(oss));
@@ -105,7 +110,7 @@ class Filter_Tests : public Test
pipe.process_msg(input_strm);
result.test_eq("output string", oss.str(), "efghAAAACC");
-
+#endif
return result;
}
@@ -113,6 +118,7 @@ class Filter_Tests : public Test
{
Test::Result result("Pipe I/O operators");
+#if defined(BOTAN_HAS_CODEC_FILTERS)
Botan::Pipe pipe(new Botan::Hex_Encoder);
pipe.process_msg("ABCD");
@@ -129,6 +135,8 @@ class Filter_Tests : public Test
pipe.set_default_msg(1);
oss << pipe;
result.test_eq("output string2", oss.str(), "4142434441414141");
+#endif
+
return result;
}
@@ -170,6 +178,8 @@ class Filter_Tests : public Test
Test::Result test_pipe_mac()
{
Test::Result result("Pipe");
+
+#if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_HMAC) && defined(BOTAN_HAS_SHA2_32)
const Botan::SymmetricKey key("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
Botan::Pipe pipe(new Botan::MAC_Filter("HMAC(SHA-256)", key, 12),
new Botan::Base64_Encoder);
@@ -181,13 +191,15 @@ class Filter_Tests : public Test
result.test_eq("MAC 1", pipe.read_all_as_string(0), "e7NoVbtudgU0QiCZ");
result.test_eq("MAC 2", pipe.read_all_as_string(1), "LhPnfEG+0rk+Ej6y");
result.test_eq("MAC 3", pipe.read_all_as_string(2), "e7NoVbtudgU0QiCZ");
-
+#endif
return result;
}
Test::Result test_pipe_hash()
{
Test::Result result("Pipe");
+
+#if defined(BOTAN_HAS_SHA2_32)
Botan::Pipe pipe(new Botan::Hash_Filter("SHA-224"));
pipe.pop();
pipe.append(new Botan::Hash_Filter("SHA-256"));
@@ -231,7 +243,7 @@ class Filter_Tests : public Test
pipe.process_msg(std::vector<uint8_t>(1024, 0));
result.test_eq("Expected CRC32d", pipe.read_all(1), "99841F60");
#endif
-
+#endif
return result;
}
@@ -239,6 +251,7 @@ class Filter_Tests : public Test
{
Test::Result result("Pipe");
+#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC) && defined(BOTAN_HAS_CIPHER_MODE_PADDING)
Botan::Cipher_Mode_Filter* cipher =
new Botan::Cipher_Mode_Filter(Botan::get_cipher_mode("AES-128/CBC/PKCS7", Botan::ENCRYPTION));
@@ -284,6 +297,7 @@ class Filter_Tests : public Test
result.test_eq("Bytes read", pipe.get_bytes_read(), zeros_out.size());
result.test_eq("Cipher roundtrip", zeros_in, zeros_out);
+#endif
return result;
}
@@ -326,6 +340,7 @@ class Filter_Tests : public Test
{
Test::Result result("Pipe");
+#if defined(BOTAN_HAS_CODEC_FILTERS)
Botan::Pipe pipe(new Botan::Base64_Encoder);
result.test_eq("Message count", pipe.message_count(), 0);
@@ -391,6 +406,7 @@ class Filter_Tests : public Test
pipe.process_msg("hex encoding this string");
result.test_eq("hex lowercase with linebreaks", pipe.read_all_as_string(8),
"68657820656e636f\n64696e6720746869\n7320737472696e67\n");
+#endif
return result;
}
@@ -399,6 +415,7 @@ class Filter_Tests : public Test
{
Test::Result result("Pipe");
+#if defined(BOTAN_HAS_CTR_BE)
Botan::Keyed_Filter* aes = nullptr;
const Botan::SymmetricKey some_other_key("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE");
const Botan::SymmetricKey key("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
@@ -412,7 +429,7 @@ class Filter_Tests : public Test
result.test_eq("Message count", pipe.message_count(), 1);
result.test_eq("Ciphertext", pipe.read_all(), "FDFD6238F7C6");
-
+#endif
return result;
}
@@ -420,6 +437,7 @@ class Filter_Tests : public Test
{
Test::Result result("Filter Fork");
+#if defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64)
Botan::Pipe pipe(new Botan::Fork(new Botan::Hash_Filter("SHA-256"),
new Botan::Hash_Filter("SHA-512-256")));
@@ -430,7 +448,7 @@ class Filter_Tests : public Test
// Test reading out of order
result.test_eq("Hash 2", pipe.read_all(1), "610480FFA82F24F6926544B976FE387878E3D973C03DFD591C2E9896EFB903E0");
result.test_eq("Hash 1", pipe.read_all(0), "C00862D1C6C1CF7C1B49388306E7B3C1BB79D8D6EC978B41035B556DBB3797DF");
-
+#endif
return result;
}
@@ -438,6 +456,7 @@ class Filter_Tests : public Test
{
Test::Result result("Filter Chain");
+#if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64)
std::unique_ptr<Botan::Fork> fork(
new Botan::Fork(
new Botan::Chain(new Botan::Hash_Filter("SHA-256"), new Botan::Hex_Encoder),
@@ -453,15 +472,17 @@ class Filter_Tests : public Test
result.test_eq("Hash 1", pipe.read_all_as_string(0), "C00862D1C6C1CF7C1B49388306E7B3C1BB79D8D6EC978B41035B556DBB3797DF");
result.test_eq("Hash 2", pipe.read_all_as_string(1), "610480FFA82F24F6926544B976FE387878E3D973C03DFD591C2E9896EFB903E0");
+#endif
return result;
}
-#if defined(BOTAN_TARGET_OS_HAS_THREADS)
+
Test::Result test_threaded_fork()
{
- Test::Result result("Threaded_Fork");
+ Test::Result result("Threaded_Fork");
+#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_SHA2_32)
Botan::Pipe pipe(new Botan::Threaded_Fork(new Botan::Hex_Encoder,
new Botan::Base64_Encoder));
@@ -497,10 +518,9 @@ class Filter_Tests : public Test
result.test_eq("Output " + std::to_string(i),
pipe.read_all(2+i),
"327AD8055223F5926693D8BEA40F7B35BDEEB535647DFB93F464E40EA01939A9");
-
+#endif
return result;
}
-#endif
};
diff --git a/src/tests/test_name_constraint.cpp b/src/tests/test_name_constraint.cpp
index 95cb9f229..11336bb1f 100644
--- a/src/tests/test_name_constraint.cpp
+++ b/src/tests/test_name_constraint.cpp
@@ -8,7 +8,7 @@
#if defined(BOTAN_HAS_X509_CERTIFICATES)
#include <botan/x509path.h>
- #include <botan/internal/filesystem.h>
+ #include <botan/calendar.h>
#endif
#include <algorithm>
@@ -65,6 +65,9 @@ class Name_Constraint_Tests : public Test
std::vector<Test::Result> results;
const Botan::Path_Validation_Restrictions restrictions(false, 80);
+ std::chrono::system_clock::time_point validation_time =
+ Botan::calendar_point(2016,10,21,4,20,0).to_std_timepoint();
+
for(const auto& t: test_cases)
{
Botan::X509_Certificate root(Test::data_file("name_constraint/" + std::get<0>(t)));
@@ -74,7 +77,8 @@ class Name_Constraint_Tests : public Test
trusted.add_certificate(root);
Botan::Path_Validation_Result path_result = Botan::x509_path_validate(
- sub, restrictions, trusted, std::get<2>(t), Botan::Usage_Type::TLS_SERVER_AUTH);
+ sub, restrictions, trusted, std::get<2>(t), Botan::Usage_Type::TLS_SERVER_AUTH,
+ validation_time);
if(path_result.successful_validation() && path_result.trust_root() != root)
path_result = Botan::Path_Validation_Result(Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
diff --git a/src/tests/test_ocsp.cpp b/src/tests/test_ocsp.cpp
index 41faa5edf..587977149 100644
--- a/src/tests/test_ocsp.cpp
+++ b/src/tests/test_ocsp.cpp
@@ -98,11 +98,17 @@ class OCSP_Tests : public Test
result.test_success("Bad arguments rejected");
}
- const Botan::OCSP::Request req(issuer, end_entity);
+
const std::string expected_request = "ME4wTKADAgEAMEUwQzBBMAkGBSsOAwIaBQAEFPLgavmFih2NcJtJGSN6qbUaKH5kBBRK3QYWG7z2aLV29YG2u2IaulqBLwIIQkg+DF+RYMY=";
+ const Botan::OCSP::Request req1(issuer, end_entity);
result.test_eq("Encoded OCSP request",
- req.base64_encode(),
+ req1.base64_encode(),
+ expected_request);
+
+ const Botan::OCSP::Request req2(issuer, BigInt::decode(end_entity.serial_number()));
+ result.test_eq("Encoded OCSP request",
+ req2.base64_encode(),
expected_request);
return result;
@@ -148,6 +154,7 @@ class OCSP_Tests : public Test
{
Test::Result result("OCSP online check");
+ // Expired end-entity certificate:
std::shared_ptr<const Botan::X509_Certificate> ee = load_test_X509_cert("ocsp/randombit.pem");
std::shared_ptr<const Botan::X509_Certificate> ca = load_test_X509_cert("ocsp/letsencrypt.pem");
std::shared_ptr<const Botan::X509_Certificate> trust_root = load_test_X509_cert("ocsp/identrust.pem");
@@ -168,7 +175,7 @@ class OCSP_Tests : public Test
{
if(result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1))
{
- result.confirm("Status good", ocsp_status[0].count(Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD));
+ result.confirm("Status expired", ocsp_status[0].count(Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED));
}
if(result.test_eq("Expected size of ocsp_status[1]", ocsp_status[1].size(), 1))
{
diff --git a/src/tests/test_os_utils.cpp b/src/tests/test_os_utils.cpp
index 58858a4c2..5153338d7 100644
--- a/src/tests/test_os_utils.cpp
+++ b/src/tests/test_os_utils.cpp
@@ -7,6 +7,7 @@
#include "tests.h"
#include <botan/internal/os_utils.h>
+#include <thread>
// For __ud2 intrinsic
#if defined(BOTAN_TARGET_COMPILER_IS_MSVC)
@@ -72,7 +73,7 @@ class OS_Utils_Tests : public Test
const uint64_t proc_ts1 = Botan::OS::get_processor_timestamp();
// do something that consumes a little time
- Botan::OS::get_process_id();
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
uint64_t proc_ts2 = Botan::OS::get_processor_timestamp();
@@ -146,10 +147,6 @@ class OS_Utils_Tests : public Test
result.confirm("Correct result returned by working probe fn", run_rc == 5);
- std::function<int ()> throw_fn = []() -> int { throw 3.14159; return 5; };
- const int throw_rc = Botan::OS::run_cpu_instruction_probe(throw_fn);
- result.confirm("Error return if probe function threw exception", throw_rc < 0);
-
std::function<int ()> crash_probe;
#if defined(BOTAN_TARGET_COMPILER_IS_MSVC)
diff --git a/src/tests/test_passhash.cpp b/src/tests/test_passhash.cpp
index 126b68780..05f53780a 100644
--- a/src/tests/test_passhash.cpp
+++ b/src/tests/test_passhash.cpp
@@ -36,9 +36,8 @@ class Bcrypt_Tests : public Text_Based_Test
Test::Result result("bcrypt");
result.test_eq("correct hash accepted", Botan::check_bcrypt(password, passhash), true);
- const size_t max_level = (Test::run_long_tests() ? 14 : 11);
-
- for(size_t level = 1; level <= max_level; ++level)
+ // self-test low levels for each test password
+ for(size_t level = 4; level <= 6; ++level)
{
const std::string gen_hash = generate_bcrypt(password, Test::rng(), level);
result.test_eq("generated hash accepted", Botan::check_bcrypt(password, gen_hash), true);
@@ -46,6 +45,27 @@ class Bcrypt_Tests : public Text_Based_Test
return result;
}
+
+ std::vector<Test::Result> run_final_tests()
+ {
+ Test::Result result("bcrypt");
+
+ uint64_t start = Test::timestamp();
+
+ const std::string password = "ag00d1_2BE5ur3";
+
+ const size_t max_level = (Test::run_long_tests() ? 15 : 10);
+
+ for(size_t level = 4; level <= max_level; ++level)
+ {
+ const std::string gen_hash = generate_bcrypt(password, Test::rng(), level);
+ result.test_eq("generated hash accepted", Botan::check_bcrypt(password, gen_hash), true);
+ }
+
+ result.set_ns_consumed(Test::timestamp() - start);
+
+ return {result};
+ }
};
BOTAN_REGISTER_TEST("bcrypt", Bcrypt_Tests);
diff --git a/src/tests/test_utils.cpp b/src/tests/test_utils.cpp
index ae9cf72dd..a2601722d 100644
--- a/src/tests/test_utils.cpp
+++ b/src/tests/test_utils.cpp
@@ -7,6 +7,7 @@
#include "tests.h"
#include <functional>
+#include <ctime>
#include <botan/loadstor.h>
#include <botan/calendar.h>
#include <botan/internal/rounding.h>
@@ -201,7 +202,7 @@ class Date_Format_Tests : public Text_Based_Test
const std::vector<uint32_t> d = parse_date(get_req_str(vars, "Date"));
- if(type == "valid" || type == "valid.not_std")
+ if(type == "valid" || type == "valid.not_std" || type == "valid.64_bit_time_t")
{
Botan::calendar_point c(d[0], d[1], d[2], d[3], d[4], d[5]);
result.test_is_eq("year", c.year, d[0]);
@@ -211,7 +212,7 @@ class Date_Format_Tests : public Text_Based_Test
result.test_is_eq("minute", c.minutes, d[4]);
result.test_is_eq("second", c.seconds, d[5]);
- if(type == "valid.not_std")
+ if(type == "valid.not_std" || (type == "valid.64_bit_time_t" && c.year > 2037 && sizeof(std::time_t) == 4))
{
result.test_throws("valid but out of std::timepoint range", [c]() { c.to_std_timepoint(); });
}
diff --git a/src/tests/unit_ecc.cpp b/src/tests/unit_ecc.cpp
index 3ab22e57e..1672c289d 100644
--- a/src/tests/unit_ecc.cpp
+++ b/src/tests/unit_ecc.cpp
@@ -18,6 +18,8 @@
#include <botan/reducer.h>
#include <botan/oids.h>
#include <botan/hex.h>
+ #include <botan/data_src.h>
+ #include <botan/x509_key.h>
#endif
namespace Botan_Tests {
@@ -833,6 +835,31 @@ class ECC_Unit_Tests : public Test
BOTAN_REGISTER_TEST("ecc_unit", ECC_Unit_Tests);
+class ECC_Invalid_Key_Tests : public Text_Based_Test
+ {
+ public:
+ ECC_Invalid_Key_Tests() :
+ Text_Based_Test("pubkey/ecc_invalid.vec", "SubjectPublicKey") {}
+
+ bool clear_between_callbacks() const override { return false; }
+
+ Test::Result run_one_test(const std::string&, const VarMap& vars) override
+ {
+ Test::Result result("ECC invalid keys");
+
+ const std::string encoded = get_req_str(vars, "SubjectPublicKey");
+ Botan::DataSource_Memory key_data(Botan::hex_decode(encoded));
+
+ std::unique_ptr<Botan::Public_Key> key(Botan::X509::load_key(key_data));
+ result.test_eq("public key fails check", key->check_key(Test::rng(), false), false);
+
+
+ return result;
+ }
+ };
+
+BOTAN_REGISTER_TEST("ecc_invalid", ECC_Invalid_Key_Tests);
+
#endif
}
diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp
index 933ccfe39..77aebce93 100644
--- a/src/tests/unit_tls.cpp
+++ b/src/tests/unit_tls.cpp
@@ -57,7 +57,7 @@ class Credentials_Manager_Test : public Botan::Credentials_Manager
const Botan::X509_Certificate* dsa_cert,
Botan::Private_Key* dsa_key,
const Botan::X509_Certificate* dsa_ca,
- const Botan::X509_CRL* dsa_crl) :
+ Botan::X509_CRL* dsa_crl) :
m_rsa_cert(rsa_cert),
m_rsa_ca(rsa_ca),
m_rsa_key(rsa_key),
@@ -66,7 +66,8 @@ class Credentials_Manager_Test : public Botan::Credentials_Manager
m_ecdsa_key(ecdsa_key),
m_dsa_cert(dsa_cert),
m_dsa_ca(dsa_ca),
- m_dsa_key(dsa_key)
+ m_dsa_key(dsa_key),
+ m_dsa_crl(dsa_crl)
{
std::unique_ptr<Botan::Certificate_Store_In_Memory> store(new Botan::Certificate_Store_In_Memory);
store->add_certificate(m_rsa_ca);
@@ -78,9 +79,9 @@ class Credentials_Manager_Test : public Botan::Credentials_Manager
{
store->add_certificate(*m_dsa_ca);
}
- if(dsa_crl != nullptr)
+ if(m_dsa_crl != nullptr)
{
- store->add_crl(*dsa_crl);
+ store->add_crl(*m_dsa_crl);
}
m_stores.push_back(std::move(store));
@@ -176,6 +177,7 @@ class Credentials_Manager_Test : public Botan::Credentials_Manager
std::unique_ptr<const Botan::X509_Certificate> m_dsa_cert, m_dsa_ca;
std::unique_ptr<Botan::Private_Key> m_dsa_key;
+ std::unique_ptr<Botan::X509_CRL> m_dsa_crl;
std::vector<std::unique_ptr<Botan::Certificate_Store>> m_stores;
bool m_provides_client_certs;
};
diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp
index b51914ee8..e23017738 100644
--- a/src/tests/unit_x509.cpp
+++ b/src/tests/unit_x509.cpp
@@ -57,8 +57,8 @@ Botan::X509_Cert_Options req_opts1(const std::string& algo)
opts.dns = "botan.randombit.net";
opts.email = "[email protected]";
- opts.not_before("1601012000Z");
- opts.not_after("3001012000Z");
+ opts.not_before("160101200000Z");
+ opts.not_after("300101200000Z");
if(algo == "RSA")
{
@@ -170,23 +170,21 @@ Test::Result test_x509_dates()
Botan::X509_Time time;
result.confirm("unset time not set", !time.time_is_set());
- time = Botan::X509_Time("0802011822Z", Botan::ASN1_Tag::UTC_TIME);
+ time = Botan::X509_Time("080201182200Z", Botan::ASN1_Tag::UTC_TIME);
result.confirm("time set after construction", time.time_is_set());
result.test_eq("time readable_string", time.readable_string(), "2008/02/01 18:22:00 UTC");
- const std::vector<std::string> valid = {
- "0802010000Z",
- "0802011724Z",
- "0406142334Z",
- "9906142334Z",
- "0006142334Z",
+ time = Botan::X509_Time("200305100350Z", Botan::ASN1_Tag::UTC_TIME);
+ result.test_eq("UTC_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
- "080201000000Z",
- "080201172412Z",
- "040614233433Z",
- "990614233444Z",
- "000614233455Z",
- };
+ time = Botan::X509_Time("200305100350Z", Botan::ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+ result.test_eq("UTC_OR_GENERALIZED_TIME from UTC_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
+
+ time = Botan::X509_Time("20200305100350Z", Botan::ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+ result.test_eq("UTC_OR_GENERALIZED_TIME from GENERALIZED_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
+
+ time = Botan::X509_Time("20200305100350Z", Botan::ASN1_Tag::GENERALIZED_TIME);
+ result.test_eq("GENERALIZED_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
// Dates that are valid per X.500 but rejected as unsupported
const std::vector<std::string> valid_but_unsup = {
@@ -205,7 +203,16 @@ Test::Result test_x509_dates()
"000614233455+0530",
};
- const std::vector<std::string> invalid = {
+ // valid length 13
+ const std::vector<std::string> valid_utc = {
+ "080201000000Z",
+ "080201172412Z",
+ "040614233433Z",
+ "990614233444Z",
+ "000614233455Z",
+ };
+
+ const std::vector<std::string> invalid_utc = {
"",
" ",
"2008`02-01",
@@ -213,6 +220,13 @@ Test::Result test_x509_dates()
"2000-02-01 17",
"999921",
+ // No seconds
+ "0802010000Z",
+ "0802011724Z",
+ "0406142334Z",
+ "9906142334Z",
+ "0006142334Z",
+
// valid length 13 -> range check
"080201000061Z", // seconds too big (61)
"080201000060Z", // seconds too big (60, leap seconds not covered by the standard)
@@ -247,25 +261,84 @@ Test::Result test_x509_dates()
"2\n2211221122Z",
// wrong time zone
- "0802010000",
- "0802010000z"
+ "080201000000",
+ "080201000000z",
+
+ // Fractional seconds
+ "170217180154.001Z",
+
+ // Timezone offset
+ "170217180154+0100",
+
+ // Extra digits
+ "17021718015400Z",
+
+ // Non-digits
+ "17021718015aZ",
+
+ // Trailing garbage
+ "170217180154Zlongtrailinggarbage",
+
+ // Swapped type
+ "20170217180154Z",
+ };
+
+ // valid length 15
+ const std::vector<std::string> valid_generalized_time = {
+ "20000305100350Z",
+ };
+
+ const std::vector<std::string> invalid_generalized = {
+ // No trailing Z
+ "20000305100350",
+
+ // No seconds
+ "200003051003Z",
+
+ // Fractional seconds
+ "20000305100350.001Z",
+
+ // Timezone offset
+ "20170217180154+0100",
+
+ // Extra digits
+ "2017021718015400Z",
+
+ // Non-digits
+ "2017021718015aZ",
+
+ // Trailing garbage
+ "20170217180154Zlongtrailinggarbage",
+
+ // Swapped type
+ "170217180154Z",
};
- for(auto&& v : valid)
+ for(auto&& v : valid_but_unsup)
+ {
+ result.test_throws("valid but unsupported", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); });
+ }
+
+ for(auto&& v : valid_utc)
{
Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME);
}
- for(auto&& v : valid_but_unsup)
+ for(auto&& v : valid_generalized_time)
{
- result.test_throws("valid but unsupported", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); });
+ Botan::X509_Time t(v, Botan::ASN1_Tag::GENERALIZED_TIME);
}
- for(auto&& v : invalid)
+ for(auto&& v : invalid_utc)
{
result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); });
}
+ for (auto&& v : invalid_generalized)
+ {
+ result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::GENERALIZED_TIME); });
+ }
+
return result;
}