aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xconfigure.py70
-rw-r--r--src/build-data/buildh.in17
-rw-r--r--src/cli/timing_tests.cpp454
-rw-r--r--src/lib/asn1/x509_dn.cpp1
-rw-r--r--src/lib/math/ec_gfp/point_gfp.cpp50
-rw-r--r--src/lib/pk_pad/iso9796/iso9796.cpp40
-rw-r--r--src/lib/pubkey/curve25519/curve25519.cpp2
-rw-r--r--src/lib/pubkey/pbes2/pbes2.cpp2
-rw-r--r--src/lib/pubkey/rsa/rsa.cpp1
-rw-r--r--src/lib/pubkey/x509_key.cpp2
-rw-r--r--src/lib/x509/x509_ext.cpp2
-rw-r--r--src/lib/x509/x509_obj.cpp1
-rw-r--r--src/lib/x509/x509cert.cpp1
-rw-r--r--src/tests/test_ocsp.cpp3
-rw-r--r--src/tests/test_pubkey.cpp12
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());
}
}
}