//static CPUID::Endian_status CPUID::runtime_check_endian() { // Check runtime endian const uint32_t endian32 = 0x01234567; const uint8_t* e8 = reinterpret_cast<const uint8_t*>(&endian32); Endian_status endian = ENDIAN_UNKNOWN; if(e8[0] == 0x01 && e8[1] == 0x23 && e8[2] == 0x45 && e8[3] == 0x67) { endian = ENDIAN_BIG; } else if(e8[0] == 0x67 && e8[1] == 0x45 && e8[2] == 0x23 && e8[3] == 0x01) { endian = ENDIAN_LITTLE; } else { throw Internal_Error("Unexpected endian at runtime, neither big nor little"); } // If we were compiled with a known endian, verify it matches at runtime #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) BOTAN_ASSERT(endian == ENDIAN_LITTLE, "Build and runtime endian match"); #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) BOTAN_ASSERT(endian == ENDIAN_BIG, "Build and runtime endian match"); #endif return endian; }
void Threefish_512::encrypt_n(const byte in[], byte out[], size_t blocks) const { BOTAN_ASSERT(m_K.size() == 9, "Key was set"); BOTAN_ASSERT(m_T.size() == 3, "Tweak was set"); for(size_t i = 0; i != blocks; ++i) { u64bit X0 = load_le<u64bit>(in, 0); u64bit X1 = load_le<u64bit>(in, 1); u64bit X2 = load_le<u64bit>(in, 2); u64bit X3 = load_le<u64bit>(in, 3); u64bit X4 = load_le<u64bit>(in, 4); u64bit X5 = load_le<u64bit>(in, 5); u64bit X6 = load_le<u64bit>(in, 6); u64bit X7 = load_le<u64bit>(in, 7); THREEFISH_INJECT_KEY(0); THREEFISH_ENC_8_ROUNDS(1,2); THREEFISH_ENC_8_ROUNDS(3,4); THREEFISH_ENC_8_ROUNDS(5,6); THREEFISH_ENC_8_ROUNDS(7,8); THREEFISH_ENC_8_ROUNDS(9,10); THREEFISH_ENC_8_ROUNDS(11,12); THREEFISH_ENC_8_ROUNDS(13,14); THREEFISH_ENC_8_ROUNDS(15,16); THREEFISH_ENC_8_ROUNDS(17,18); store_le(out, X0, X1, X2, X3, X4, X5, X6, X7); in += 64; out += 64; } }
void EAX_Decryption::finish(secure_vector<byte>& buffer, size_t offset) { BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); const size_t sz = buffer.size() - offset; byte* buf = buffer.data() + offset; BOTAN_ASSERT(sz >= tag_size(), "Have the tag as part of final input"); const size_t remaining = sz - tag_size(); if(remaining) { m_cmac->update(buf, remaining); m_ctr->cipher(buf, buf, remaining); } const byte* included_tag = &buf[remaining]; secure_vector<byte> mac = m_cmac->final(); mac ^= m_nonce_mac; mac ^= m_ad_mac; if(!same_mem(mac.data(), included_tag, tag_size())) throw Integrity_Failure("EAX tag check failed"); buffer.resize(offset + remaining); }
void Threefish_512::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { BOTAN_ASSERT(m_K.size() == 9, "Key was set"); BOTAN_ASSERT(m_T.size() == 3, "Tweak was set"); #if defined(BOTAN_HAS_THREEFISH_512_AVX2) if(CPUID::has_avx2()) { return avx2_encrypt_n(in, out, blocks); } #endif BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks; ++i) { uint64_t X0, X1, X2, X3, X4, X5, X6, X7; load_le(in + BLOCK_SIZE*i, X0, X1, X2, X3, X4, X5, X6, X7); THREEFISH_INJECT_KEY(0); THREEFISH_ENC_8_ROUNDS(1,2); THREEFISH_ENC_8_ROUNDS(3,4); THREEFISH_ENC_8_ROUNDS(5,6); THREEFISH_ENC_8_ROUNDS(7,8); THREEFISH_ENC_8_ROUNDS(9,10); THREEFISH_ENC_8_ROUNDS(11,12); THREEFISH_ENC_8_ROUNDS(13,14); THREEFISH_ENC_8_ROUNDS(15,16); THREEFISH_ENC_8_ROUNDS(17,18); store_le(out + BLOCK_SIZE*i, X0, X1, X2, X3, X4, X5, X6, X7); } }
/* * Add a new output queue */ void Output_Buffers::add(SecureQueue* queue) { BOTAN_ASSERT(queue, "queue was provided"); BOTAN_ASSERT(m_buffers.size() < m_buffers.max_size(), "Room was available in container"); m_buffers.push_back(queue); }
void CCM_Decryption::finish(secure_vector<byte>& buffer, size_t offset) { BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end()); const size_t sz = buffer.size() - offset; byte* buf = buffer.data() + offset; BOTAN_ASSERT(sz >= tag_size(), "We have the tag"); const secure_vector<byte>& ad = ad_buf(); BOTAN_ASSERT(ad.size() % BS == 0, "AD is block size multiple"); const BlockCipher& E = cipher(); secure_vector<byte> T(BS); E.encrypt(format_b0(sz - tag_size()), T); for(size_t i = 0; i != ad.size(); i += BS) { xor_buf(T.data(), &ad[i], BS); E.encrypt(T); } secure_vector<byte> C = format_c0(); secure_vector<byte> S0(BS); E.encrypt(C, S0); inc(C); secure_vector<byte> X(BS); const byte* buf_end = &buf[sz - tag_size()]; while(buf != buf_end) { const size_t to_proc = std::min<size_t>(BS, buf_end - buf); E.encrypt(C, X); xor_buf(buf, X.data(), to_proc); inc(C); xor_buf(T.data(), buf, to_proc); E.encrypt(T); buf += to_proc; } T ^= S0; if(!same_mem(T.data(), buf_end, tag_size())) throw Integrity_Failure("CCM tag check failed"); buffer.resize(buffer.size() - tag_size()); }
size_t Sqlite3_Database::Sqlite3_Statement::get_size_t(int column) { BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_INTEGER, "Return count is an integer"); const int sessions_int = ::sqlite3_column_int(m_stmt, column); BOTAN_ASSERT(sessions_int >= 0, "Expected size_t is non-negative"); return static_cast<size_t>(sessions_int); }
std::pair<const byte*, size_t> Sqlite3_Database::Sqlite3_Statement::get_blob(int column) { BOTAN_ASSERT(::sqlite3_column_type(m_stmt, 0) == SQLITE_BLOB, "Return value is a blob"); const void* session_blob = ::sqlite3_column_blob(m_stmt, column); const int session_blob_size = ::sqlite3_column_bytes(m_stmt, column); BOTAN_ASSERT(session_blob_size >= 0, "Blob size is non-negative"); return std::make_pair(static_cast<const byte*>(session_blob), static_cast<size_t>(session_blob_size)); }
void ECB_Encryption::update(secure_vector<byte>& buffer, size_t offset) { BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); const size_t sz = buffer.size() - offset; byte* buf = buffer.data() + offset; const size_t BS = cipher().block_size(); BOTAN_ASSERT(sz % BS == 0, "ECB input is full blocks"); const size_t blocks = sz / BS; cipher().encrypt_n(buf, buf, blocks); }
void CCM_Encryption::finish(secure_vector<byte>& buffer, size_t offset) { BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end()); const size_t sz = buffer.size() - offset; byte* buf = buffer.data() + offset; const secure_vector<byte>& ad = ad_buf(); BOTAN_ASSERT(ad.size() % BS == 0, "AD is block size multiple"); const BlockCipher& E = cipher(); secure_vector<byte> T(BS); E.encrypt(format_b0(sz), T); for(size_t i = 0; i != ad.size(); i += BS) { xor_buf(T.data(), &ad[i], BS); E.encrypt(T); } secure_vector<byte> C = format_c0(); secure_vector<byte> S0(BS); E.encrypt(C, S0); inc(C); secure_vector<byte> X(BS); const byte* buf_end = &buf[sz]; while(buf != buf_end) { const size_t to_proc = std::min<size_t>(BS, buf_end - buf); xor_buf(T.data(), buf, to_proc); E.encrypt(T); E.encrypt(C, X); xor_buf(buf, X.data(), to_proc); inc(C); buf += to_proc; } T ^= S0; buffer += std::make_pair(T.data(), tag_size()); }
std::shared_ptr<const X509_Certificate> Certificate_Store_MacOS::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const { if(key_hash.size() != 20) { throw Invalid_Argument("Certificate_Store_MacOS::find_cert_by_pubkey_sha1 invalid hash"); } scoped_CFType<CFDataRef> key_hash_cfdata(createCFDataView(key_hash)); check_notnull(key_hash_cfdata, "create key hash search object"); scoped_CFType<CFArrayRef> result(m_impl->search( { {kSecAttrPublicKeyHash, key_hash_cfdata.get()}, })); if(!result) { return nullptr; // no certificate found } const auto count = CFArrayGetCount(result.get()); BOTAN_ASSERT(count > 0, "certificate result list contains an object"); // `count` might be greater than 1, but we'll just select the first match auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), 0)); return readCertificate(cfCert); }
EC_PrivateKey::EC_PrivateKey(const AlgorithmIdentifier& alg_id, const secure_vector<byte>& key_bits) { m_domain_params = EC_Group(alg_id.parameters); m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT; OID key_parameters; secure_vector<byte> public_key_bits; BER_Decoder(key_bits) .start_cons(SEQUENCE) .decode_and_check<size_t>(1, "Unknown version code for ECC key") .decode_octet_string_bigint(m_private_key) .decode_optional(key_parameters, ASN1_Tag(0), PRIVATE) .decode_optional_string(public_key_bits, BIT_STRING, 1, PRIVATE) .end_cons(); if(!key_parameters.empty() && key_parameters != alg_id.oid) throw Decoding_Error("EC_PrivateKey - inner and outer OIDs did not match"); if(public_key_bits.empty()) { m_public_key = domain().get_base_point() * m_private_key; BOTAN_ASSERT(m_public_key.on_the_curve(), "Public point derived from loaded key was on the curve"); } else { m_public_key = OS2ECP(public_key_bits, domain().get_curve()); // OS2ECP verifies that the point is on the curve } }
bool XMSS_WOTS_Verification_Operation::is_valid_signature(const byte sig[], size_t sig_len) { const XMSS_WOTS_Parameters& w = m_pub_key.public_key().wots_parameters(); BOTAN_ASSERT(sig_len == w.element_size() * w.len(), "Invalid signature size."); wots_keysig_t signature(0); signature.reserve(sig_len); size_t begin = 0; size_t end = 0; while(signature.size() < w.len()) { begin = end; end = begin + w.element_size(); signature.push_back(secure_vector<byte>(sig + begin, sig + end)); } XMSS_WOTS_PublicKey pubkey_msg(w.oid(), m_msg_buf, signature, m_pub_key.address(), m_pub_key.public_key().public_seed()); return pubkey_msg.key_data() == m_pub_key.public_key().key_data(); }
GOST_3410_PublicKey::GOST_3410_PublicKey(const AlgorithmIdentifier& alg_id, const secure_vector<byte>& key_bits) { OID ecc_param_id; // The parameters also includes hash and cipher OIDs BER_Decoder(alg_id.parameters).start_cons(SEQUENCE).decode(ecc_param_id); domain_params = EC_Group(ecc_param_id); secure_vector<byte> bits; BER_Decoder(key_bits).decode(bits, OCTET_STRING); const size_t part_size = bits.size() / 2; // Keys are stored in little endian format (WTF) for(size_t i = 0; i != part_size / 2; ++i) { std::swap(bits[i], bits[part_size-1-i]); std::swap(bits[part_size+i], bits[2*part_size-1-i]); } BigInt x(bits.data(), part_size); BigInt y(&bits[part_size], part_size); public_key = PointGFp(domain().get_curve(), x, y); BOTAN_ASSERT(public_key.on_the_curve(), "Loaded GOST 34.10 public key is on the curve"); }
size_t CBC_Decryption::process(uint8_t buf[], size_t sz) { const size_t BS = cipher().block_size(); BOTAN_ASSERT(sz % BS == 0, "Input is full blocks"); size_t blocks = sz / BS; while(blocks) { const size_t to_proc = std::min(BS * blocks, m_tempbuf.size()); cipher().decrypt_n(buf, m_tempbuf.data(), to_proc / BS); xor_buf(m_tempbuf.data(), state_ptr(), BS); xor_buf(&m_tempbuf[BS], buf, to_proc - BS); copy_mem(state_ptr(), buf + (to_proc - BS), BS); copy_mem(buf, m_tempbuf.data(), to_proc); buf += to_proc; blocks -= to_proc / BS; } return sz; }
std::vector<X509_DN> Certificate_Store_MacOS::all_subjects() const { scoped_CFType<CFArrayRef> result(m_impl->search()); if(!result) { return {}; // not a single certificate found in the keychain } const auto count = CFArrayGetCount(result.get()); BOTAN_ASSERT(count > 0, "subject result list contains data"); std::vector<X509_DN> output; output.reserve(count); for(unsigned int i = 0; i < count; ++i) { // Note: Apple's API provides SecCertificateCopyNormalizedSubjectSequence // which would have saved us from reading a Botan::X509_Certificate, // however, this function applies the same DN "normalization" as // stated above. auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), i)); auto cert = readCertificate(cfCert); output.emplace_back(cert->subject_dn()); } return output; }
/** * EC_PrivateKey constructor */ EC_PrivateKey::EC_PrivateKey(RandomNumberGenerator& rng, const EC_Group& ec_group, const BigInt& x, bool with_modular_inverse) { m_domain_params = ec_group; if (!ec_group.get_oid().empty()) m_domain_encoding = EC_DOMPAR_ENC_OID; else m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT; if(x == 0) { m_private_key = BigInt::random_integer(rng, 1, domain().get_order()); } else { m_private_key = x; } m_public_key = domain().get_base_point() * ((with_modular_inverse) ? inverse_mod(m_private_key, m_domain_params.get_order()) : m_private_key); BOTAN_ASSERT(m_public_key.on_the_curve(), "Generated public key point was on the curve"); }
void GHASH::update(const uint8_t input[], size_t length) { BOTAN_ASSERT(m_ghash.size() == GCM_BS, "Key was set"); m_text_len += length; ghash_update(m_ghash, input, length); }
void Stream_Decompression::process(secure_vector<byte>& buf, size_t offset, u32bit flags) { BOTAN_ASSERT(m_stream, "Initialized"); BOTAN_ASSERT(buf.size() >= offset, "Offset is sane"); if(m_buffer.size() < buf.size() + offset) m_buffer.resize(buf.size() + offset); m_stream->next_in(buf.data() + offset, buf.size() - offset); m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset); while(true) { const bool stream_end = m_stream->run(flags); if(stream_end) { if(m_stream->avail_in() == 0) // all data consumed? { m_buffer.resize(m_buffer.size() - m_stream->avail_out()); clear(); break; } // More data follows: try to process as a following stream const size_t read = (buf.size() - offset) - m_stream->avail_in(); start(); m_stream->next_in(buf.data() + offset + read, buf.size() - offset - read); } if(m_stream->avail_out() == 0) { const size_t added = 8 + m_buffer.size(); m_buffer.resize(m_buffer.size() + added); m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added); } else if(m_stream->avail_in() == 0) { m_buffer.resize(m_buffer.size() - m_stream->avail_out()); break; } } copy_mem(m_buffer.data(), buf.data(), offset); buf.swap(m_buffer); }
std::vector<byte> Transform_Filter::Nonce_State::get() { BOTAN_ASSERT(m_fresh_nonce, "The nonce is fresh for this message"); if(!m_nonce.empty()) m_fresh_nonce = false; return m_nonce; }
void CCM_Mode::update(secure_vector<byte>& buffer, size_t offset) { BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); const size_t sz = buffer.size() - offset; byte* buf = buffer.data() + offset; m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz); buffer.resize(offset); // truncate msg }
/* * Get a particular output queue */ SecureQueue* Output_Buffers::get(Pipe::message_id msg) const { if(msg < m_offset) return nullptr; BOTAN_ASSERT(msg < message_count(), "Message number is in range"); return m_buffers[msg-m_offset]; }
void EAX_Decryption::update(secure_vector<byte>& buffer, size_t offset) { BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); const size_t sz = buffer.size() - offset; byte* buf = buffer.data() + offset; m_cmac->update(buf, sz); m_ctr->cipher(buf, buf, sz); }
Cascade_Cipher::Cascade_Cipher(BlockCipher* c1, BlockCipher* c2) : m_cipher1(c1), m_cipher2(c2) { m_block = block_size_for_cascade(c1->block_size(), c2->block_size()); BOTAN_ASSERT(m_block % c1->block_size() == 0 && m_block % c2->block_size() == 0, "Combined block size is a multiple of each ciphers block"); }
void XTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) { BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); const size_t sz = buffer.size() - offset; uint8_t* buf = buffer.data() + offset; BOTAN_ASSERT(sz >= minimum_final_size(), "Have sufficient final input in XTS decrypt"); const size_t BS = cipher().block_size(); if(sz % BS == 0) { update(buffer, offset); } else { // steal ciphertext const size_t full_blocks = ((sz / BS) - 1) * BS; const size_t final_bytes = sz - full_blocks; BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes); buffer.resize(full_blocks + offset); update(buffer, offset); xor_buf(last, tweak() + BS, BS); cipher().decrypt(last); xor_buf(last, tweak() + BS, BS); for(size_t i = 0; i != final_bytes - BS; ++i) { last[i] ^= last[i + BS]; last[i + BS] ^= last[i]; last[i] ^= last[i + BS]; } xor_buf(last, tweak(), BS); cipher().decrypt(last); xor_buf(last, tweak(), BS); buffer += last; } }
void CTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) { BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); const size_t sz = buffer.size() - offset; uint8_t* buf = buffer.data() + offset; const size_t BS = cipher().block_size(); if(sz < BS + 1) throw Encoding_Error(name() + ": insufficient data to decrypt"); if(sz % BS == 0) { // swap last two blocks for(size_t i = 0; i != BS; ++i) std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]); update(buffer, offset); } else { const size_t full_blocks = ((sz / BS) - 1) * BS; const size_t final_bytes = sz - full_blocks; BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes); buffer.resize(full_blocks + offset); update(buffer, offset); cipher().decrypt(last.data()); xor_buf(last.data(), &last[BS], final_bytes - BS); for(size_t i = 0; i != final_bytes - BS; ++i) std::swap(last[i], last[i + BS]); cipher().decrypt(last.data()); xor_buf(last.data(), state_ptr(), BS); buffer += last; } }
void Threefish_512::skein_feedfwd(const secure_vector<u64bit>& M, const secure_vector<u64bit>& T) { BOTAN_ASSERT(m_K.size() == 9, "Key was set"); BOTAN_ASSERT(M.size() == 8, "Single block"); m_T[0] = T[0]; m_T[1] = T[1]; m_T[2] = T[0] ^ T[1]; u64bit X0 = M[0]; u64bit X1 = M[1]; u64bit X2 = M[2]; u64bit X3 = M[3]; u64bit X4 = M[4]; u64bit X5 = M[5]; u64bit X6 = M[6]; u64bit X7 = M[7]; THREEFISH_INJECT_KEY(0); THREEFISH_ENC_8_ROUNDS(1,2); THREEFISH_ENC_8_ROUNDS(3,4); THREEFISH_ENC_8_ROUNDS(5,6); THREEFISH_ENC_8_ROUNDS(7,8); THREEFISH_ENC_8_ROUNDS(9,10); THREEFISH_ENC_8_ROUNDS(11,12); THREEFISH_ENC_8_ROUNDS(13,14); THREEFISH_ENC_8_ROUNDS(15,16); THREEFISH_ENC_8_ROUNDS(17,18); m_K[0] = M[0] ^ X0; m_K[1] = M[1] ^ X1; m_K[2] = M[2] ^ X2; m_K[3] = M[3] ^ X3; m_K[4] = M[4] ^ X4; m_K[5] = M[5] ^ X5; m_K[6] = M[6] ^ X6; m_K[7] = M[7] ^ X7; m_K[8] = m_K[0] ^ m_K[1] ^ m_K[2] ^ m_K[3] ^ m_K[4] ^ m_K[5] ^ m_K[6] ^ m_K[7] ^ 0x1BD11BDAA9FC1A22; }
void CCM_Mode::encode_length(size_t len, byte out[]) { const size_t len_bytes = L(); BOTAN_ASSERT(len_bytes < sizeof(size_t), "Length field fits"); for(size_t i = 0; i != len_bytes; ++i) out[len_bytes-1-i] = get_byte(sizeof(size_t)-1-i, len); BOTAN_ASSERT((len >> (len_bytes*8)) == 0, "Message length fits in field"); }
secure_vector<byte> PK_Decryptor::decrypt_or_random(const byte in[], size_t length, size_t expected_pt_len, RandomNumberGenerator& rng, const byte required_content_bytes[], const byte required_content_offsets[], size_t required_contents_length) const { const secure_vector<byte> fake_pms = rng.random_vec(expected_pt_len); //CT::poison(in, length); byte valid_mask = 0; secure_vector<byte> decoded = do_decrypt(valid_mask, in, length); valid_mask &= CT::is_equal(decoded.size(), expected_pt_len); decoded.resize(expected_pt_len); for(size_t i = 0; i != required_contents_length; ++i) { /* These values are chosen by the application and for TLS are constants, so this early failure via assert is fine since we know 0,1 < 48 If there is a protocol that has content checks on the key where the expected offsets are controllable by the attacker this could still leak. Alternately could always reduce the offset modulo the length? */ const byte exp = required_content_bytes[i]; const byte off = required_content_offsets[i]; BOTAN_ASSERT(off < expected_pt_len, "Offset in range of plaintext"); valid_mask &= CT::is_equal(decoded[off], exp); } CT::conditional_copy_mem(valid_mask, /*output*/decoded.data(), /*from0*/decoded.data(), /*from1*/fake_pms.data(), expected_pt_len); //CT::unpoison(in, length); //CT::unpoison(decoded.data(), decoded.size()); return decoded; }
void XMSS_WOTS_Verification_Operation::update(const byte msg[], size_t msg_len) { BOTAN_ASSERT(msg_len == m_pub_key.public_key().wots_parameters(). element_size() && m_msg_buf.size() == 0, "XMSS WOTS only supports one message part of size n."); for(size_t i = 0; i < msg_len; i++) { m_msg_buf.push_back(msg[i]); } }