diff options
-rwxr-xr-x | configure.py | 70 | ||||
-rw-r--r-- | src/build-data/buildh.in | 17 | ||||
-rw-r--r-- | src/cli/timing_tests.cpp | 454 | ||||
-rw-r--r-- | src/lib/asn1/x509_dn.cpp | 1 | ||||
-rw-r--r-- | src/lib/math/ec_gfp/point_gfp.cpp | 50 | ||||
-rw-r--r-- | src/lib/pk_pad/iso9796/iso9796.cpp | 40 | ||||
-rw-r--r-- | src/lib/pubkey/curve25519/curve25519.cpp | 2 | ||||
-rw-r--r-- | src/lib/pubkey/pbes2/pbes2.cpp | 2 | ||||
-rw-r--r-- | src/lib/pubkey/rsa/rsa.cpp | 1 | ||||
-rw-r--r-- | src/lib/pubkey/x509_key.cpp | 2 | ||||
-rw-r--r-- | src/lib/x509/x509_ext.cpp | 2 | ||||
-rw-r--r-- | src/lib/x509/x509_obj.cpp | 1 | ||||
-rw-r--r-- | src/lib/x509/x509cert.cpp | 1 | ||||
-rw-r--r-- | src/tests/test_ocsp.cpp | 3 | ||||
-rw-r--r-- | src/tests/test_pubkey.cpp | 12 |
15 files changed, 539 insertions, 119 deletions
diff --git a/configure.py b/configure.py index 9f4246775..7e40545dd 100755 --- a/configure.py +++ b/configure.py @@ -38,7 +38,7 @@ import optparse # pylint: disable=deprecated-module if 'dont_write_bytecode' in sys.__dict__: sys.dont_write_bytecode = True -import botan_version +import botan_version # pylint: disable=wrong-import-position class ConfigureError(Exception): pass @@ -521,8 +521,6 @@ def lex_me_harder(infofile, to_obj, allowed_groups, name_val_pairs): Generic lexer function for info.txt and src/build-data files """ - to_obj.infofile = infofile - # Format as a nameable Python variable def py_var(group): return group.replace(':', '_') @@ -536,18 +534,6 @@ def lex_me_harder(infofile, to_obj, allowed_groups, name_val_pairs): def __str__(self): return '%s at %s:%d' % (self.msg, infofile, self.line) - (dirname, basename) = os.path.split(infofile) - - to_obj.lives_in = dirname - if basename == 'info.txt': - (obj_dir, to_obj.basename) = os.path.split(dirname) - if os.access(os.path.join(obj_dir, 'info.txt'), os.R_OK): - to_obj.parent_module = os.path.basename(obj_dir) - else: - to_obj.parent_module = None - else: - to_obj.basename = basename.replace('.txt', '') - lexer = shlex.shlex(open(infofile), infofile, posix=True) lexer.wordchars += '|:.<>/,-!+' # handle various funky chars in info.txt @@ -609,13 +595,33 @@ def force_to_dict(l): return dict(zip(l[::3], l[2::3])) -class ModuleInfo(object): + +class InfoObject(object): + def __init__(self, infofile): + """ + Constructor sets members `infofile`, `lives_in`, `parent_module` and `basename` + """ + + self.infofile = infofile + (dirname, basename) = os.path.split(infofile) + self.lives_in = dirname + if basename == 'info.txt': + (obj_dir, self.basename) = os.path.split(dirname) + if os.access(os.path.join(obj_dir, 'info.txt'), os.R_OK): + self.parent_module = os.path.basename(obj_dir) + else: + self.parent_module = None + else: + self.basename = basename.replace('.txt', '') + + +class ModuleInfo(InfoObject): """ Represents the information about a particular module """ def __init__(self, infofile): - + super(ModuleInfo, self).__init__(infofile) lex_me_harder(infofile, self, ['header:internal', 'header:public', 'header:external', 'requires', 'os', 'arch', @@ -791,8 +797,9 @@ class ModuleInfo(object): return 0 return 1 -class ModulePolicyInfo(object): +class ModulePolicyInfo(InfoObject): def __init__(self, infofile): + super(ModulePolicyInfo, self).__init__(infofile) lex_me_harder(infofile, self, ['required', 'if_available', 'prohibited'], {}) @@ -809,8 +816,9 @@ class ModulePolicyInfo(object): check('prohibited', self.prohibited) -class ArchInfo(object): +class ArchInfo(InfoObject): def __init__(self, infofile): + super(ArchInfo, self).__init__(infofile) lex_me_harder(infofile, self, ['aliases', 'submodels', 'submodel_aliases', 'isa_extensions'], {'endian': None, @@ -893,8 +901,9 @@ class ArchInfo(object): return macros -class CompilerInfo(object): +class CompilerInfo(InfoObject): def __init__(self, infofile): + super(CompilerInfo, self).__init__(infofile) lex_me_harder(infofile, self, ['so_link_commands', 'binary_link_commands', 'mach_opt', 'mach_abi_linking', 'isa_flags'], {'binary_name': None, @@ -1095,8 +1104,9 @@ class CompilerInfo(object): return ['BUILD_COMPILER_IS_' + self.macro_name] -class OsInfo(object): +class OsInfo(InfoObject): def __init__(self, infofile): + super(OsInfo, self).__init__(infofile) lex_me_harder(infofile, self, ['aliases', 'target_features'], {'os_type': None, @@ -1307,7 +1317,7 @@ def gen_bakefile(build_config, options, external_libs): # global options f.write('includedirs += build/include/;\n') - + for lib in external_libs.split(" "): f.write('libs += "%s";\n' %lib.replace('.lib', '')) @@ -2348,7 +2358,7 @@ def main(argv=None): time.sleep(0.1) # Final attempt, pass any exceptions up to caller. - os.makedirs(dir) + os.makedirs(directory) try: if options.clean_build_tree: @@ -2357,12 +2367,12 @@ def main(argv=None): if e.errno != errno.ENOENT: logging.error('Problem while removing build dir: %s' % (e)) - for dir in build_config.build_dirs: + for build_dir in build_config.build_dirs: try: - robust_makedirs(dir) + robust_makedirs(build_dir) except OSError as e: if e.errno != errno.EEXIST: - logging.error('Error while creating "%s": %s' % (dir, e)) + logging.error('Error while creating "%s": %s' % (build_dir, e)) def write_template(sink, template): try: @@ -2387,15 +2397,15 @@ def main(argv=None): link_method = choose_link_method(options) - def link_headers(headers, type, dir): - logging.debug('Linking %d %s header files in %s' % (len(headers), type, dir)) + def link_headers(headers, visibility, directory): + logging.debug('Linking %d %s header files in %s' % (len(headers), visibility, directory)) for header_file in headers: try: - portable_symlink(header_file, dir, link_method) + portable_symlink(header_file, directory, link_method) except OSError as e: if e.errno != errno.EEXIST: - raise ConfigureError('Error linking %s into %s: %s' % (header_file, dir, e)) + raise ConfigureError('Error linking %s into %s: %s' % (header_file, directory, e)) link_headers(build_config.public_headers, 'public', build_config.botan_include_dir) diff --git a/src/build-data/buildh.in b/src/build-data/buildh.in index de2b5c8b5..1c389fe02 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 diff --git a/src/cli/timing_tests.cpp b/src/cli/timing_tests.cpp new file mode 100644 index 000000000..7177cdc25 --- /dev/null +++ b/src/cli/timing_tests.cpp @@ -0,0 +1,454 @@ +/* +* 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 +* +* (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/extra_tests/timing/timing-tests/data/ --warmup-runs=1000 --measurement-runs=10000") + {} + + virtual void go() + { + 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 + { + // 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 + + return nullptr; + } + + +} diff --git a/src/lib/asn1/x509_dn.cpp b/src/lib/asn1/x509_dn.cpp index e5cd2b8cc..a7ceeef4c 100644 --- a/src/lib/asn1/x509_dn.cpp +++ b/src/lib/asn1/x509_dn.cpp @@ -269,7 +269,6 @@ void X509_DN::decode_from(BER_Decoder& source) rdn.start_cons(SEQUENCE) .decode(oid) .decode(str) - .verify_end() .end_cons(); add_attribute(oid, str.value()); 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/pk_pad/iso9796/iso9796.cpp b/src/lib/pk_pad/iso9796/iso9796.cpp index f123a7e15..f56689389 100644 --- a/src/lib/pk_pad/iso9796/iso9796.cpp +++ b/src/lib/pk_pad/iso9796/iso9796.cpp @@ -9,6 +9,7 @@ #include <botan/mgf1.h> #include <botan/internal/bit_ops.h> #include <botan/hash_id.h> +#include <botan/internal/ct_utils.h> namespace Botan { @@ -125,7 +126,8 @@ bool iso9796_verification(const secure_vector<uint8_t>& const_coded, } secure_vector<uint8_t> coded = const_coded; - + + CT::poison(coded.data(), coded.size()); //remove mask uint8_t* DB = coded.data(); const size_t DB_size = coded.size() - HASH_SIZE - tLength; @@ -137,19 +139,26 @@ bool iso9796_verification(const secure_vector<uint8_t>& const_coded, DB[0] &= 0x7F; //recover msg1 and salt - size_t msg1_offset = 0; - for(size_t j = 0; j != DB_size; ++j) + size_t msg1_offset = 1; + uint8_t waiting_for_delim = 0xFF; + uint8_t bad_input = 0; + for(size_t j = 0; j < DB_size; ++j) { - if(DB[j] == 0x01) - { - msg1_offset = j + 1; - break; - } - } - if(msg1_offset == 0) - { - return false; + const uint8_t one_m = CT::is_equal<uint8_t>(DB[j], 0x01); + const uint8_t zero_m = CT::is_zero(DB[j]); + const uint8_t add_m = waiting_for_delim & zero_m; + + bad_input |= waiting_for_delim & ~(zero_m | one_m); + msg1_offset += CT::select<uint8_t>(add_m, 1, 0); + + waiting_for_delim &= zero_m; } + + //invalid, if delimiter 0x01 was not found or msg1_offset is too big + bad_input |= waiting_for_delim; + bad_input |= CT::is_less(coded.size(), tLength + HASH_SIZE + msg1_offset + SALT_SIZE); + //in case that msg1_offset is too big, just continue with offset = 0. + msg1_offset = CT::select<size_t>(bad_input, 0, msg1_offset); secure_vector<uint8_t> msg1(coded.begin() + msg1_offset, coded.end() - tLength - HASH_SIZE - SALT_SIZE); secure_vector<uint8_t> salt(coded.begin() + msg1_offset + msg1.size(), @@ -186,9 +195,12 @@ bool iso9796_verification(const secure_vector<uint8_t>& const_coded, hash->update(msg2); hash->update(salt); secure_vector<uint8_t> H2 = hash->final(); - + //check if H3 == H2 - return same_mem(H3.data(), H2.data(), HASH_SIZE); + bad_input |= CT::is_equal<uint8_t>(same_mem(H3.data(), H2.data(), HASH_SIZE), false); + CT::unpoison(coded.data(), coded.size()); + + return (bad_input == 0); } } diff --git a/src/lib/pubkey/curve25519/curve25519.cpp b/src/lib/pubkey/curve25519/curve25519.cpp index 4908bf46f..070b8e841 100644 --- a/src/lib/pubkey/curve25519/curve25519.cpp +++ b/src/lib/pubkey/curve25519/curve25519.cpp @@ -52,7 +52,6 @@ Curve25519_PublicKey::Curve25519_PublicKey(const AlgorithmIdentifier&, BER_Decoder(key_bits) .start_cons(SEQUENCE) .decode(m_public, OCTET_STRING) - .verify_end() .end_cons(); size_check(m_public.size(), "public key"); @@ -81,7 +80,6 @@ Curve25519_PrivateKey::Curve25519_PrivateKey(const AlgorithmIdentifier&, .start_cons(SEQUENCE) .decode(m_public, OCTET_STRING) .decode(m_private, OCTET_STRING) - .verify_end() .end_cons(); size_check(m_public.size(), "public key"); diff --git a/src/lib/pubkey/pbes2/pbes2.cpp b/src/lib/pubkey/pbes2/pbes2.cpp index 3f1000170..01bab76bb 100644 --- a/src/lib/pubkey/pbes2/pbes2.cpp +++ b/src/lib/pubkey/pbes2/pbes2.cpp @@ -116,7 +116,6 @@ pbes2_decrypt(const secure_vector<uint8_t>& key_bits, .start_cons(SEQUENCE) .decode(kdf_algo) .decode(enc_algo) - .verify_end() .end_cons(); AlgorithmIdentifier prf_algo; @@ -136,7 +135,6 @@ pbes2_decrypt(const secure_vector<uint8_t>& key_bits, .decode_optional(prf_algo, SEQUENCE, CONSTRUCTED, AlgorithmIdentifier("HMAC(SHA-160)", AlgorithmIdentifier::USE_NULL_PARAM)) - .verify_end() .end_cons(); const std::string cipher = OIDS::lookup(enc_algo.oid); diff --git a/src/lib/pubkey/rsa/rsa.cpp b/src/lib/pubkey/rsa/rsa.cpp index 1a287473a..d8deccab3 100644 --- a/src/lib/pubkey/rsa/rsa.cpp +++ b/src/lib/pubkey/rsa/rsa.cpp @@ -58,7 +58,6 @@ RSA_PublicKey::RSA_PublicKey(const AlgorithmIdentifier&, .start_cons(SEQUENCE) .decode(m_n) .decode(m_e) - .verify_end() .end_cons(); } diff --git a/src/lib/pubkey/x509_key.cpp b/src/lib/pubkey/x509_key.cpp index 700020901..6fadeb70d 100644 --- a/src/lib/pubkey/x509_key.cpp +++ b/src/lib/pubkey/x509_key.cpp @@ -46,7 +46,6 @@ Public_Key* load_key(DataSource& source) .start_cons(SEQUENCE) .decode(alg_id) .decode(key_bits, BIT_STRING) - .verify_end() .end_cons(); } else @@ -59,7 +58,6 @@ Public_Key* load_key(DataSource& source) .start_cons(SEQUENCE) .decode(alg_id) .decode(key_bits, BIT_STRING) - .verify_end() .end_cons(); } diff --git a/src/lib/x509/x509_ext.cpp b/src/lib/x509/x509_ext.cpp index 199ca6bcc..965c8efcf 100644 --- a/src/lib/x509/x509_ext.cpp +++ b/src/lib/x509/x509_ext.cpp @@ -219,7 +219,6 @@ void Extensions::decode_from(BER_Decoder& from_source) .decode(oid) .decode_optional(critical, BOOLEAN, UNIVERSAL, false) .decode(value, OCTET_STRING) - .verify_end() .end_cons(); m_extensions_raw.emplace(oid, std::make_pair(value, critical)); @@ -300,7 +299,6 @@ void Basic_Constraints::decode_inner(const std::vector<uint8_t>& in) .start_cons(SEQUENCE) .decode_optional(m_is_ca, BOOLEAN, UNIVERSAL, false) .decode_optional(m_path_limit, INTEGER, UNIVERSAL, NO_CERT_PATH_LIMIT) - .verify_end() .end_cons(); if(m_is_ca == false) diff --git a/src/lib/x509/x509_obj.cpp b/src/lib/x509/x509_obj.cpp index cc97c1f15..43e92d322 100644 --- a/src/lib/x509/x509_obj.cpp +++ b/src/lib/x509/x509_obj.cpp @@ -105,7 +105,6 @@ void X509_Object::decode_from(BER_Decoder& from) .end_cons() .decode(m_sig_algo) .decode(m_sig, BIT_STRING) - .verify_end() .end_cons(); } diff --git a/src/lib/x509/x509cert.cpp b/src/lib/x509/x509cert.cpp index e53034dce..b6e15a3e0 100644 --- a/src/lib/x509/x509cert.cpp +++ b/src/lib/x509/x509cert.cpp @@ -95,7 +95,6 @@ void X509_Certificate::force_decode() .start_cons(SEQUENCE) .decode(start) .decode(end) - .verify_end() .end_cons() .decode(dn_subject); diff --git a/src/tests/test_ocsp.cpp b/src/tests/test_ocsp.cpp index 41faa5edf..3fc1ad765 100644 --- a/src/tests/test_ocsp.cpp +++ b/src/tests/test_ocsp.cpp @@ -148,6 +148,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 +169,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_pubkey.cpp b/src/tests/test_pubkey.cpp index 712fa8df4..9a8c5d2e7 100644 --- a/src/tests/test_pubkey.cpp +++ b/src/tests/test_pubkey.cpp @@ -42,10 +42,18 @@ void check_invalid_signatures(Test::Result& result, { const std::vector<uint8_t> bad_sig = Test::mutate_vec(signature); - if(!result.test_eq("incorrect signature invalid", - verifier.verify_message(message, bad_sig), false)) + try + { + if(!result.test_eq("incorrect signature invalid", + verifier.verify_message(message, bad_sig), false)) + { + result.test_note("Accepted invalid signature " + Botan::hex_encode(bad_sig)); + } + } + catch(std::exception& e) { result.test_note("Accepted invalid signature " + Botan::hex_encode(bad_sig)); + result.test_failure("Modified signature rejected with exception", e.what()); } } } |