diff options
41 files changed, 977 insertions, 605 deletions
diff --git a/doc/hacking.rst b/doc/hacking.rst index 5f7d34e76..7c7ae3fb2 100644 --- a/doc/hacking.rst +++ b/doc/hacking.rst @@ -1,50 +1,49 @@ - Source Code Layout ================================================= -Under `src` there are directories +Under ``src`` there are directories -* `lib` is the library itself, more on that below -* `cli` is the command line application `botan` -* `tests` contain what you would expect. Input files go under `tests/data`. -* `build-data` contains files read by the configure script. For - example `build-data/cc/gcc.txt` describes various gcc options. -* `scripts` contains misc scripts: install, distribution, various - codegen things. Scripts controlling CI go under `scripts/ci`. -* `python/botan.py` is the Python ctypes wrapper +* ``lib`` is the library itself, more on that below +* ``cli`` is the command line application ``botan`` +* ``tests`` contain what you would expect. Input files go under ``tests/data``. +* ``build-data`` contains files read by the configure script. For + example ``build-data/cc/gcc.txt`` describes various gcc options. +* ``scripts`` contains misc scripts: install, distribution, various + codegen things. Scripts controlling CI go under ``scripts/ci``. +* ``python/botan.py`` is the Python ctypes wrapper Library Layout ======================================== -* `base` defines some high level types -* `utils` contains various utility functions and types -* `codec` has hex, base64 -* `block` contains the block cipher implementations -* `modes` contains block cipher modes (CBC, GCM, etc) -* `stream` contains the stream ciphers -* `hash` contains the hash function implementations -* `passhash` contains password hashing algorithms for authentication -* `kdf` contains the key derivation functions -* `mac` contains the message authentication codes -* `pbkdf` contains password hashing algorithms for key derivation -* `math` is the math library for public key operations. It is divided into - four parts: `mp` which are the low level algorithms; `bigint` which is - a C++ wrapper around `mp`; `numbertheory` which contains algorithms like - primality testing and exponentiation; and `ec_gfp` which defines elliptic +* ``base`` defines some high level types +* ``utils`` contains various utility functions and types +* ``codec`` has hex, base64 +* ``block`` contains the block cipher implementations +* ``modes`` contains block cipher modes (CBC, GCM, etc) +* ``stream`` contains the stream ciphers +* ``hash`` contains the hash function implementations +* ``passhash`` contains password hashing algorithms for authentication +* ``kdf`` contains the key derivation functions +* ``mac`` contains the message authentication codes +* ``pbkdf`` contains password hashing algorithms for key derivation +* ``math`` is the math library for public key operations. It is divided into + four parts: ``mp`` which are the low level algorithms; ``bigint`` which is + a C++ wrapper around ``mp``; ``numbertheory`` which contains algorithms like + primality testing and exponentiation; and ``ec_gfp`` which defines elliptic curves over prime fields. -* `pubkey` contains the public key implementations -* `pk_pad` contains padding schemes for public key algorithms -* `rng` contains the random number generators -* `entropy` has various entropy sources -* `asn1` is the DER encoder/decoder -* `cert` has `x509` (X.509 PKI OCSP is also here) and `cvc` (Card Verifiable Ceritifcates, +* ``pubkey`` contains the public key implementations +* ``pk_pad`` contains padding schemes for public key algorithms +* ``rng`` contains the random number generators +* ``entropy`` has various entropy sources +* ``asn1`` is the DER encoder/decoder +* ``cert`` has ``x509`` (X.509 PKI OCSP is also here) and ``cvc`` (Card Verifiable Ceritifcates, for ePassports) -* `tls` contains the TLS implementation -* `filters` is a filter/pipe API for data transforms -* `compression` has the compression wrappers (zlib, bzip2, lzma) -* `ffi` is the C99 API -* `prov` contains bindings to external libraries like OpenSSL -* `misc` contains odds and ends: format preserving encryption, SRP, threshold +* ``tls`` contains the TLS implementation +* ``filters`` is a filter/pipe API for data transforms +* ``compression`` has the compression wrappers (zlib, bzip2, lzma) +* ``ffi`` is the C99 API +* ``prov`` contains bindings to external libraries like OpenSSL +* ``misc`` contains odds and ends: format preserving encryption, SRP, threshold secret sharing, all or nothing transform, and others Copyright Notice @@ -66,7 +65,7 @@ change are consecutive, avoid year ranges: specify each year separated by a comma. Also if you are a new contributor or making an addition in a new year, -include an update to `doc/license.txt` in your PR. +include an update to ``doc/license.txt`` in your PR. Style Conventions ======================================== @@ -84,32 +83,35 @@ some code calling your idealized API, then just implement that API. This can often help avoid cut-and-paste by creating the correct abstractions needed to solve the problem at hand. -The C++11 `auto` keyword is very convenient but only use it when the +The C++11 ``auto`` keyword is very convenient but only use it when the type truly is obvious (considering also the potential for unexpected integer conversions and the like, such as an apparent uint8_t being promoted to an int). -If a variable is defined and not modified, declare it `const` +If a variable is defined and not modified, declare it ``const``. +Some exception for very short-lived variables, but generally speaking +being able to read the declaration and know it will not be modified +is useful. -Use `override` annotations whenever overriding a virtual function. +Use ``override`` annotations whenever overriding a virtual function. A formatting setup for emacs is included in `scripts/indent.el` but the basic formatting style should be obvious. No tabs, and remove trailing whitespace. -Use m_ prefix on all member variables. The current code is not +Use ``m_`` prefix on all member variables. The current code is not consistent but all new code should use it. Prefer using braces on both sides of if/else blocks, even if only using a single statement. Again the current code doesn't always do this. -Avoid `using namespace` declarations, even inside of single functions. -One allowed exception is `using namespace std::placeholders` in -functions which use `std::bind`. +Avoid ``using namespace`` declarations, even inside of single functions. +One allowed exception is ``using namespace std::placeholders`` in +functions which use ``std::bind``. -Use `::` to explicitly refer to the global namespace (eg, when calling -an OS or library function like `::select` or `::sqlite3_open`). +Use ``::`` to explicitly refer to the global namespace (eg, when calling +an OS or library function like ``::select`` or ``::sqlite3_open``). Sending patches ======================================== @@ -120,16 +122,16 @@ change email the mailing list or open a discussion ticket on github before starting out to make sure you are on the right path. Depending on what your change is, your PR should probably also include -an update to `doc/news.rst` with a note explaining the change. If your +an update to ``doc/news.rst`` with a note explaining the change. If your change is a simple bug fix, a one sentence description is perhaps sufficient. If there is an existing ticket on GitHub with discussion or other information, reference it in your change note as 'GH #000'. -Update `doc/credits.txt` with your information so people know what +Update ``doc/credits.txt`` with your information so people know what you did! (This is optional) If you are interested in contributing but don't know where to start -check out `doc/todo.rst` for some ideas - these are changes we would +check out ``doc/todo.rst`` for some ideas - these are changes we would almost certainly accept once they've passed code review. Also, try building and testing it on whatever hardware you have handy, @@ -218,19 +220,34 @@ developers or auditors. Python ======================================== -The house language for scripts is Python. The aim is to support 2.7 and latest -3.x with the minimum possible number of explicit version checks (ideally zero). -Support for CPython 2.6, PyPy, etc is a bonus but not required. +Scripts should be in Python whenever possible. + +For configure.py (and install.py) the target is stock (no modules outside the +standard library) CPython 2.7 plus latest CPython 3.x. Support for CPython 2.6, +PyPy, etc is great when viable (in the sense of not causing problems for 2.7 or +3.x, and not requiring huge blocks of version dependent code). As running this +program succesfully is required for a working build making it as portable as +possible is considered key. + +The python wrapper botan.py targets CPython 2.7, 3.x, and latest PyPy. Note that +a single file is used to avoid dealing with any of Python's various crazy module +distribution issues. + +For random scripts not typically run by an end-user (codegen, visualization, and +so on) there isn't any need to worry about 2.6 and even just running under +Python2 xor Python3 is acceptable if needed. Here it's fine to depend on any +useful modules such as graphviz or matplotlib, regardless if it is available +from a stock CPython install. Build Tools and Hints ======================================== If you don't already use it for all your C/C++ development, install -`ccache` now and configure a large cache on a fast disk. It allows for +``ccache`` now and configure a large cache on a fast disk. It allows for very quick rebuilds by caching the compiler output. -Use `--with-sanitizers` to enable ASan. UBSan has to be added separately -with --cc-abi-flags at the moment as GCC 4.8 does not have UBSan. +Use ``--with-sanitizers`` to enable ASan. UBSan has to be added separately +with ``--cc-abi-flags`` at the moment as GCC 4.8 does not have UBSan. Other Ways You Can Help ======================================== diff --git a/doc/news.rst b/doc/news.rst index 39753a752..e340033c4 100644 --- a/doc/news.rst +++ b/doc/news.rst @@ -4,6 +4,11 @@ Release Notes Version 1.11.26, Not Yet Released ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* Deprecation warning: Nyberg-Rueppel signatures, MARS, RC2, RC5, RC6, + SAFER, HAS-160, RIPEMD-128, and MD2 are being considered for removal + in a future release. If there is a compelling use case for keeping + any of them in the library, please open a discussion ticket on GitHub. + * Root all exceptions thrown by the library in the `Botan::Exception` class. Previously the library would in many cases throw `std::runtime_error` or `std::invalid_argument` exceptions which would make it hard to determine @@ -21,6 +26,22 @@ Version 1.11.26, Not Yet Released random number generation, RSA key generation, and signing are supported. Tested using Trousers and an ST TPM +* Add generalized interface for KEM (key encapsulation) techniques. Convert + McEliece KEM to use it. The previous interfaces McEliece_KEM_Encryptor and + McEliece_KEM_Decryptor have been removed. The new KEM interface now uses a KDF + to hash the resulting keys; to get the same output as previously provided by + McEliece_KEM_Encryptor, use "KDF1(SHA-512)" and request exactly 64 bytes. + +* Add support for RSA-KEM from ISO 18033-2 + +* Avoid calling memcpy, memset, or memmove with a length of zero to + avoid undefined behavior, as calling these functions with an invalid + or null pointer, even with a length of zero, is invalid. Often there + are corner cases where this can occur, such as pointing to the very + end of a buffer. + +* Export MGF1 function mgf1_mask GH #380 + Version 1.11.25, 2015-12-07 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/build-data/cc/clang.txt b/src/build-data/cc/clang.txt index b290bbc13..3382f5d65 100644 --- a/src/build-data/cc/clang.txt +++ b/src/build-data/cc/clang.txt @@ -16,6 +16,7 @@ maintainer_warning_flags "-Qunused-arguments -Werror -Wno-error=unused-parameter compile_flags "-c" debug_info_flags "-g" optimization_flags "-O3" +sanitizer_flags "-fsanitize=address,undefined -fsanitize-coverage=edge,indirect-calls,8bit-counters -fno-sanitize-recover=undefined" shared_flags "-fPIC" coverage_flags "--coverage" diff --git a/src/cli/asn1.cpp b/src/cli/asn1.cpp index 8096f2d19..957fa1f24 100644 --- a/src/cli/asn1.cpp +++ b/src/cli/asn1.cpp @@ -147,6 +147,9 @@ std::string type_name(Botan::ASN1_Tag type) case Botan::BOOLEAN: return "BOOLEAN"; + + default: + return "TAG(" + std::to_string(static_cast<size_t>(type)) + ")"; } return "(UNKNOWN)"; diff --git a/src/cli/bench.cpp b/src/cli/bench.cpp index d2688421c..ba498d767 100644 --- a/src/cli/bench.cpp +++ b/src/cli/bench.cpp @@ -132,16 +132,6 @@ class Timer uint64_t m_event_count = 0, m_event_mult = 0; }; -inline bool operator<(const Timer& x, const Timer& y) - { - return (x.get_name() < y.get_name()); - } - -inline bool operator==(const Timer& x, const Timer& y) - { - return (x.get_name() == y.get_name()); - } - std::ostream& operator<<(std::ostream& out, Timer& timer) { const double events_per_second = timer.events() / timer.seconds(); diff --git a/src/cli/cli.h b/src/cli/cli.h index df7e345f5..85d6770ba 100644 --- a/src/cli/cli.h +++ b/src/cli/cli.h @@ -106,6 +106,7 @@ class Command // for checking all spec strings at load time //parse_spec(); } + virtual ~Command() = default; int run(const std::vector<std::string>& params) { @@ -439,31 +440,9 @@ class Command } template<typename Alloc> - void write_output_file(const std::string& who, - const std::vector<uint8_t, Alloc>& vec) const + void write_output(const std::vector<uint8_t, Alloc>& vec) { - write_output_file(who, vec.begin(), vec.size()); - } - - void write_output_file(const std::string& output_file, - const uint8_t buf[], size_t buf_len) const - { - std::ofstream out(output_file, std::ios::binary); - if(!out.good()) - throw CLI_IO_Error("writing", output_file); - - out.write(reinterpret_cast<const char*>(buf), buf_len); - out.close(); - } - - void write_output_file(const std::string& output_file, const std::string& outstr) const - { - std::ofstream out(output_file); - if(!out.good()) - throw CLI_IO_Error("writing", output_file); - - out.write(outstr.data(), outstr.size()); - out.close(); + output().write(reinterpret_cast<const char*>(vec.data()), vec.size()); } private: diff --git a/src/cli/pubkey.cpp b/src/cli/pubkey.cpp index 35d50592f..2616f6065 100644 --- a/src/cli/pubkey.cpp +++ b/src/cli/pubkey.cpp @@ -24,6 +24,10 @@ #include <botan/rsa.h> #endif +#if defined(BOTAN_HAS_DSA) + #include <botan/dsa.h> +#endif + #if defined(BOTAN_HAS_ECDSA) #include <botan/ecdsa.h> #endif @@ -41,7 +45,7 @@ namespace Botan_CLI { class PK_Keygen : public Command { public: - PK_Keygen() : Command("keygen --algo=RSA --params= --passphrase= --pbe= --pbe-millis=300") {} + PK_Keygen() : Command("keygen --algo=RSA --params= --passphrase= --pbe= --pbe-millis=300 --der-out") {} std::unique_ptr<Botan::Private_Key> do_keygen(const std::string& algo, const std::string& params, @@ -59,6 +63,15 @@ class PK_Keygen : public Command }; #endif +#if defined(BOTAN_HAS_DSA) + generators["DSA"] = [&rng](std::string param) -> std::unique_ptr<Botan::Private_Key> { + if(param.empty()) + param = "dsa/botan/2048"; + return std::unique_ptr<Botan::Private_Key>( + new Botan::DSA_PrivateKey(rng, param)); + }; +#endif + #if defined(BOTAN_HAS_ECDSA) generators["ECDSA"] = [&rng](std::string param) { if(param.empty()) @@ -105,18 +118,32 @@ class PK_Keygen : public Command std::unique_ptr<Botan::Private_Key> key(do_keygen(get_arg("algo"), get_arg("params"), rng)); const std::string pass = get_arg("passphrase"); + const bool der_out = flag_set("der-out"); - if(pass.empty()) + const std::chrono::milliseconds pbe_millis(get_arg_sz("pbe-millis")); + const std::string pbe = get_arg("pbe"); + + if(der_out) { - output() << Botan::PKCS8::PEM_encode(*key); + if(pass.empty()) + { + write_output(Botan::PKCS8::BER_encode(*key)); + } + else + { + write_output(Botan::PKCS8::BER_encode(*key, rng, pass, pbe_millis, pbe)); + } } else { - output() << Botan::PKCS8::PEM_encode(*key, - rng, - pass, - std::chrono::milliseconds(get_arg_sz("pbe-millis")), - get_arg("pbe")); + if(pass.empty()) + { + output() << Botan::PKCS8::PEM_encode(*key); + } + else + { + output() << Botan::PKCS8::PEM_encode(*key, rng, pass, pbe_millis, pbe); + } } } }; @@ -235,39 +262,59 @@ BOTAN_REGISTER_COMMAND(Gen_DL_Group); class PKCS8_Tool : public Command { public: - PKCS8_Tool() : Command("pkcs8 --pass-in= --pub-out --pass-out= --pbe= --pbe-millis=300 key") {} + PKCS8_Tool() : Command("pkcs8 --pass-in= --pub-out --der-out --pass-out= --pbe= --pbe-millis=300 key") {} void go() override { Botan::AutoSeeded_RNG rng; - std::unique_ptr<Botan::Private_Key> key(Botan::PKCS8::load_key(get_arg("key"), - rng, - get_arg("pass-in"))); + std::unique_ptr<Botan::Private_Key> key( + Botan::PKCS8::load_key(get_arg("key"), + rng, + get_arg("pass-in"))); + + const std::chrono::milliseconds pbe_millis(get_arg_sz("pbe-millis")); + const std::string pbe = get_arg("pbe"); + const bool der_out = flag_set("der-out"); if(flag_set("pub-out")) { - output() << Botan::X509::PEM_encode(*key); + if(der_out) + { + write_output(Botan::X509::BER_encode(*key)); + } + else + { + output() << Botan::X509::PEM_encode(*key); + } } else { const std::string pass = get_arg("pass-out"); - if(pass.empty()) + if(der_out) { - output() << Botan::PKCS8::PEM_encode(*key); + if(pass.empty()) + { + write_output(Botan::PKCS8::BER_encode(*key)); + } + else + { + write_output(Botan::PKCS8::BER_encode(*key, rng, pass, pbe_millis, pbe)); + } } else { - output() << Botan::PKCS8::PEM_encode(*key, - rng, - pass, - std::chrono::milliseconds(get_arg_sz("pbe-millis")), - get_arg("pbe")); - } - + if(pass.empty()) + { + output() << Botan::PKCS8::PEM_encode(*key); + } + else + { + output() << Botan::PKCS8::PEM_encode(*key, rng, pass, pbe_millis, pbe); + } + } } - } }; diff --git a/src/cli/utils.cpp b/src/cli/utils.cpp index f3ce5f0f9..9302ec5d0 100644 --- a/src/cli/utils.cpp +++ b/src/cli/utils.cpp @@ -124,7 +124,11 @@ class Hash : public Command const size_t buf_size = get_arg_sz("buf-size"); - for(auto fsname : get_arg_list("files")) + auto files = get_arg_list("files"); + if(files.empty()) + files.push_back("-"); // read stdin if no arguments on command line + + for(auto fsname : files) { auto update_hash = [&](const uint8_t b[], size_t l) { hash_fn->update(b, l); }; read_file(fsname, update_hash, buf_size); diff --git a/src/lib/math/mp/info.txt b/src/lib/math/mp/info.txt index a47475f7b..6aa0142f3 100644 --- a/src/lib/math/mp/info.txt +++ b/src/lib/math/mp/info.txt @@ -1,11 +1,10 @@ -define BIGINT_MP 20131128 +define BIGINT_MP 20151225 <source> mp_asm.cpp mp_comba.cpp mp_karat.cpp mp_monty.cpp -mp_mulop.cpp mp_misc.cpp mp_shift.cpp </source> diff --git a/src/lib/math/mp/mp_asm.cpp b/src/lib/math/mp/mp_asm.cpp index cc573a792..b7d90552e 100644 --- a/src/lib/math/mp/mp_asm.cpp +++ b/src/lib/math/mp/mp_asm.cpp @@ -1,5 +1,5 @@ /* -* Lowest Level MPI Algorithms +* MPI Add, Subtract, Word Multiply * (C) 1999-2010 Jack Lloyd * 2006 Luca Piccarreta * diff --git a/src/lib/math/mp/mp_comba.cpp b/src/lib/math/mp/mp_comba.cpp index 0170c9fcd..1ebd29817 100644 --- a/src/lib/math/mp/mp_comba.cpp +++ b/src/lib/math/mp/mp_comba.cpp @@ -1,6 +1,7 @@ /* * Comba Multiplication and Squaring -* (C) 1999-2007,2011,2014 Jack Lloyd +* +* This file was automatically generated by comba.py * * Botan is released under the Simplified BSD License (see license.txt) */ diff --git a/src/lib/math/mp/mp_core.h b/src/lib/math/mp/mp_core.h index b97384d18..9921f1f07 100644 --- a/src/lib/math/mp/mp_core.h +++ b/src/lib/math/mp/mp_core.h @@ -81,15 +81,6 @@ void bigint_shr2(word y[], const word x[], size_t x_size, size_t word_shift, size_t bit_shift); /* -* Simple O(N^2) Multiplication and Squaring -*/ -void bigint_simple_mul(word z[], - const word x[], size_t x_size, - const word y[], size_t y_size); - -void bigint_simple_sqr(word z[], const word x[], size_t x_size); - -/* * Linear Multiply */ void bigint_linmul2(word x[], size_t x_size, word y); diff --git a/src/lib/math/mp/mp_karat.cpp b/src/lib/math/mp/mp_karat.cpp index 96d9adae2..c7f179191 100644 --- a/src/lib/math/mp/mp_karat.cpp +++ b/src/lib/math/mp/mp_karat.cpp @@ -1,5 +1,5 @@ /* -* Karatsuba Multiplication/Squaring +* Multiplication and Squaring * (C) 1999-2010 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) @@ -16,6 +16,37 @@ namespace { const size_t KARATSUBA_MULTIPLY_THRESHOLD = 32; const size_t KARATSUBA_SQUARE_THRESHOLD = 32; +namespace { + +/* +* Simple O(N^2) Multiplication +*/ +void basecase_mul(word z[], + const word x[], size_t x_size, + const word y[], size_t y_size) + { + const size_t x_size_8 = x_size - (x_size % 8); + + clear_mem(z, x_size + y_size); + + for(size_t i = 0; i != y_size; ++i) + { + const word y_i = y[i]; + + word carry = 0; + + for(size_t j = 0; j != x_size_8; j += 8) + carry = word8_madd3(z + i + j, x + j, y_i, carry); + + for(size_t j = x_size_8; j != x_size; ++j) + z[i+j] = word_madd3(x[j], y_i, z[i+j], &carry); + + z[x_size+i] = carry; + } + } + +} + /* * Karatsuba Multiplication Operation */ @@ -31,7 +62,7 @@ void karatsuba_mul(word z[], const word x[], const word y[], size_t N, else if(N == 16) return bigint_comba_mul16(z, x, y); else - return bigint_simple_mul(z, x, N, y, N); + return basecase_mul(z, x, N, y, N); } const size_t N2 = N / 2; @@ -101,7 +132,7 @@ void karatsuba_sqr(word z[], const word x[], size_t N, word workspace[]) else if(N == 16) return bigint_comba_sqr16(z, x); else - return bigint_simple_sqr(z, x, N); + return basecase_mul(z, x, N, x, N); } const size_t N2 = N / 2; @@ -262,7 +293,7 @@ void bigint_mul(word z[], size_t z_size, word workspace[], y_sw < KARATSUBA_MULTIPLY_THRESHOLD || !workspace) { - bigint_simple_mul(z, x, x_sw, y, y_sw); + basecase_mul(z, x, x_sw, y, y_sw); } else { @@ -271,7 +302,7 @@ void bigint_mul(word z[], size_t z_size, word workspace[], if(N) karatsuba_mul(z, x, y, N, workspace); else - bigint_simple_mul(z, x, x_sw, y, y_sw); + basecase_mul(z, x, x_sw, y, y_sw); } } @@ -307,7 +338,7 @@ void bigint_sqr(word z[], size_t z_size, word workspace[], } else if(x_size < KARATSUBA_SQUARE_THRESHOLD || !workspace) { - bigint_simple_sqr(z, x, x_sw); + basecase_mul(z, x, x_sw, x, x_sw); } else { @@ -316,7 +347,7 @@ void bigint_sqr(word z[], size_t z_size, word workspace[], if(N) karatsuba_sqr(z, x, N, workspace); else - bigint_simple_sqr(z, x, x_sw); + basecase_mul(z, x, x_sw, x, x_sw); } } diff --git a/src/lib/math/mp/mp_mulop.cpp b/src/lib/math/mp/mp_mulop.cpp deleted file mode 100644 index 432c7ef53..000000000 --- a/src/lib/math/mp/mp_mulop.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* -* Simple O(N^2) Multiplication and Squaring -* (C) 1999-2008 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include <botan/internal/mp_core.h> -#include <botan/internal/mp_madd.h> -#include <botan/internal/mp_asmi.h> -#include <botan/mem_ops.h> - -namespace Botan { - -/* -* Simple O(N^2) Multiplication -*/ -void bigint_simple_mul(word z[], const word x[], size_t x_size, - const word y[], size_t y_size) - { - const size_t x_size_8 = x_size - (x_size % 8); - - clear_mem(z, x_size + y_size); - - for(size_t i = 0; i != y_size; ++i) - { - const word y_i = y[i]; - - word carry = 0; - - for(size_t j = 0; j != x_size_8; j += 8) - carry = word8_madd3(z + i + j, x + j, y_i, carry); - - for(size_t j = x_size_8; j != x_size; ++j) - z[i+j] = word_madd3(x[j], y_i, z[i+j], &carry); - - z[x_size+i] = carry; - } - } - -/* -* Simple O(N^2) Squaring -* -* This is exactly the same algorithm as bigint_simple_mul, however -* because C/C++ compilers suck at alias analysis it is good to have -* the version where the compiler knows that x == y -* -* There is an O(n^1.5) squaring algorithm specified in Handbook of -* Applied Cryptography, chapter 14 -* -*/ -void bigint_simple_sqr(word z[], const word x[], size_t x_size) - { - const size_t x_size_8 = x_size - (x_size % 8); - - clear_mem(z, 2*x_size); - - for(size_t i = 0; i != x_size; ++i) - { - const word x_i = x[i]; - word carry = 0; - - for(size_t j = 0; j != x_size_8; j += 8) - carry = word8_madd3(z + i + j, x + j, x_i, carry); - - for(size_t j = x_size_8; j != x_size; ++j) - z[i+j] = word_madd3(x[j], x_i, z[i+j], &carry); - - z[x_size+i] = carry; - } - } - -} diff --git a/src/lib/pk_pad/mgf1/mgf1.h b/src/lib/pk_pad/mgf1/mgf1.h index ed2f1d023..bddb8bba8 100644 --- a/src/lib/pk_pad/mgf1/mgf1.h +++ b/src/lib/pk_pad/mgf1/mgf1.h @@ -15,9 +15,9 @@ namespace Botan { /** * MGF1 from PKCS #1 v2.0 */ -void mgf1_mask(HashFunction& hash, - const byte in[], size_t in_len, - byte out[], size_t out_len); +void BOTAN_DLL mgf1_mask(HashFunction& hash, + const byte in[], size_t in_len, + byte out[], size_t out_len); } diff --git a/src/lib/pubkey/mce/info.txt b/src/lib/pubkey/mce/info.txt index 1e9b848dd..bb0f06764 100644 --- a/src/lib/pubkey/mce/info.txt +++ b/src/lib/pubkey/mce/info.txt @@ -1,7 +1,6 @@ define MCELIECE 20150922 <header:public> -mce_kem.h mceliece.h polyn_gf2m.h gf2m_small_m.h diff --git a/src/lib/pubkey/mce/mce_kem.cpp b/src/lib/pubkey/mce/mce_kem.cpp index dede67731..b2cefaab2 100644 --- a/src/lib/pubkey/mce/mce_kem.cpp +++ b/src/lib/pubkey/mce/mce_kem.cpp @@ -1,51 +1,74 @@ /** - * (C) 2014 cryptosource GmbH - * (C) 2014 Falko Strenzke [email protected] - * - * Botan is released under the Simplified BSD License (see license.txt) - * - */ - -#include <botan/mce_kem.h> +* (C) 2014 cryptosource GmbH +* (C) 2014 Falko Strenzke [email protected] +* (C) 2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +* +*/ + #include <botan/internal/mce_internal.h> -#include <botan/sha2_64.h> +#include <botan/internal/pk_ops_impl.h> +#include <botan/internal/pk_utils.h> namespace Botan { -McEliece_KEM_Encryptor::McEliece_KEM_Encryptor(const McEliece_PublicKey& public_key) : - m_key(public_key) +class MCE_KEM_Encryptor : public PK_Ops::KEM_Encryption_with_KDF { - } + public: + typedef McEliece_PublicKey Key_Type; -std::pair<secure_vector<byte>, secure_vector<byte>> -McEliece_KEM_Encryptor::encrypt(RandomNumberGenerator& rng) - { - const secure_vector<byte> plaintext = m_key.random_plaintext_element(rng); + MCE_KEM_Encryptor(const McEliece_PublicKey& key, + const std::string& kdf) : + KEM_Encryption_with_KDF(kdf), m_key(key) {} + + private: + void raw_kem_encrypt(secure_vector<byte>& out_encapsulated_key, + secure_vector<byte>& raw_shared_key, + Botan::RandomNumberGenerator& rng) override + { + secure_vector<byte> plaintext = m_key.random_plaintext_element(rng); - secure_vector<byte> ciphertext, error_mask; - mceliece_encrypt(ciphertext, error_mask, plaintext, m_key, rng); + secure_vector<byte> ciphertext, error_mask; + mceliece_encrypt(ciphertext, error_mask, plaintext, m_key, rng); - SHA_512 hash; - hash.update(plaintext); - hash.update(error_mask); - secure_vector<byte> sym_key = hash.final(); + raw_shared_key.clear(); + raw_shared_key += plaintext; + raw_shared_key += error_mask; - return std::make_pair(ciphertext, sym_key); - } + out_encapsulated_key.swap(ciphertext); + } -McEliece_KEM_Decryptor::McEliece_KEM_Decryptor(const McEliece_PrivateKey& key) : m_key(key) { } + const McEliece_PublicKey& m_key; + }; -secure_vector<Botan::byte> McEliece_KEM_Decryptor::decrypt(const byte msg[], size_t msg_len) +class MCE_KEM_Decryptor : public PK_Ops::KEM_Decryption_with_KDF { - secure_vector<byte> plaintext, error_mask; - mceliece_decrypt(plaintext, error_mask, msg, msg_len, m_key); + public: + typedef McEliece_PrivateKey Key_Type; + + MCE_KEM_Decryptor(const McEliece_PrivateKey& key, + const std::string& kdf) : + KEM_Decryption_with_KDF(kdf), m_key(key) {} + + private: + secure_vector<byte> + raw_kem_decrypt(const byte encap_key[], size_t len) override + { + secure_vector<byte> plaintext, error_mask; + mceliece_decrypt(plaintext, error_mask, encap_key, len, m_key); + + secure_vector<byte> output; + output.reserve(plaintext.size() + error_mask.size()); + output.insert(output.end(), plaintext.begin(), plaintext.end()); + output.insert(output.end(), error_mask.begin(), error_mask.end()); + return output; + } - SHA_512 hash; - hash.update(plaintext); - hash.update(error_mask); + const McEliece_PrivateKey& m_key; + }; - secure_vector<byte> sym_key = hash.final(); - return sym_key; - } +BOTAN_REGISTER_PK_KEM_ENCRYPTION_OP("McEliece", MCE_KEM_Encryptor); +BOTAN_REGISTER_PK_KEM_DECRYPTION_OP("McEliece", MCE_KEM_Decryptor); } diff --git a/src/lib/pubkey/mce/mce_kem.h b/src/lib/pubkey/mce/mce_kem.h deleted file mode 100644 index cd899d568..000000000 --- a/src/lib/pubkey/mce/mce_kem.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * (C) 2014 cryptosource GmbH - * (C) 2014 Falko Strenzke [email protected] - * - * Botan is released under the Simplified BSD License (see license.txt) - * - */ - -#ifndef BOTAN_MCE_KEM_H__ -#define BOTAN_MCE_KEM_H__ - -#include <botan/mceliece.h> -#include <utility> - -namespace Botan { - -class BOTAN_DLL McEliece_KEM_Encryptor - { - public: - McEliece_KEM_Encryptor(const McEliece_PublicKey& public_key); - - /** - * returns the pair (mceliece ciphertext, symmetric key) - */ - std::pair<secure_vector<byte>, secure_vector<byte>> encrypt(RandomNumberGenerator& rng); - - private: - const McEliece_PublicKey& m_key; - }; - -class BOTAN_DLL McEliece_KEM_Decryptor - { - public: - McEliece_KEM_Decryptor(const McEliece_PrivateKey& mce_key); - - /** - * returns the derived 512-bit symmetric key - */ - secure_vector<Botan::byte> decrypt(const byte msg[], size_t msg_len); - - /** - * returns the derived 512-bit symmetric key - */ - template<typename Alloc> - secure_vector<Botan::byte> decrypt_vec(const std::vector<byte, Alloc>& v) - { - return decrypt(v.data(), v.size()); - } - - private: - const McEliece_PrivateKey& m_key; - }; -} - -#endif diff --git a/src/lib/pubkey/mceies/mceies.cpp b/src/lib/pubkey/mceies/mceies.cpp index e83fa257e..0af71719a 100644 --- a/src/lib/pubkey/mceies/mceies.cpp +++ b/src/lib/pubkey/mceies/mceies.cpp @@ -8,7 +8,7 @@ #include <botan/mceies.h> #include <botan/aead.h> #include <botan/mceliece.h> -#include <botan/mce_kem.h> +#include <botan/pubkey.h> namespace Botan { @@ -36,11 +36,10 @@ mceies_encrypt(const McEliece_PublicKey& pubkey, RandomNumberGenerator& rng, const std::string& algo) { - McEliece_KEM_Encryptor kem_op(pubkey); + PK_KEM_Encryptor kem_op(pubkey, "KDF1(SHA-512)"); - const std::pair<secure_vector<byte>,secure_vector<byte>> mce_ciphertext__key = kem_op.encrypt(rng); - const secure_vector<byte>& mce_ciphertext = mce_ciphertext__key.first; - const secure_vector<byte>& mce_key = mce_ciphertext__key.second; + secure_vector<byte> mce_ciphertext, mce_key; + kem_op.encrypt(mce_ciphertext, mce_key, 64, rng); const size_t mce_code_bytes = (pubkey.get_code_length() + 7) / 8; @@ -75,7 +74,7 @@ mceies_decrypt(const McEliece_PrivateKey& privkey, { try { - McEliece_KEM_Decryptor kem_op(privkey); + PK_KEM_Decryptor kem_op(privkey, "KDF1(SHA-512)"); const size_t mce_code_bytes = (privkey.get_code_length() + 7) / 8; @@ -88,7 +87,7 @@ mceies_decrypt(const McEliece_PrivateKey& privkey, if(ct_len < mce_code_bytes + nonce_len + aead->tag_size()) throw Exception("Input message too small to be valid"); - const secure_vector<byte> mce_key = kem_op.decrypt(ct, mce_code_bytes); + const secure_vector<byte> mce_key = kem_op.decrypt(ct, mce_code_bytes, 64); aead->set_key(aead_key(mce_key, *aead)); aead->set_associated_data(ad, ad_len); diff --git a/src/lib/pubkey/pk_ops.cpp b/src/lib/pubkey/pk_ops.cpp index bc421eb90..81b087894 100644 --- a/src/lib/pubkey/pk_ops.cpp +++ b/src/lib/pubkey/pk_ops.cpp @@ -129,4 +129,47 @@ bool PK_Ops::Verification_with_EMSA::is_valid_signature(const byte sig[], size_t } } +void PK_Ops::KEM_Encryption_with_KDF::kem_encrypt(secure_vector<byte>& out_encapsulated_key, + secure_vector<byte>& out_shared_key, + size_t desired_shared_key_len, + Botan::RandomNumberGenerator& rng, + const uint8_t salt[], + size_t salt_len) + { + secure_vector<byte> raw_shared; + this->raw_kem_encrypt(out_encapsulated_key, raw_shared, rng); + + out_shared_key = m_kdf->derive_key(desired_shared_key_len, + raw_shared.data(), raw_shared.size(), + salt, salt_len); + } + +PK_Ops::KEM_Encryption_with_KDF::KEM_Encryption_with_KDF(const std::string& kdf) + { + m_kdf.reset(get_kdf(kdf)); + } + +PK_Ops::KEM_Encryption_with_KDF::~KEM_Encryption_with_KDF() {} + +secure_vector<byte> +PK_Ops::KEM_Decryption_with_KDF::kem_decrypt(const byte encap_key[], + size_t len, + size_t desired_shared_key_len, + const uint8_t salt[], + size_t salt_len) + { + secure_vector<byte> raw_shared = this->raw_kem_decrypt(encap_key, len); + + return m_kdf->derive_key(desired_shared_key_len, + raw_shared.data(), raw_shared.size(), + salt, salt_len); + } + +PK_Ops::KEM_Decryption_with_KDF::KEM_Decryption_with_KDF(const std::string& kdf) + { + m_kdf.reset(get_kdf(kdf)); + } + +PK_Ops::KEM_Decryption_with_KDF::~KEM_Decryption_with_KDF() {} + } diff --git a/src/lib/pubkey/pk_ops.h b/src/lib/pubkey/pk_ops.h index 3a2a8bdb5..6fc21ea4a 100644 --- a/src/lib/pubkey/pk_ops.h +++ b/src/lib/pubkey/pk_ops.h @@ -47,11 +47,13 @@ typedef PK_Spec<Private_Key> PK_Spec_Private_Key; class BOTAN_DLL Encryption { public: + typedef PK_Spec_Public_Key Spec; + virtual size_t max_input_bits() const = 0; - virtual secure_vector<byte> encrypt(const byte msg[], size_t msg_len, RandomNumberGenerator& rng) = 0; - - typedef PK_Spec_Public_Key Spec; + virtual secure_vector<byte> encrypt(const byte msg[], + size_t msg_len, + RandomNumberGenerator& rng) = 0; virtual ~Encryption() {} }; @@ -164,6 +166,38 @@ class BOTAN_DLL Key_Agreement virtual ~Key_Agreement() {} }; +/** +* KEM (key encapsulation) +*/ +class BOTAN_DLL KEM_Encryption + { + public: + typedef PK_Spec_Public_Key Spec; + + virtual void kem_encrypt(secure_vector<byte>& out_encapsulated_key, + secure_vector<byte>& out_shared_key, + size_t desired_shared_key_len, + Botan::RandomNumberGenerator& rng, + const uint8_t salt[], + size_t salt_len) = 0; + + virtual ~KEM_Encryption() {} + }; + +class BOTAN_DLL KEM_Decryption + { + public: + typedef PK_Spec_Private_Key Spec; + + virtual secure_vector<byte> kem_decrypt(const byte encap_key[], + size_t len, + size_t desired_shared_key_len, + const uint8_t salt[], + size_t salt_len) = 0; + + virtual ~KEM_Decryption() {} + }; + } } diff --git a/src/lib/pubkey/pk_ops_impl.h b/src/lib/pubkey/pk_ops_impl.h index f27de4af4..0acceb53c 100644 --- a/src/lib/pubkey/pk_ops_impl.h +++ b/src/lib/pubkey/pk_ops_impl.h @@ -139,6 +139,46 @@ class Key_Agreement_with_KDF : public Key_Agreement std::unique_ptr<KDF> m_kdf; }; +class KEM_Encryption_with_KDF : public KEM_Encryption + { + public: + void kem_encrypt(secure_vector<byte>& out_encapsulated_key, + secure_vector<byte>& out_shared_key, + size_t desired_shared_key_len, + Botan::RandomNumberGenerator& rng, + const uint8_t salt[], + size_t salt_len) override; + + protected: + virtual void raw_kem_encrypt(secure_vector<byte>& out_encapsulated_key, + secure_vector<byte>& raw_shared_key, + Botan::RandomNumberGenerator& rng) = 0; + + KEM_Encryption_with_KDF(const std::string& kdf); + ~KEM_Encryption_with_KDF(); + private: + std::unique_ptr<KDF> m_kdf; + }; + +class KEM_Decryption_with_KDF : public KEM_Decryption + { + public: + secure_vector<byte> kem_decrypt(const byte encap_key[], + size_t len, + size_t desired_shared_key_len, + const uint8_t salt[], + size_t salt_len); + + protected: + virtual secure_vector<byte> + raw_kem_decrypt(const byte encap_key[], size_t len) = 0; + + KEM_Decryption_with_KDF(const std::string& kdf); + ~KEM_Decryption_with_KDF(); + private: + std::unique_ptr<KDF> m_kdf; + }; + } } diff --git a/src/lib/pubkey/pk_utils.h b/src/lib/pubkey/pk_utils.h index 326a6ea68..04a0bf5ca 100644 --- a/src/lib/pubkey/pk_utils.h +++ b/src/lib/pubkey/pk_utils.h @@ -32,6 +32,9 @@ OP* make_pk_op(const typename T::Spec& spec) #define BOTAN_REGISTER_PK_VERIFY_OP(NAME, TYPE) BOTAN_REGISTER_PK_OP(PK_Ops::Verification, NAME, TYPE) #define BOTAN_REGISTER_PK_KEY_AGREE_OP(NAME, TYPE) BOTAN_REGISTER_PK_OP(PK_Ops::Key_Agreement, NAME, TYPE) +#define BOTAN_REGISTER_PK_KEM_ENCRYPTION_OP(NAME, TYPE) BOTAN_REGISTER_PK_OP(PK_Ops::KEM_Encryption, NAME, TYPE) +#define BOTAN_REGISTER_PK_KEM_DECRYPTION_OP(NAME, TYPE) BOTAN_REGISTER_PK_OP(PK_Ops::KEM_Decryption, NAME, TYPE) + } #endif diff --git a/src/lib/pubkey/pubkey.cpp b/src/lib/pubkey/pubkey.cpp index b9923f54b..e870dfdec 100644 --- a/src/lib/pubkey/pubkey.cpp +++ b/src/lib/pubkey/pubkey.cpp @@ -59,6 +59,46 @@ secure_vector<byte> PK_Decryptor_EME::dec(const byte msg[], size_t length) const return m_op->decrypt(msg, length); } +PK_KEM_Encryptor::PK_KEM_Encryptor(const Public_Key& key, + const std::string& param, + const std::string& provider) + { + m_op.reset(get_pk_op<PK_Ops::KEM_Encryption>("KEM", key, param, provider)); + } + +void PK_KEM_Encryptor::encrypt(secure_vector<byte>& out_encapsulated_key, + secure_vector<byte>& out_shared_key, + size_t desired_shared_key_len, + Botan::RandomNumberGenerator& rng, + const uint8_t salt[], + size_t salt_len) + { + m_op->kem_encrypt(out_encapsulated_key, + out_shared_key, + desired_shared_key_len, + rng, + salt, + salt_len); + } + +PK_KEM_Decryptor::PK_KEM_Decryptor(const Private_Key& key, + const std::string& param, + const std::string& provider) + { + m_op.reset(get_pk_op<PK_Ops::KEM_Decryption>("KEM", key, param, provider)); + } + +secure_vector<byte> PK_KEM_Decryptor::decrypt(const byte encap_key[], + size_t encap_key_len, + size_t desired_shared_key_len, + const uint8_t salt[], + size_t salt_len) + { + return m_op->kem_decrypt(encap_key, encap_key_len, + desired_shared_key_len, + salt, salt_len); + } + PK_Key_Agreement::PK_Key_Agreement(const Private_Key& key, const std::string& kdf) { m_op.reset(get_pk_op<PK_Ops::Key_Agreement>("Key agreement", key, kdf)); diff --git a/src/lib/pubkey/pubkey.h b/src/lib/pubkey/pubkey.h index 637e522e4..a8caf58ac 100644 --- a/src/lib/pubkey/pubkey.h +++ b/src/lib/pubkey/pubkey.h @@ -438,6 +438,87 @@ class BOTAN_DLL PK_Decryptor_EME : public PK_Decryptor std::unique_ptr<PK_Ops::Decryption> m_op; }; +class BOTAN_DLL PK_KEM_Encryptor + { + public: + PK_KEM_Encryptor(const Public_Key& key, + const std::string& kem_param = "", + const std::string& provider = ""); + + void encrypt(secure_vector<byte>& out_encapsulated_key, + secure_vector<byte>& out_shared_key, + size_t desired_shared_key_len, + Botan::RandomNumberGenerator& rng, + const uint8_t salt[], + size_t salt_len); + + template<typename Alloc> + void encrypt(secure_vector<byte>& out_encapsulated_key, + secure_vector<byte>& out_shared_key, + size_t desired_shared_key_len, + Botan::RandomNumberGenerator& rng, + const std::vector<uint8_t, Alloc>& salt) + { + this->encrypt(out_encapsulated_key, + out_shared_key, + desired_shared_key_len, + rng, + salt.data(), salt.size()); + } + + void encrypt(secure_vector<byte>& out_encapsulated_key, + secure_vector<byte>& out_shared_key, + size_t desired_shared_key_len, + Botan::RandomNumberGenerator& rng) + { + this->encrypt(out_encapsulated_key, + out_shared_key, + desired_shared_key_len, + rng, + nullptr, + 0); + } + + private: + std::unique_ptr<PK_Ops::KEM_Encryption> m_op; + }; + +class BOTAN_DLL PK_KEM_Decryptor + { + public: + PK_KEM_Decryptor(const Private_Key& key, + const std::string& kem_param = "", + const std::string& provider = ""); + + secure_vector<byte> decrypt(const byte encap_key[], + size_t encap_key_len, + size_t desired_shared_key_len, + const uint8_t salt[], + size_t salt_len); + + secure_vector<byte> decrypt(const byte encap_key[], + size_t encap_key_len, + size_t desired_shared_key_len) + { + return this->decrypt(encap_key, encap_key_len, + desired_shared_key_len, + nullptr, 0); + } + + template<typename Alloc1, typename Alloc2> + secure_vector<byte> decrypt(const std::vector<byte, Alloc1>& encap_key, + size_t desired_shared_key_len, + const std::vector<byte, Alloc2>& salt) + { + return this->decrypt(encap_key.data(), encap_key.size(), + desired_shared_key_len, + salt.data(), salt.size()); + } + + private: + std::unique_ptr<PK_Ops::KEM_Decryption> m_op; + }; + } #endif diff --git a/src/lib/pubkey/rsa/rsa.cpp b/src/lib/pubkey/rsa/rsa.cpp index 5804d0034..d18843315 100644 --- a/src/lib/pubkey/rsa/rsa.cpp +++ b/src/lib/pubkey/rsa/rsa.cpp @@ -1,6 +1,6 @@ /* * RSA -* (C) 1999-2010 Jack Lloyd +* (C) 1999-2010,2015 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -13,6 +13,8 @@ #include <botan/reducer.h> #include <future> +#include <iostream> + namespace Botan { /* @@ -156,11 +158,34 @@ class RSA_Decryption_Operation : public PK_Ops::Decryption_with_EME, const BigInt m(msg, msg_len); const BigInt x = blinded_private_op(m); const BigInt c = m_powermod_e_n(x); - BOTAN_ASSERT(m == c, "RSA sign consistency check"); + BOTAN_ASSERT(m == c, "RSA decrypt consistency check"); return BigInt::encode_locked(x); } }; +class RSA_KEM_Decryption_Operation : public PK_Ops::KEM_Decryption_with_KDF, + private RSA_Private_Operation + { + public: + typedef RSA_PrivateKey Key_Type; + + RSA_KEM_Decryption_Operation(const RSA_PrivateKey& key, + const std::string& kdf) : + PK_Ops::KEM_Decryption_with_KDF(kdf), + RSA_Private_Operation(key) + {} + + secure_vector<byte> + raw_kem_decrypt(const byte encap_key[], size_t len) override + { + const BigInt m(encap_key, len); + const BigInt x = blinded_private_op(m); + const BigInt c = m_powermod_e_n(x); + BOTAN_ASSERT(m == c, "RSA KEM consistency check"); + return BigInt::encode_1363(x, n.bytes()); + } + }; + /** * RSA public (encrypt/verify) operation */ @@ -181,6 +206,8 @@ class RSA_Public_Operation return powermod_e_n(m); } + const BigInt& get_n() const { return n; } + const BigInt& n; Fixed_Exponent_Power_Mod powermod_e_n; }; @@ -230,11 +257,42 @@ class RSA_Verify_Operation : public PK_Ops::Verification_with_EMSA, } }; +class RSA_KEM_Encryption_Operation : public PK_Ops::KEM_Encryption_with_KDF, + private RSA_Public_Operation + { + public: + typedef RSA_PublicKey Key_Type; + + RSA_KEM_Encryption_Operation(const RSA_PublicKey& key, + const std::string& kdf) : + PK_Ops::KEM_Encryption_with_KDF(kdf), + RSA_Public_Operation(key) {} + + private: + void raw_kem_encrypt(secure_vector<byte>& out_encapsulated_key, + secure_vector<byte>& raw_shared_key, + Botan::RandomNumberGenerator& rng) override + { + const BigInt r = BigInt::random_integer(rng, 1, get_n()); + std::cout << "R = " << r << "\n"; + const BigInt c = public_op(r); + std::cout << "C0 = " << c << "\n"; + + out_encapsulated_key = BigInt::encode_locked(c); + raw_shared_key = BigInt::encode_locked(r); + } + }; + + BOTAN_REGISTER_PK_ENCRYPTION_OP("RSA", RSA_Encryption_Operation); BOTAN_REGISTER_PK_DECRYPTION_OP("RSA", RSA_Decryption_Operation); + BOTAN_REGISTER_PK_SIGNATURE_OP("RSA", RSA_Signature_Operation); BOTAN_REGISTER_PK_VERIFY_OP("RSA", RSA_Verify_Operation); +BOTAN_REGISTER_PK_KEM_ENCRYPTION_OP("RSA", RSA_KEM_Encryption_Operation); +BOTAN_REGISTER_PK_KEM_DECRYPTION_OP("RSA", RSA_KEM_Decryption_Operation); + } } diff --git a/src/lib/tls/tls_session_manager.h b/src/lib/tls/tls_session_manager.h index 5ab151c26..e01462f66 100644 --- a/src/lib/tls/tls_session_manager.h +++ b/src/lib/tls/tls_session_manager.h @@ -127,7 +127,7 @@ class BOTAN_DLL Session_Manager_In_Memory : public Session_Manager void remove_entry(const std::vector<byte>& session_id) override; - size_t remove_all(); + size_t remove_all() override; void save(const Session& session_data) override; diff --git a/src/lib/utils/loadstor.h b/src/lib/utils/loadstor.h index 53700fc86..8d33c2eef 100644 --- a/src/lib/utils/loadstor.h +++ b/src/lib/utils/loadstor.h @@ -308,24 +308,27 @@ inline void load_le(T out[], const byte in[], size_t count) { + if(count > 0) + { #if defined(BOTAN_TARGET_CPU_HAS_KNOWN_ENDIANNESS) - std::memcpy(out, in, sizeof(T)*count); + std::memcpy(out, in, sizeof(T)*count); #if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - const size_t blocks = count - (count % 4); - const size_t left = count - blocks; + const size_t blocks = count - (count % 4); + const size_t left = count - blocks; - for(size_t i = 0; i != blocks; i += 4) - bswap_4(out + i); + for(size_t i = 0; i != blocks; i += 4) + bswap_4(out + i); - for(size_t i = 0; i != left; ++i) - out[blocks+i] = reverse_bytes(out[blocks+i]); + for(size_t i = 0; i != left; ++i) + out[blocks+i] = reverse_bytes(out[blocks+i]); #endif #else - for(size_t i = 0; i != count; ++i) - out[i] = load_le<T>(in, i); + for(size_t i = 0; i != count; ++i) + out[i] = load_le<T>(in, i); #endif + } } /** @@ -397,24 +400,27 @@ inline void load_be(T out[], const byte in[], size_t count) { + if(count > 0) + { #if defined(BOTAN_TARGET_CPU_HAS_KNOWN_ENDIANNESS) - std::memcpy(out, in, sizeof(T)*count); + std::memcpy(out, in, sizeof(T)*count); #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - const size_t blocks = count - (count % 4); - const size_t left = count - blocks; + const size_t blocks = count - (count % 4); + const size_t left = count - blocks; - for(size_t i = 0; i != blocks; i += 4) - bswap_4(out + i); + for(size_t i = 0; i != blocks; i += 4) + bswap_4(out + i); - for(size_t i = 0; i != left; ++i) - out[blocks+i] = reverse_bytes(out[blocks+i]); + for(size_t i = 0; i != left; ++i) + out[blocks+i] = reverse_bytes(out[blocks+i]); #endif #else - for(size_t i = 0; i != count; ++i) - out[i] = load_be<T>(in, i); + for(size_t i = 0; i != count; ++i) + out[i] = load_be<T>(in, i); #endif + } } /** diff --git a/src/lib/utils/mem_ops.h b/src/lib/utils/mem_ops.h index 6ea7bdafe..d11e3368c 100644 --- a/src/lib/utils/mem_ops.h +++ b/src/lib/utils/mem_ops.h @@ -28,7 +28,10 @@ BOTAN_DLL void zero_mem(void* ptr, size_t n); */ template<typename T> inline void clear_mem(T* ptr, size_t n) { - std::memset(ptr, 0, sizeof(T)*n); + if(n > 0) + { + std::memset(ptr, 0, sizeof(T)*n); + } } /** @@ -39,7 +42,10 @@ template<typename T> inline void clear_mem(T* ptr, size_t n) */ template<typename T> inline void copy_mem(T* out, const T* in, size_t n) { - std::memmove(out, in, sizeof(T)*n); + if(n > 0) + { + std::memmove(out, in, sizeof(T)*n); + } } /** @@ -51,7 +57,10 @@ template<typename T> inline void copy_mem(T* out, const T* in, size_t n) template<typename T> inline void set_mem(T* ptr, size_t n, byte val) { - std::memset(ptr, val, sizeof(T)*n); + if(n > 0) + { + std::memset(ptr, val, sizeof(T)*n); + } } /** diff --git a/src/scripts/comba.py b/src/scripts/comba.py index dcac14657..f0165a3ce 100755 --- a/src/scripts/comba.py +++ b/src/scripts/comba.py @@ -1,7 +1,10 @@ -#!/usr/bin/python +#!/usr/bin/python2 import sys +# (C) 2011,2014,2015 Jack Lloyd +# Botan is released under the Simplified BSD License (see license.txt) + # Used to generate src/lib/math/mp/mp_comba.cpp def comba_indexes(N): @@ -78,7 +81,8 @@ def main(args = None): print """/* * Comba Multiplication and Squaring -* (C) 1999-2007,2011,2014 Jack Lloyd +* +* This file was automatically generated by comba.py * * Botan is released under the Simplified BSD License (see license.txt) */ diff --git a/src/tests/data/nist_x509/expected.txt b/src/tests/data/nist_x509/expected.txt new file mode 100644 index 000000000..0b2535fca --- /dev/null +++ b/src/tests/data/nist_x509/expected.txt @@ -0,0 +1,76 @@ +test01:Verified +test02:Signature error +test03:Signature error +test04:Verified +test05:Certificate is not yet valid +test06:Certificate is not yet valid +test07:Verified +test08:Certificate is not yet valid +test09:Certificate has expired +test10:Certificate has expired +test11:Certificate has expired +test12:Verified +test13:Certificate issuer not found +test14:Certificate issuer not found +test15:Verified +test16:Verified +test17:Verified +test18:Verified +test19:No revocation data +test20:Certificate is revoked +test21:Certificate is revoked +test22:CA certificate not allowed to issue certs +test23:CA certificate not allowed to issue certs +test24:Verified +test25:CA certificate not allowed to issue certs +test26:Verified +test27:Verified +test28:CA certificate not allowed to issue certs +test29:CA certificate not allowed to issue certs +test30:Verified +test31:CA certificate not allowed to issue CRLs +test32:CA certificate not allowed to issue CRLs +test33:Verified +test34:Verified +test35:Verified +test36:Verified +test37:Verified +test38:Verified +test39:Verified +test40:Verified +test41:Verified +test42:Verified +test43:Verified +test44:Verified +#test45:Explicit policy required +#test46: +#test47:Explicit policy required +test48:Verified +test49:Verified +test50:Verified +test51:Verified +test52:Verified +test53:Verified +test54:Certificate chain too long +test55:Certificate chain too long +test56:Verified +test57:Verified +test58:Certificate chain too long +test59:Certificate chain too long +test60:Certificate chain too long +test61:Certificate chain too long +test62:Verified +test63:Verified +test64:CRL bad signature +test65:No revocation data +test66:No revocation data +test67:Verified +test68:Certificate is revoked +test69:Certificate is revoked +test70:Certificate is revoked +test71:Certificate is revoked +test72:CRL has expired +test73:CRL has expired +test74:Verified +#test75: +#test76: diff --git a/src/tests/data/pubkey/rsa_kem.vec b/src/tests/data/pubkey/rsa_kem.vec new file mode 100644 index 000000000..6fb76fcfe --- /dev/null +++ b/src/tests/data/pubkey/rsa_kem.vec @@ -0,0 +1,25 @@ + +# RSA-KEM tests vectors from ISO-18033-2 +# http://www.shoup.net/iso/std4.pdf + +# R values here are -1 from the actual desired value to account for +# some logic in random_integer wrt the bounds + +# Test C.6.2 +E = 65537 +P = 74100103850091296168511028051948833436338123529747970640732238422269665602829 +Q = 79461607023043824134896992211543210236933205105414344240218914846895267687977 +R = 032E45326FA859A72EC235ACFF929B15D1372E30B207255F0611B8F785D764374152E0AC009E509E7BA30CD2F1778E113B64E135CF4E2292C75EFE5288EDFDA3 +C0 = 4603E5324CAB9CEF8365C817052D954D44447B1667099EDC69942D32CD594E4FFCF268AE3836E2C35744AAA53AE201FE499806B67DEDAA26BF72ECBD117A6FC0 +KDF = KDF2(SHA-1) +K = 0E6A26EB7B956CCB8B3BDC1CA975BC57C3989E8FBAD31A224655D800C46954840F + +# Test C.6.4 + +E = 65537 +P = 74100103850091296168511028051948833436338123529747970640732238422269665602829 +Q = 79461607023043824134896992211543210236933205105414344240218914846895267687977 +R = 032E45326FA859A72EC235ACFF929B15D1372E30B207255F0611B8F785D764374152E0AC009E509E7BA30CD2F1778E113B64E135CF4E2292C75EFE5288EDFDA3 +C0 = 4603E5324CAB9CEF8365C817052D954D44447B1667099EDC69942D32CD594E4FFCF268AE3836E2C35744AAA53AE201FE499806B67DEDAA26BF72ECBD117A6FC0 +KDF = KDF2(SHA-256) +K = 10a2403db42a8743cb989de86e668d168cbe6046 diff --git a/src/tests/main.cpp b/src/tests/main.cpp index 692bc41af..57a525dbd 100644 --- a/src/tests/main.cpp +++ b/src/tests/main.cpp @@ -71,7 +71,7 @@ class Test_Runner : public Botan_CLI::Command const size_t threads = get_arg_sz("threads"); const size_t soak_level = get_arg_sz("soak"); const std::string drbg_seed = get_arg("drbg-seed"); - bool log_success = flag_set("log-success"); + const bool log_success = flag_set("log-success"); const std::string data_dir = get_arg_or("data-dir", "src/tests/data"); std::vector<std::string> req = get_arg_list("suites"); @@ -211,7 +211,9 @@ class Test_Runner : public Botan_CLI::Command for(auto&& test_name : tests_to_run) { auto run_it = [test_name] { - return Botan_Tests::Test::run_test(test_name, false); }; + return Botan_Tests::Test::run_test(test_name, false); + }; + fut_results.push_back(std::async(std::launch::async, run_it)); while(fut_results.size() > threads) diff --git a/src/tests/test_dh.cpp b/src/tests/test_dh.cpp index 643206031..a30564f92 100644 --- a/src/tests/test_dh.cpp +++ b/src/tests/test_dh.cpp @@ -28,7 +28,7 @@ class Diffie_Hellman_KAT_Tests : public PK_Key_Agreement_Test {"KDF"}) {} - std::string default_kdf(const VarMap&) { return "Raw"; } + std::string default_kdf(const VarMap&) const override { return "Raw"; } std::unique_ptr<Botan::Private_Key> load_our_key(const VarMap& vars) override { diff --git a/src/tests/test_mceliece.cpp b/src/tests/test_mceliece.cpp index d894f2045..36ecd660e 100644 --- a/src/tests/test_mceliece.cpp +++ b/src/tests/test_mceliece.cpp @@ -11,7 +11,6 @@ #if defined(BOTAN_HAS_MCELIECE) #include <botan/mceliece.h> -#include <botan/mce_kem.h> #include <botan/pubkey.h> #include <botan/oids.h> #include <botan/hmac_drbg.h> @@ -81,13 +80,17 @@ class McEliece_Keygen_Encrypt_Test : public Text_Based_Test rng.clear(); rng.add_entropy(encrypt_seed.data(), encrypt_seed.size()); - Botan::McEliece_KEM_Encryptor kem_enc(mce_priv); - Botan::McEliece_KEM_Decryptor kem_dec(mce_priv); + Botan::PK_KEM_Encryptor kem_enc(mce_priv, "KDF1(SHA-512)"); + Botan::PK_KEM_Decryptor kem_dec(mce_priv, "KDF1(SHA-512)"); - const auto kem = kem_enc.encrypt(rng); - result.test_eq("ciphertext", kem.first, ciphertext); - result.test_eq("encrypt shared", kem.second, shared_key); - result.test_eq("decrypt shared", kem_dec.decrypt_vec(kem.first), shared_key); + Botan::secure_vector<byte> encap_key, prod_shared_key; + kem_enc.encrypt(encap_key, prod_shared_key, 64, rng); + + Botan::secure_vector<byte> dec_shared_key = kem_dec.decrypt(encap_key.data(), encap_key.size(), 64); + + result.test_eq("ciphertext", encap_key, ciphertext); + result.test_eq("encrypt shared", prod_shared_key, shared_key); + result.test_eq("decrypt shared", dec_shared_key, shared_key); return result; } @@ -176,18 +179,19 @@ class McEliece_Tests : public Test { Test::Result result("McEliece KEM"); - Botan::McEliece_KEM_Encryptor pub_op(pk); - Botan::McEliece_KEM_Decryptor priv_op(sk); + Botan::PK_KEM_Encryptor enc_op(pk, "KDF2(SHA-256)"); + Botan::PK_KEM_Decryptor dec_op(sk, "KDF2(SHA-256)"); for(size_t i = 0; i <= Test::soak_level(); i++) { - const std::pair<Botan::secure_vector<byte>,Botan::secure_vector<byte> > ciphertext__sym_key = pub_op.encrypt(Test::rng()); - const Botan::secure_vector<byte>& ciphertext = ciphertext__sym_key.first; - const Botan::secure_vector<byte>& sym_key_encr = ciphertext__sym_key.second; + Botan::secure_vector<byte> salt = Test::rng().random_vec(i); + + Botan::secure_vector<byte> encap_key, shared_key; + enc_op.encrypt(encap_key, shared_key, 64, Test::rng(), salt); - const Botan::secure_vector<byte> sym_key_decr = priv_op.decrypt(ciphertext.data(), ciphertext.size()); + Botan::secure_vector<byte> shared_key2 = dec_op.decrypt(encap_key, 64, salt); - result.test_eq("same key", sym_key_decr, sym_key_encr); + result.test_eq("same key", shared_key, shared_key2); } return result; } diff --git a/src/tests/test_pubkey.cpp b/src/tests/test_pubkey.cpp index 14f36dbbd..4521717e9 100644 --- a/src/tests/test_pubkey.cpp +++ b/src/tests/test_pubkey.cpp @@ -249,6 +249,46 @@ PK_Encryption_Decryption_Test::run_one_test(const std::string&, const VarMap& va return result; } +Test::Result PK_KEM_Test::run_one_test(const std::string&, const VarMap& vars) + { + const std::vector<uint8_t> K = get_req_bin(vars, "K"); + const std::vector<uint8_t> C0 = get_req_bin(vars, "C0"); + const std::vector<uint8_t> salt = get_opt_bin(vars, "Salt"); + const std::string kdf = get_req_str(vars, "KDF"); + + Test::Result result(algo_name() + "/" + kdf + " KEM"); + + std::unique_ptr<Botan::Private_Key> privkey = load_private_key(vars); + + const size_t desired_key_len = K.size(); + + Botan::PK_KEM_Encryptor enc(*privkey, kdf); + + Fixed_Output_RNG fixed_output_rng(get_req_bin(vars, "R")); + + Botan::secure_vector<byte> produced_encap_key, shared_key; + enc.encrypt(produced_encap_key, + shared_key, + desired_key_len, + fixed_output_rng, + salt); + + result.test_eq("C0 matches", produced_encap_key, C0); + result.test_eq("K matches", shared_key, K); + + Botan::PK_KEM_Decryptor dec(*privkey, kdf); + + const Botan::secure_vector<uint8_t> decr_shared_key = + dec.decrypt(C0.data(), C0.size(), + desired_key_len, + salt.data(), + salt.size()); + + result.test_eq("decrypted K matches", decr_shared_key, K); + + return result; + } + Test::Result PK_Key_Agreement_Test::run_one_test(const std::string&, const VarMap& vars) { const std::vector<uint8_t> shared = get_req_bin(vars, "K"); @@ -288,55 +328,102 @@ PK_Key_Generation_Test::test_key(const std::string& algo, const Botan::Private_K { Test::Result result(algo + " keygen"); - const std::string pub_pem = Botan::X509::PEM_encode(key); + try + { + Botan::DataSource_Memory data_src(Botan::X509::PEM_encode(key)); + std::unique_ptr<Botan::Public_Key> loaded(Botan::X509::load_key(data_src)); + + result.test_eq("recovered public key from private", loaded.get(), true); + result.test_eq("public key has same type", loaded->algo_name(), key.algo_name()); + result.test_eq("public key passes checks", loaded->check_key(Test::rng(), false), true); + } + catch(std::exception& e) + { + result.test_failure("roundtrip PEM public key", e.what()); + } try { - Botan::DataSource_Memory input_pub(pub_pem); - std::unique_ptr<Botan::Public_Key> restored_pub(Botan::X509::load_key(input_pub)); + Botan::DataSource_Memory data_src(Botan::X509::BER_encode(key)); + std::unique_ptr<Botan::Public_Key> loaded(Botan::X509::load_key(data_src)); - result.test_eq("recovered public key from private", restored_pub.get(), true); - result.test_eq("public key has same type", restored_pub->algo_name(), key.algo_name()); - result.test_eq("public key passes checks", restored_pub->check_key(Test::rng(), false), true); + result.test_eq("recovered public key from private", loaded.get(), true); + result.test_eq("public key has same type", loaded->algo_name(), key.algo_name()); + result.test_eq("public key passes checks", loaded->check_key(Test::rng(), false), true); } catch(std::exception& e) { - result.test_failure("roundtrip public key", e.what()); + result.test_failure("roundtrip BER public key", e.what()); } - const std::string priv_pem = Botan::PKCS8::PEM_encode(key); + try + { + Botan::DataSource_Memory data_src(Botan::PKCS8::PEM_encode(key)); + std::unique_ptr<Botan::Private_Key> loaded( + Botan::PKCS8::load_key(data_src, Test::rng())); + + result.test_eq("recovered private key from PEM blob", loaded.get(), true); + result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); + result.test_eq("private key passes checks", loaded->check_key(Test::rng(), false), true); + } + catch(std::exception& e) + { + result.test_failure("roundtrip PEM private key", e.what()); + } + /* + // Currently broken GH #379 try { - Botan::DataSource_Memory input_priv(priv_pem); - std::unique_ptr<Botan::Private_Key> restored_priv( - Botan::PKCS8::load_key(input_priv, Test::rng())); + Botan::DataSource_Memory data_src(Botan::PKCS8::BER_encode(key)); + std::unique_ptr<Botan::Public_Key> loaded(Botan::PKCS8::load_key(data_src, Test::rng())); - result.test_eq("recovered private key from blob", restored_priv.get(), true); - result.test_eq("reloaded key has same type", restored_priv->algo_name(), key.algo_name()); - result.test_eq("private key passes checks", restored_priv->check_key(Test::rng(), false), true); + result.test_eq("recovered public key from private", loaded.get(), true); + result.test_eq("public key has same type", loaded->algo_name(), key.algo_name()); + result.test_eq("public key passes checks", loaded->check_key(Test::rng(), false), true); } catch(std::exception& e) { - result.test_failure("roundtrip private key", e.what()); + result.test_failure("roundtrip BER private key", e.what()); } + */ const std::string passphrase = Test::random_password(); - const std::string enc_priv_pem = Botan::PKCS8::PEM_encode(key, Test::rng(), passphrase, - std::chrono::milliseconds(10)); + try { - Botan::DataSource_Memory input_priv(priv_pem); - std::unique_ptr<Botan::Private_Key> restored_priv( - Botan::PKCS8::load_key(input_priv, Test::rng(), passphrase)); + Botan::DataSource_Memory data_src( + Botan::PKCS8::PEM_encode(key, Test::rng(), passphrase, + std::chrono::milliseconds(10))); + + std::unique_ptr<Botan::Private_Key> loaded( + Botan::PKCS8::load_key(data_src, Test::rng(), passphrase)); + + result.test_eq("recovered private key from encrypted blob", loaded.get(), true); + result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); + result.test_eq("private key passes checks", loaded->check_key(Test::rng(), false), true); + } + catch(std::exception& e) + { + result.test_failure("roundtrip encrypted PEM private key", e.what()); + } + + try + { + Botan::DataSource_Memory data_src( + Botan::PKCS8::BER_encode(key, Test::rng(), passphrase, + std::chrono::milliseconds(10))); + + std::unique_ptr<Botan::Private_Key> loaded( + Botan::PKCS8::load_key(data_src, Test::rng(), passphrase)); - result.test_eq("recovered private key from encrypted blob", restored_priv.get(), true); - result.test_eq("reloaded key has same type", restored_priv->algo_name(), key.algo_name()); - result.test_eq("private key passes checks", restored_priv->check_key(Test::rng(), false), true); + result.test_eq("recovered private key from BER blob", loaded.get(), true); + result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); + result.test_eq("private key passes checks", loaded->check_key(Test::rng(), false), true); } catch(std::exception& e) { - result.test_failure("roundtrip private key", e.what()); + result.test_failure("roundtrip encrypted BER private key", e.what()); } return result; diff --git a/src/tests/test_pubkey.h b/src/tests/test_pubkey.h index edb36f07b..beb1b2ea2 100644 --- a/src/tests/test_pubkey.h +++ b/src/tests/test_pubkey.h @@ -87,6 +87,22 @@ class PK_Key_Agreement_Test : public Text_Based_Test Test::Result run_one_test(const std::string& header, const VarMap& vars) override; }; +class PK_KEM_Test : public Text_Based_Test + { + public: + //using Text_Based_Test::Text_Based_Test; + + PK_KEM_Test(const std::string& algo, + const std::string& test_src, + const std::vector<std::string>& required_keys, + const std::vector<std::string>& optional_keys = {}) : + Text_Based_Test(algo, test_src, required_keys, optional_keys) {} + + virtual std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) = 0; + private: + Test::Result run_one_test(const std::string& header, const VarMap& vars) override; + }; + class PK_Key_Generation_Test : public Test { protected: diff --git a/src/tests/test_rng.h b/src/tests/test_rng.h index 343356550..83ae9698a 100644 --- a/src/tests/test_rng.h +++ b/src/tests/test_rng.h @@ -32,7 +32,7 @@ class Fixed_Output_RNG : public Botan::RandomNumberGenerator size_t reseed_with_sources(Botan::Entropy_Sources&, size_t, - std::chrono::milliseconds) { return 0; } + std::chrono::milliseconds) override { return 0; } void randomize(uint8_t out[], size_t len) override { diff --git a/src/tests/test_rsa.cpp b/src/tests/test_rsa.cpp index 2720ae49a..6c89a5b29 100644 --- a/src/tests/test_rsa.cpp +++ b/src/tests/test_rsa.cpp @@ -38,6 +38,25 @@ class RSA_ES_KAT_Tests : public PK_Encryption_Decryption_Test } }; +class RSA_KEM_Tests : public PK_KEM_Test + { + public: + RSA_KEM_Tests() : PK_KEM_Test("RSA", "pubkey/rsa_kem.vec", + {"E", "P", "Q", "R", "C0", "KDF", "OutLen", "K"}) + {} + + std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override + { + const BigInt p = get_req_bn(vars, "P"); + const BigInt q = get_req_bn(vars, "Q"); + const BigInt e = get_req_bn(vars, "E"); + + std::unique_ptr<Botan::Private_Key> key(new Botan::RSA_PrivateKey(Test::rng(), p, q, e)); + return key; + } + + }; + class RSA_Signature_KAT_Tests : public PK_Signature_Generation_Test { public: @@ -100,6 +119,7 @@ class RSA_Keygen_Tests : public PK_Key_Generation_Test BOTAN_REGISTER_TEST("rsa_encrypt", RSA_ES_KAT_Tests); BOTAN_REGISTER_TEST("rsa_sign", RSA_Signature_KAT_Tests); BOTAN_REGISTER_TEST("rsa_verify", RSA_Signature_Verify_Tests); +BOTAN_REGISTER_TEST("rsa_kem", RSA_KEM_Tests); BOTAN_REGISTER_TEST("rsa_keygen", RSA_Keygen_Tests); #endif diff --git a/src/tests/test_x509_path.cpp b/src/tests/test_x509_path.cpp index 149c6ec2a..646f573a4 100644 --- a/src/tests/test_x509_path.cpp +++ b/src/tests/test_x509_path.cpp @@ -25,6 +25,33 @@ namespace { #if defined(BOTAN_HAS_X509_CERTIFICATES) +std::map<std::string, std::string> read_results(const std::string& results_file) + { + std::ifstream in(results_file); + if(!in.good()) + throw Test_Error("Failed reading " + results_file); + + std::map<std::string, std::string> m; + std::string line; + while(in.good()) + { + std::getline(in, line); + if(line == "") + continue; + if(line[0] == '#') + continue; + + std::vector<std::string> parts = Botan::split_on(line, ':'); + + if(parts.size() != 2) + throw Test_Error("Invalid line " + line); + + m[parts[0]] = parts[1]; + } + + return m; + } + class X509test_Path_Validation_Tests : public Test { public: @@ -33,26 +60,27 @@ class X509test_Path_Validation_Tests : public Test std::vector<Test::Result> results; // Test certs generated by https://github.com/yymax/x509test - const std::string test_dir = "src/tests/data/x509test"; - std::map<std::string, std::string> expected = read_results(test_dir + "/expected.txt"); + std::map<std::string, std::string> expected = + read_results(Test::data_file("x509test/expected.txt")); const Botan::Path_Validation_Restrictions default_restrictions; - Botan::X509_Certificate root(test_dir + "/root.pem"); + Botan::X509_Certificate root(Test::data_file("x509test/root.pem")); Botan::Certificate_Store_In_Memory trusted; trusted.add_certificate(root); for(auto i = expected.begin(); i != expected.end(); ++i) { Test::Result result("X509test path validation"); - const std::string fsname = i->first; - const std::string expected = i->second; + const std::string filename = i->first; + const std::string expected_result = i->second; - std::vector<Botan::X509_Certificate> certs = load_cert_file(test_dir + "/" + fsname); + std::vector<Botan::X509_Certificate> certs = + load_cert_file(Test::data_file("x509test/" + filename)); if(certs.empty()) - throw Test_Error("Failed to read certs from " + fsname); + throw Test_Error("Failed to read certs from " + filename); Botan::Path_Validation_Result path_result = Botan::x509_path_validate( certs, default_restrictions, trusted, @@ -61,7 +89,7 @@ class X509test_Path_Validation_Tests : public Test if(path_result.successful_validation() && path_result.trust_root() != root) path_result = Botan::Path_Validation_Result(Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST); - result.test_eq("validation result", path_result.result_string(), expected); + result.test_eq("validation result", path_result.result_string(), expected_result); results.push_back(result); } @@ -86,32 +114,6 @@ class X509test_Path_Validation_Tests : public Test return certs; } - std::map<std::string, std::string> read_results(const std::string& results_file) - { - std::ifstream in(results_file); - if(!in.good()) - throw Test_Error("Failed reading " + results_file); - - std::map<std::string, std::string> m; - std::string line; - while(in.good()) - { - std::getline(in, line); - if(line == "") - continue; - if(line[0] == '#') - continue; - - std::vector<std::string> parts = Botan::split_on(line, ':'); - - if(parts.size() != 2) - throw Test_Error("Invalid line " + line); - - m[parts[0]] = parts[1]; - } - - return m; - } }; BOTAN_REGISTER_TEST("x509_path_x509test", X509test_Path_Validation_Tests); @@ -120,142 +122,8 @@ class NIST_Path_Validation_Tests : public Test { public: std::vector<Test::Result> run() override; - - private: - std::map<size_t, Botan::Path_Validation_Result::Code> get_expected(); }; -/* - The expected results are essentially the error codes that best coorespond - to the problem described in the testing documentation. - - There are a few cases where the tests say there should or should not be an - error, and I disagree. A few of the tests have test results different from - what they "should" be: these changes are marked as such, and have comments - explaining the problem at hand. -*/ -std::map<size_t, Botan::Path_Validation_Result::Code> NIST_Path_Validation_Tests::get_expected() - { - using namespace Botan; - - std::map<size_t, Path_Validation_Result::Code> expected_results; - - - // TODO read from a file - expected_results[1] = Certificate_Status_Code::VERIFIED; - expected_results[2] = Certificate_Status_Code::SIGNATURE_ERROR; - expected_results[3] = Certificate_Status_Code::SIGNATURE_ERROR; - expected_results[4] = Certificate_Status_Code::VERIFIED; - expected_results[5] = Certificate_Status_Code::CERT_NOT_YET_VALID; - expected_results[6] = Certificate_Status_Code::CERT_NOT_YET_VALID; - expected_results[7] = Certificate_Status_Code::VERIFIED; - expected_results[8] = Certificate_Status_Code::CERT_NOT_YET_VALID; - expected_results[9] = Certificate_Status_Code::CERT_HAS_EXPIRED; - expected_results[10] = Certificate_Status_Code::CERT_HAS_EXPIRED; - expected_results[11] = Certificate_Status_Code::CERT_HAS_EXPIRED; - expected_results[12] = Certificate_Status_Code::VERIFIED; - expected_results[13] = Certificate_Status_Code::CERT_ISSUER_NOT_FOUND; - - expected_results[14] = Certificate_Status_Code::CERT_ISSUER_NOT_FOUND; - expected_results[15] = Certificate_Status_Code::VERIFIED; - expected_results[16] = Certificate_Status_Code::VERIFIED; - expected_results[17] = Certificate_Status_Code::VERIFIED; - expected_results[18] = Certificate_Status_Code::VERIFIED; - - expected_results[19] = Certificate_Status_Code::NO_REVOCATION_DATA; - expected_results[20] = Certificate_Status_Code::CERT_IS_REVOKED; - expected_results[21] = Certificate_Status_Code::CERT_IS_REVOKED; - - expected_results[22] = Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER; - expected_results[23] = Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER; - expected_results[24] = Certificate_Status_Code::VERIFIED; - expected_results[25] = Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER; - expected_results[26] = Certificate_Status_Code::VERIFIED; - expected_results[27] = Certificate_Status_Code::VERIFIED; - expected_results[28] = Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER; - expected_results[29] = Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER; - expected_results[30] = Certificate_Status_Code::VERIFIED; - - expected_results[31] = Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER; - expected_results[32] = Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER; - expected_results[33] = Certificate_Status_Code::VERIFIED; - - /* - Policy tests: a little trickier because there are other inputs - which affect the result. - - In the case of the tests currently in the suite, the default - method (with acceptable policy being "any-policy" and with no - explicit policy required), will almost always result in a verified - status. This is not particularly helpful. So, we should do several - different tests for each test set: - - 1) With the user policy as any-policy and no explicit policy - 2) With the user policy as any-policy and an explicit policy required - 3) With the user policy as test-policy-1 (2.16.840.1.101.3.1.48.1) and - an explicit policy required - 4) With the user policy as either test-policy-1 or test-policy-2 and an - explicit policy required - - This provides reasonably good coverage of the possible outcomes. - */ - - expected_results[34] = Certificate_Status_Code::VERIFIED; - expected_results[35] = Certificate_Status_Code::VERIFIED; - expected_results[36] = Certificate_Status_Code::VERIFIED; - expected_results[37] = Certificate_Status_Code::VERIFIED; - expected_results[38] = Certificate_Status_Code::VERIFIED; - expected_results[39] = Certificate_Status_Code::VERIFIED; - expected_results[40] = Certificate_Status_Code::VERIFIED; - expected_results[41] = Certificate_Status_Code::VERIFIED; - expected_results[42] = Certificate_Status_Code::VERIFIED; - expected_results[43] = Certificate_Status_Code::VERIFIED; - expected_results[44] = Certificate_Status_Code::VERIFIED; - - //expected_results[45] = Certificate_Status_Code::EXPLICIT_POLICY_REQUIRED; - //expected_results[46] = Certificate_Status_Code::ACCEPT; - //expected_results[47] = Certificate_Status_Code::EXPLICIT_POLICY_REQUIRED; - - expected_results[48] = Certificate_Status_Code::VERIFIED; - expected_results[49] = Certificate_Status_Code::VERIFIED; - expected_results[50] = Certificate_Status_Code::VERIFIED; - expected_results[51] = Certificate_Status_Code::VERIFIED; - expected_results[52] = Certificate_Status_Code::VERIFIED; - expected_results[53] = Certificate_Status_Code::VERIFIED; - - expected_results[54] = Certificate_Status_Code::CERT_CHAIN_TOO_LONG; - expected_results[55] = Certificate_Status_Code::CERT_CHAIN_TOO_LONG; - expected_results[56] = Certificate_Status_Code::VERIFIED; - expected_results[57] = Certificate_Status_Code::VERIFIED; - expected_results[58] = Certificate_Status_Code::CERT_CHAIN_TOO_LONG; - expected_results[59] = Certificate_Status_Code::CERT_CHAIN_TOO_LONG; - expected_results[60] = Certificate_Status_Code::CERT_CHAIN_TOO_LONG; - expected_results[61] = Certificate_Status_Code::CERT_CHAIN_TOO_LONG; - expected_results[62] = Certificate_Status_Code::VERIFIED; - expected_results[63] = Certificate_Status_Code::VERIFIED; - - expected_results[64] = Certificate_Status_Code::CRL_BAD_SIGNATURE; - - expected_results[65] = Certificate_Status_Code::NO_REVOCATION_DATA; - expected_results[66] = Certificate_Status_Code::NO_REVOCATION_DATA; - - expected_results[67] = Certificate_Status_Code::VERIFIED; - - expected_results[68] = Certificate_Status_Code::CERT_IS_REVOKED; - expected_results[69] = Certificate_Status_Code::CERT_IS_REVOKED; - expected_results[70] = Certificate_Status_Code::CERT_IS_REVOKED; - expected_results[71] = Certificate_Status_Code::CERT_IS_REVOKED; - expected_results[72] = Certificate_Status_Code::CRL_HAS_EXPIRED; - expected_results[73] = Certificate_Status_Code::CRL_HAS_EXPIRED; - expected_results[74] = Certificate_Status_Code::VERIFIED; - - /* These tests use weird CRL extensions which aren't supported yet */ - //expected_results[75] = ; - //expected_results[76] = ; - - return expected_results; - } - std::vector<Test::Result> NIST_Path_Validation_Tests::run() { std::vector<Test::Result> results; @@ -270,12 +138,12 @@ std::vector<Test::Result> NIST_Path_Validation_Tests::run() * - Tests #75 and #76 are skipped as they make use of relatively * obscure CRL extensions which are not supported. */ - const std::string root_test_dir = "src/tests/data/nist_x509/"; + const std::string nist_test_dir = Test::data_dir() + "/nist_x509"; try { // Do nothing, just test filesystem access - Botan::get_files_recursive(root_test_dir); + Botan::get_files_recursive(nist_test_dir); } catch(Botan::No_Filesystem_Access) { @@ -285,80 +153,60 @@ std::vector<Test::Result> NIST_Path_Validation_Tests::run() return results; } - const size_t total_tests = 76; - std::map<size_t, Botan::Path_Validation_Result::Code> expected_results = get_expected(); + std::map<std::string, std::string> expected = + read_results(Test::data_file("nist_x509/expected.txt")); - for(size_t test_no = 1; test_no <= total_tests; ++test_no) + for(auto i = expected.begin(); i != expected.end(); ++i) { - try - { - Test::Result result("NIST path validation"); - const std::string test_dir = root_test_dir + "/test" + (test_no <= 9 ? "0" : "") + std::to_string(test_no); + const std::string test_name = i->first; + const std::string expected_result = i->second; + printf("%s %s\n", test_name.c_str(), expected_result.c_str()); - const std::vector<std::string> all_files = Botan::get_files_recursive(test_dir); - if (all_files.empty()) - { - result.test_failure("No test files found in " + test_dir); - continue; - } - - std::vector<std::string> certs, crls; - std::string root_cert, to_verify; + const std::string test_dir = nist_test_dir + "/" + test_name; - for(const auto ¤t : all_files) - { - if(current.find("int") != std::string::npos && current.find(".crt") != std::string::npos) - certs.push_back(current); - else if(current.find("root.crt") != std::string::npos) - root_cert = current; - else if(current.find("end.crt") != std::string::npos) - to_verify = current; - else if(current.find(".crl") != std::string::npos) - crls.push_back(current); - } - - if(expected_results.find(test_no) == expected_results.end()) - { - result.test_note("Skipping test"); - continue; - } - - Botan::Certificate_Store_In_Memory store; + Test::Result result("NIST path validation"); - store.add_certificate(Botan::X509_Certificate(root_cert)); + const std::vector<std::string> all_files = Botan::get_files_recursive(test_dir); - Botan::X509_Certificate end_user(to_verify); + if(all_files.empty()) + { + result.test_failure("No test files found in " + test_dir); + results.push_back(result); + continue; + } - for(size_t i = 0; i != certs.size(); i++) - store.add_certificate(Botan::X509_Certificate(certs[i])); + Botan::Certificate_Store_In_Memory store; - for(size_t i = 0; i != crls.size(); i++) + for(auto&& file : all_files) + { + if(file.find(".crt") != std::string::npos && file != "end.crt") { - Botan::DataSource_Stream in(crls[i], true); + store.add_certificate(Botan::X509_Certificate(file)); + } + else if(file.find(".crl") != std::string::npos) + { + Botan::DataSource_Stream in(file, true); Botan::X509_CRL crl(in); store.add_crl(crl); + } } - Botan::Path_Validation_Restrictions restrictions(true); + Botan::X509_Certificate end_user(test_dir + "/end.crt"); - Botan::Path_Validation_Result validation_result = - Botan::x509_path_validate(end_user, - restrictions, - store); + Botan::Path_Validation_Restrictions restrictions(true); - auto expected = expected_results[test_no]; + Botan::Path_Validation_Result validation_result = + Botan::x509_path_validate(end_user, + restrictions, + store); - result.test_eq("path validation result", - validation_result.result_string(), - Botan::Path_Validation_Result::status_string(expected)); + result.test_eq(test_name + " path validation result", + validation_result.result_string(), + expected_result); - results.push_back(result); - } - catch(std::exception& e) - { - results.push_back(Test::Result::Failure("NIST X509 " + std::to_string(test_no), e.what())); - } + results.push_back(result); } + return results; } diff --git a/src/tests/tests.h b/src/tests/tests.h index e12f5f6de..f98194278 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -388,7 +388,6 @@ class Text_Based_Test : public Test std::set<std::string> m_required_keys; std::set<std::string> m_optional_keys; std::string m_output_key; - bool m_clear_between_cb = false; bool m_first = true; std::unique_ptr<std::ifstream> m_cur; |