PointGFp multi_exponentiate(const PointGFp& p1, const BigInt& z1, const PointGFp& p2, const BigInt& z2) { const PointGFp p3 = p1 + p2; PointGFp H(p1.curve); // create as zero size_t bits_left = std::max(z1.bits(), z2.bits()); std::vector<BigInt> ws(9); while(bits_left) { H.mult2(ws); const bool z1_b = z1.get_bit(bits_left - 1); const bool z2_b = z2.get_bit(bits_left - 1); if(z1_b == true && z2_b == true) H.add(p3, ws); else if(z1_b) H.add(p1, ws); else if(z2_b) H.add(p2, ws); --bits_left; } if(z1.is_negative() != z2.is_negative()) H.negate(); return H; }
PointGFp multi_exponentiate(const PointGFp& x, const BigInt& z1, const PointGFp& y, const BigInt& z2) { const size_t z_bits = round_up(std::max(z1.bits(), z2.bits()), 2); std::vector<BigInt> ws(PointGFp::WORKSPACE_SIZE); PointGFp x2 = x; x2.mult2(ws); const PointGFp x3(x2.plus(x, ws)); PointGFp y2 = y; y2.mult2(ws); const PointGFp y3(y2.plus(y, ws)); const PointGFp M[16] = { x.zero(), // 0000 x, // 0001 x2, // 0010 x3, // 0011 y, // 0100 y.plus(x, ws), // 0101 y.plus(x2, ws), // 0110 y.plus(x3, ws), // 0111 y2, // 1000 y2.plus(x, ws), // 1001 y2.plus(x2, ws), // 1010 y2.plus(x3, ws), // 1011 y3, // 1100 y3.plus(x, ws), // 1101 y3.plus(x2, ws), // 1110 y3.plus(x3, ws), // 1111 }; PointGFp H = x.zero(); for(size_t i = 0; i != z_bits; i += 2) { if(i > 0) { H.mult2(ws); H.mult2(ws); } const uint8_t z1_b = z1.get_substring(z_bits - i - 2, 2); const uint8_t z2_b = z2.get_substring(z_bits - i - 2, 2); const uint8_t z12 = (4*z2_b) + z1_b; H.add(M[z12], ws); } if(z1.is_negative() != z2.is_negative()) H.negate(); return H; }
BigInt Montgomery_Exponentation_State::exponentiation(const BigInt& scalar) const { const size_t exp_nibbles = (scalar.bits() + m_window_bits - 1) / m_window_bits; Montgomery_Int x(m_params, m_params->R1(), false); secure_vector<word> e_bits(m_params->p_words()); secure_vector<word> ws; for(size_t i = exp_nibbles; i > 0; --i) { for(size_t j = 0; j != m_window_bits; ++j) { x.square_this(ws); } const uint32_t nibble = scalar.get_substring(m_window_bits*(i-1), m_window_bits); const_time_lookup(e_bits, m_g, nibble); x.mul_by(e_bits, ws); } return x.value(); }
PointGFp operator*(const BigInt& scalar, const PointGFp& point) { //BOTAN_ASSERT(point.on_the_curve(), "Input is on the curve"); const CurveGFp& curve = point.get_curve(); const size_t scalar_bits = scalar.bits(); std::vector<BigInt> ws(9); PointGFp R[2] = { PointGFp(curve), point }; for(size_t i = scalar_bits; i > 0; i--) { const size_t b = scalar.get_bit(i - 1); R[b ^ 1].add(R[b], ws); R[b].mult2(ws); } if(scalar.is_negative()) R[0].negate(); //BOTAN_ASSERT(R[0].on_the_curve(), "Output is on the curve"); return R[0]; }
/* * Division Operator */ BigInt operator/(const BigInt& x, const BigInt& y) { if(y.sig_words() == 1 && is_power_of_2(y.word_at(0))) return (x >> (y.bits() - 1)); BigInt q, r; divide(x, y, q, r); return q; }
/* * Generate a random integer within given range */ BigInt BigInt::random_integer(RandomNumberGenerator& rng, const BigInt& min, const BigInt& max) { BigInt range = max - min; if(range <= 0) throw Invalid_Argument("random_integer: invalid min/max values"); return (min + (BigInt(rng, range.bits() + 2) % range)); }
/* * Set the base */ void Fixed_Window_Exponentiator::set_base(const BigInt& base) { m_window_bits = Power_Mod::window_bits(m_exp.bits(), base.bits(), m_hints); m_g.resize(1U << m_window_bits); m_g[0] = 1; m_g[1] = base; for(size_t i = 2; i != m_g.size(); ++i) m_g[i] = m_reducer.multiply(m_g[i-1], m_g[1]); }
/************************************************* * IF_Core Constructor * *************************************************/ IF_Core::IF_Core(RandomNumberGenerator& rng, const BigInt& e, const BigInt& n, const BigInt& d, const BigInt& p, const BigInt& q, const BigInt& d1, const BigInt& d2, const BigInt& c) { op = Engine_Core::if_op(e, n, d, p, q, d1, d2, c); if(BLINDING_BITS) { BigInt k(rng, std::min(n.bits()-1, BLINDING_BITS)); blinder = Blinder(power_mod(k, e, n), inverse_mod(k, n), n); } }
/* * Generate a random integer within given range */ BigInt BigInt::random_integer(RandomNumberGenerator& rng, const BigInt& min, const BigInt& max) { BigInt r; const size_t bits = max.bits(); do { r.randomize(rng, bits, false); } while(r < min || r >= max); return r; }
u64bit encrypt_number(u64bit cc_number, int length, const std::string& key_str, const std::string& tweak){ BigInt n = 1; for(int j=0; j<length; ++j){ n = n * 10; } u64bit cc_ranked = cc_rank(cc_number); SymmetricKey key = sha1(key_str); BigInt c = FPE::fe1_encrypt(n, cc_ranked, key, sha1(tweak)); if(c.bits() > 50) throw std::runtime_error("FPE produced a number too large"); u64bit enc_cc = 0; for(u32bit i = 0; i != 7; ++i) enc_cc = (enc_cc << 8) | c.byte_at(6-i); return cc_derank(enc_cc); }
BigInt Montgomery_Exponentation_State::exponentiation(const BigInt& k) const { const size_t exp_nibbles = (k.bits() + m_window_bits - 1) / m_window_bits; BigInt x = m_R_mod; const size_t z_size = 2*(m_p_words + 1); BigInt z(BigInt::Positive, z_size); secure_vector<word> workspace(z.size()); secure_vector<word> e(m_p_words); for(size_t i = exp_nibbles; i > 0; --i) { for(size_t j = 0; j != m_window_bits; ++j) { bigint_monty_sqr(z, x, m_p.data(), m_p_words, m_mod_prime, workspace.data()); x = z; } const uint32_t nibble = k.get_substring(m_window_bits*(i-1), m_window_bits); BigInt::const_time_lookup(e, m_g, nibble); bigint_mul(z.mutable_data(), z.size(), x.data(), x.size(), x.sig_words(), e.data(), m_p_words, m_p_words, workspace.data()); bigint_monty_redc(z.mutable_data(), m_p.data(), m_p_words, m_mod_prime, workspace.data()); x = z; } x.grow_to(2*m_p_words + 1); bigint_monty_redc(x.mutable_data(), m_p.data(), m_p_words, m_mod_prime, workspace.data()); return x; }
Blinder::Blinder(const BigInt& modulus, RandomNumberGenerator& rng, std::function<BigInt (const BigInt&)> fwd, std::function<BigInt (const BigInt&)> inv) : m_reducer(modulus), m_rng(rng), m_fwd_fn(fwd), m_inv_fn(inv), m_modulus_bits(modulus.bits()), m_e{}, m_d{}, m_counter{} { const BigInt k = blinding_nonce(); m_e = m_fwd_fn(k); m_d = m_inv_fn(k); }
Blinder::Blinder(const BigInt& modulus, std::function<BigInt (const BigInt&)> fwd_func, std::function<BigInt (const BigInt&)> inv_func) { m_reducer = Modular_Reducer(modulus); #if defined(BOTAN_HAS_SYSTEM_RNG) auto& rng = system_rng(); #else AutoSeeded_RNG rng; #endif const BigInt k(rng, modulus.bits() - 1); m_e = fwd_func(k); m_d = inv_func(k); }
PointGFp operator*(const BigInt& scalar, const PointGFp& point) { //BOTAN_ASSERT(point.on_the_curve(), "Input is on the curve"); const CurveGFp& curve = point.get_curve(); const size_t scalar_bits = scalar.bits(); std::vector<BigInt> ws(9); if(scalar_bits <= 2) { const byte abs_val = scalar.byte_at(0); if(abs_val == 0) return PointGFp::zero_of(curve); PointGFp result = point; if(abs_val == 2) result.mult2(ws); if(scalar.is_negative()) result.negate(); return result; } PointGFp R[2] = { PointGFp(curve), point }; for(size_t i = scalar_bits; i > 0; i--) { const size_t b = scalar.get_bit(i - 1); R[b ^ 1].add(R[b], ws); R[b].mult2(ws); } if(scalar.is_negative()) R[0].negate(); //BOTAN_ASSERT(R[0].on_the_curve(), "Output is on the curve"); return R[0]; }
/* * Set the base */ void Montgomery_Exponentiator::set_base(const BigInt& base) { m_window_bits = Power_Mod::window_bits(m_exp.bits(), base.bits(), m_hints); m_g.resize((1 << m_window_bits)); BigInt z(BigInt::Positive, 2 * (m_mod_words + 1)); secure_vector<word> workspace(z.size()); m_g[0] = 1; bigint_monty_mul(z, m_g[0], m_R2_mod, m_modulus.data(), m_mod_words, m_mod_prime, workspace.data()); m_g[0] = z; m_g[1] = (base >= m_modulus) ? (base % m_modulus) : base; bigint_monty_mul(z, m_g[1], m_R2_mod, m_modulus.data(), m_mod_words, m_mod_prime, workspace.data()); m_g[1] = z; const BigInt& x = m_g[1]; for(size_t i = 2; i != m_g.size(); ++i) { const BigInt& y = m_g[i-1]; bigint_monty_mul(z, x, y, m_modulus.data(), m_mod_words, m_mod_prime, workspace.data()); m_g[i] = z; } }
PointGFp operator*(const BigInt& scalar, const PointGFp& point) { const CurveGFp& curve = point.get_curve(); if(scalar.is_zero()) return PointGFp(curve); // zero point std::vector<BigInt> ws(9); if(scalar.abs() <= 2) // special cases for small values { byte value = scalar.abs().byte_at(0); PointGFp result = point; if(value == 2) result.mult2(ws); if(scalar.is_negative()) result.negate(); return result; } const size_t scalar_bits = scalar.bits(); #if 0 PointGFp x1 = PointGFp(curve); PointGFp x2 = point; size_t bits_left = scalar_bits; // Montgomery Ladder while(bits_left) { const bool bit_set = scalar.get_bit(bits_left - 1); if(bit_set) { x1.add(x2, ws); x2.mult2(ws); } else { x2.add(x1, ws); x1.mult2(ws); } --bits_left; } if(scalar.is_negative()) x1.negate(); return x1; #else const size_t window_size = 4; std::vector<PointGFp> Ps(1 << window_size); Ps[0] = PointGFp(curve); Ps[1] = point; for(size_t i = 2; i != Ps.size(); ++i) { Ps[i] = Ps[i-1]; Ps[i].add(point, ws); } PointGFp H(curve); // create as zero size_t bits_left = scalar_bits; while(bits_left >= window_size) { for(size_t i = 0; i != window_size; ++i) H.mult2(ws); const u32bit nibble = scalar.get_substring(bits_left - window_size, window_size); H.add(Ps[nibble], ws); bits_left -= window_size; } while(bits_left) { H.mult2(ws); if(scalar.get_bit(bits_left-1)) H.add(point, ws); --bits_left; } if(scalar.is_negative()) H.negate(); return H; #endif }
BigInt monty_multi_exp(std::shared_ptr<const Montgomery_Params> params_p, const BigInt& x_bn, const BigInt& z1, const BigInt& y_bn, const BigInt& z2) { if(z1.is_negative() || z2.is_negative()) throw Invalid_Argument("multi_exponentiate exponents must be positive"); const size_t z_bits = round_up(std::max(z1.bits(), z2.bits()), 2); secure_vector<word> ws; const Montgomery_Int one(params_p, params_p->R1(), false); //const Montgomery_Int one(params_p, 1); const Montgomery_Int x1(params_p, x_bn); const Montgomery_Int x2 = x1.square(ws); const Montgomery_Int x3 = x2.mul(x1, ws); const Montgomery_Int y1(params_p, y_bn); const Montgomery_Int y2 = y1.square(ws); const Montgomery_Int y3 = y2.mul(y1, ws); const Montgomery_Int y1x1 = y1.mul(x1, ws); const Montgomery_Int y1x2 = y1.mul(x2, ws); const Montgomery_Int y1x3 = y1.mul(x3, ws); const Montgomery_Int y2x1 = y2.mul(x1, ws); const Montgomery_Int y2x2 = y2.mul(x2, ws); const Montgomery_Int y2x3 = y2.mul(x3, ws); const Montgomery_Int y3x1 = y3.mul(x1, ws); const Montgomery_Int y3x2 = y3.mul(x2, ws); const Montgomery_Int y3x3 = y3.mul(x3, ws); const Montgomery_Int* M[16] = { &one, &x1, // 0001 &x2, // 0010 &x3, // 0011 &y1, // 0100 &y1x1, &y1x2, &y1x3, &y2, // 1000 &y2x1, &y2x2, &y2x3, &y3, // 1100 &y3x1, &y3x2, &y3x3 }; Montgomery_Int H = one; for(size_t i = 0; i != z_bits; i += 2) { if(i > 0) { H.square_this(ws); H.square_this(ws); } const uint8_t z1_b = z1.get_substring(z_bits - i - 2, 2); const uint8_t z2_b = z2.get_substring(z_bits - i - 2, 2); const uint8_t z12 = (4*z2_b) + z1_b; H.mul_by(*M[z12], ws); } return H.value(); }
PointGFp Blinded_Point_Multiply::blinded_multiply(const BigInt& scalar_in, RandomNumberGenerator& rng) { if(scalar_in.is_negative()) throw std::invalid_argument("Blinded_Point_Multiply scalar must be positive"); #if BOTAN_POINTGFP_SCALAR_BLINDING_BITS > 0 // 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 scalar = scalar_in + m_order * mask; #else const BigInt& scalar = scalar_in; #endif const size_t scalar_bits = scalar.bits(); // Randomize each point representation (Coron's 3rd countermeasure) 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; R.randomize_repr(rng); /* Algorithm 7 from "Randomizing the Montgomery Powering Ladder" Duc-Phong Le, Chik How Tan and Michael Tunstall http://eprint.iacr.org/2015/657 It takes a random walk through (a subset of) the set of addition chains that end in k. */ for(size_t i = scalar_bits; i > 0; i--) { const int32_t ki = scalar.get_bit(i); // choose gamma from -h,...,h const int32_t gamma = static_cast<int32_t>((rng.next_byte() % (2*m_h))) - m_h; const int32_t l = gamma - 2*alpha + ki - (ki ^ 1); R.mult2(m_ws); R.add(m_U.at(3*m_h + 1 + l), m_ws); alpha = gamma; } 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 u32bit 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 u32bit nibble = scalar.get_substring((windows-1)*m_h, m_h); R.add(m_U[nibble], m_ws); windows--; } } #endif //BOTAN_ASSERT(R.on_the_curve(), "Output is on the curve"); return R; }
/* * Create a new Client Key Exchange message */ Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io, Handshake_State& state, const Policy& policy, Credentials_Manager& creds, const Public_Key* server_public_key, const std::string& hostname, RandomNumberGenerator& rng) { const std::string kex_algo = state.ciphersuite().kex_algo(); if(kex_algo == "PSK") { std::string identity_hint = ""; if(state.server_kex()) { TLS_Data_Reader reader("ClientKeyExchange", state.server_kex()->params()); identity_hint = reader.get_string(2, 0, 65535); } const std::string psk_identity = creds.psk_identity("tls-client", hostname, identity_hint); append_tls_length_value(m_key_material, psk_identity, 2); SymmetricKey psk = creds.psk("tls-client", hostname, psk_identity); std::vector<byte> zeros(psk.length()); append_tls_length_value(m_pre_master, zeros, 2); append_tls_length_value(m_pre_master, psk.bits_of(), 2); } else if(state.server_kex()) { TLS_Data_Reader reader("ClientKeyExchange", state.server_kex()->params()); SymmetricKey psk; if(kex_algo == "DHE_PSK" || kex_algo == "ECDHE_PSK") { std::string identity_hint = reader.get_string(2, 0, 65535); const std::string psk_identity = creds.psk_identity("tls-client", hostname, identity_hint); append_tls_length_value(m_key_material, psk_identity, 2); psk = creds.psk("tls-client", hostname, psk_identity); } if(kex_algo == "DH" || kex_algo == "DHE_PSK") { BigInt p = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); BigInt g = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); BigInt Y = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); if(reader.remaining_bytes()) throw Decoding_Error("Bad params size for DH key exchange"); if(p.bits() < policy.minimum_dh_group_size()) throw TLS_Exception(Alert::INSUFFICIENT_SECURITY, "Server sent DH group of " + std::to_string(p.bits()) + " bits, policy requires at least " + std::to_string(policy.minimum_dh_group_size())); /* * A basic check for key validity. As we do not know q here we * cannot check that Y is in the right subgroup. However since * our key is ephemeral there does not seem to be any * advantage to bogus keys anyway. */ if(Y <= 1 || Y >= p - 1) throw TLS_Exception(Alert::INSUFFICIENT_SECURITY, "Server sent bad DH key for DHE exchange"); DL_Group group(p, g); if(!group.verify_group(rng, false)) throw TLS_Exception(Alert::INSUFFICIENT_SECURITY, "DH group validation failed"); DH_PublicKey counterparty_key(group, Y); DH_PrivateKey priv_key(rng, group); PK_Key_Agreement ka(priv_key, "Raw"); secure_vector<byte> dh_secret = CT::strip_leading_zeros( ka.derive_key(0, counterparty_key.public_value()).bits_of()); if(kex_algo == "DH") m_pre_master = dh_secret; else { append_tls_length_value(m_pre_master, dh_secret, 2); append_tls_length_value(m_pre_master, psk.bits_of(), 2); } append_tls_length_value(m_key_material, priv_key.public_value(), 2); } else if(kex_algo == "ECDH" || kex_algo == "ECDHE_PSK") { const byte curve_type = reader.get_byte(); if(curve_type != 3) throw Decoding_Error("Server sent non-named ECC curve"); const u16bit curve_id = reader.get_u16bit(); const std::string name = Supported_Elliptic_Curves::curve_id_to_name(curve_id); if(name == "") throw Decoding_Error("Server sent unknown named curve " + std::to_string(curve_id)); EC_Group group(name); std::vector<byte> ecdh_key = reader.get_range<byte>(1, 1, 255); ECDH_PublicKey counterparty_key(group, OS2ECP(ecdh_key, group.get_curve())); ECDH_PrivateKey priv_key(rng, group); PK_Key_Agreement ka(priv_key, "Raw"); secure_vector<byte> ecdh_secret = ka.derive_key(0, counterparty_key.public_value()).bits_of(); if(kex_algo == "ECDH") m_pre_master = ecdh_secret; else { append_tls_length_value(m_pre_master, ecdh_secret, 2); append_tls_length_value(m_pre_master, psk.bits_of(), 2); } append_tls_length_value(m_key_material, priv_key.public_value(), 1); } #if defined(BOTAN_HAS_SRP6) else if(kex_algo == "SRP_SHA") { const BigInt N = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); const BigInt g = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); std::vector<byte> salt = reader.get_range<byte>(1, 1, 255); const BigInt B = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); const std::string srp_group = srp6_group_identifier(N, g); const std::string srp_identifier = creds.srp_identifier("tls-client", hostname); const std::string srp_password = creds.srp_password("tls-client", hostname, srp_identifier); std::pair<BigInt, SymmetricKey> srp_vals = srp6_client_agree(srp_identifier, srp_password, srp_group, "SHA-1", salt, B, rng); append_tls_length_value(m_key_material, BigInt::encode(srp_vals.first), 2); m_pre_master = srp_vals.second.bits_of(); } #endif else { throw Internal_Error("Client_Key_Exchange: Unknown kex " + kex_algo); } reader.assert_done(); } else { // No server key exchange msg better mean RSA kex + RSA key in cert if(kex_algo != "RSA") throw Unexpected_Message("No server kex but negotiated kex " + kex_algo); if(!server_public_key) throw Internal_Error("No server public key for RSA exchange"); if(auto rsa_pub = dynamic_cast<const RSA_PublicKey*>(server_public_key)) { const Protocol_Version offered_version = state.client_hello()->version(); m_pre_master = rng.random_vec(48); m_pre_master[0] = offered_version.major_version(); m_pre_master[1] = offered_version.minor_version(); PK_Encryptor_EME encryptor(*rsa_pub, "PKCS1v15"); const std::vector<byte> encrypted_key = encryptor.encrypt(m_pre_master, rng); append_tls_length_value(m_key_material, encrypted_key, 2); } else throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Expected a RSA key in server cert but got " + server_public_key->algo_name()); } state.hash().update(io.send(*this)); }
/* * Set the exponent */ void Montgomery_Exponentiator::set_exponent(const BigInt& exp) { m_exp = exp; m_exp_bits = exp.bits(); }
// Botan is released under the Simplified BSD License (see license.txt) #include "catchy_tests.h" #if defined(BOTAN_HAS_BIGINT) #include <botan/bigint.h> using namespace Botan; TEST_CASE("Bigint basics", "[bigint]") { SECTION("in 0-bit border") { BigInt a(0u); CHECK_THAT(a.bits(), Equals(0)); CHECK_THAT(a.bytes(), Equals(0)); CHECK_THAT(a.to_u32bit(), Equals(0)); } SECTION("above 0-bit border") { BigInt a(1u); CHECK_THAT(a.bits(), Equals(1)); CHECK_THAT(a.bytes(), Equals(1)); CHECK_THAT(a.to_u32bit(), Equals(1)); } SECTION("in 8-bit border") { BigInt a(255u); CHECK_THAT(a.bits(), Equals(8)); CHECK_THAT(a.bytes(), Equals(1));
void Montgomery_Exponentiator::set_base(const BigInt& base) { size_t window_bits = Power_Mod::window_bits(m_e.bits(), base.bits(), m_hints); m_monty = monty_precompute(m_monty_params, base, window_bits); }