bool EMSA1::verify(const secure_vector<uint8_t>& input, const secure_vector<uint8_t>& raw, size_t key_bits) { try { if(raw.size() != m_hash->output_length()) throw Encoding_Error("EMSA1::encoding_of: Invalid size for input"); // Call emsa1_encoding to handle any required bit shifting const secure_vector<uint8_t> our_coding = emsa1_encoding(raw, key_bits); if(our_coding.size() < input.size()) return false; const size_t offset = our_coding.size() - input.size(); // must be >= 0 per check above // If our encoding is longer, all the bytes in it must be zero for(size_t i = 0; i != offset; ++i) if(our_coding[i] != 0) return false; return same_mem(input.data(), &our_coding[offset], input.size()); } catch(Invalid_Argument) { return false; } }
Ed25519_PrivateKey::Ed25519_PrivateKey(RandomNumberGenerator& rng) { const secure_vector<uint8_t> seed = rng.random_vec(32); m_public.resize(32); m_private.resize(64); ed25519_gen_keypair(m_public.data(), m_private.data(), seed.data()); }
void mceliece_decrypt(secure_vector<byte>& plaintext_out, secure_vector<byte>& error_mask_out, const secure_vector<byte>& ciphertext, const McEliece_PrivateKey& key) { mceliece_decrypt(plaintext_out, error_mask_out, ciphertext.data(), ciphertext.size(), key); }
void Montgomery_Params::mul_by(BigInt& x, const secure_vector<word>& y, secure_vector<word>& ws) const { const size_t output_size = 2*m_p_words + 2; if(ws.size() < 2*output_size) ws.resize(2*output_size); word* z_data = &ws[0]; word* ws_data = &ws[output_size]; BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words); bigint_mul(z_data, output_size, x.data(), x.size(), std::min(m_p_words, x.size()), y.data(), y.size(), std::min(m_p_words, y.size()), ws_data, output_size); bigint_monty_redc(z_data, m_p.data(), m_p_words, m_p_dash, ws_data, output_size); if(x.size() < output_size) x.grow_to(output_size); copy_mem(x.mutable_data(), z_data, output_size); }
//static void SHA_3::expand(size_t bitrate, secure_vector<uint64_t>& S, uint8_t output[], size_t output_length) { BOTAN_ARG_CHECK(bitrate % 8 == 0); size_t Si = 0; for(size_t i = 0; i != output_length; ++i) { if(i > 0) { if(i % (bitrate / 8) == 0) { SHA_3::permute(S.data()); Si = 0; } else if(i % 8 == 0) { Si += 1; } } output[i] = get_byte(7 - (i % 8), S[Si]); } }
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 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); }
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); }
uint32_t HOTP::generate_hotp(uint64_t counter) { m_mac->update_be(counter); const secure_vector<uint8_t> mac = m_mac->final(); const size_t offset = mac[mac.size()-1] & 0x0F; const uint32_t code = load_be<uint32_t>(mac.data() + offset, 0) & 0x7FFFFFFF; return code % m_digit_mod; }
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 }
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()); }
//static void SHA_3::finish(size_t bitrate, secure_vector<uint64_t>& S, size_t S_pos, uint8_t init_pad, uint8_t fini_pad) { BOTAN_ARG_CHECK(bitrate % 64 == 0, "SHA-3 bitrate must be multiple of 64"); S[S_pos / 8] ^= static_cast<uint64_t>(init_pad) << (8 * (S_pos % 8)); S[(bitrate / 64) - 1] ^= static_cast<uint64_t>(fini_pad) << 56; SHA_3::permute(S.data()); }
BigInt Montgomery_Params::sqr(const BigInt& x, secure_vector<word>& ws) const { const size_t output_size = 2*m_p_words + 2; if(ws.size() < output_size) ws.resize(output_size); BigInt z(BigInt::Positive, output_size); BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words); bigint_sqr(z.mutable_data(), z.size(), x.data(), x.size(), std::min(m_p_words, x.size()), ws.data(), ws.size()); bigint_monty_redc(z.mutable_data(), m_p.data(), m_p_words, m_p_dash, ws.data(), ws.size()); return z; }
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; }
secure_vector<uint8_t> rfc3394_keywrap(const secure_vector<uint8_t>& key, const SymmetricKey& kek) { BOTAN_ARG_CHECK(kek.size() == 16 || kek.size() == 24 || kek.size() == 32, "Invalid KEK length for NIST key wrap"); const std::string cipher_name = "AES-" + std::to_string(8*kek.size()); std::unique_ptr<BlockCipher> aes(BlockCipher::create_or_throw(cipher_name)); aes->set_key(kek); std::vector<uint8_t> wrapped = nist_key_wrap(key.data(), key.size(), *aes); return secure_vector<uint8_t>(wrapped.begin(), wrapped.end()); }
void Stream_Compression::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); // If the output buffer has zero length, .data() might return nullptr. This would // make some compression algorithms (notably those provided by zlib) fail. // Any small positive value works fine, but we choose 32 as it is the smallest power // of two that is large enough to hold all the headers and trailers of the common // formats, preventing further resizings to make room for output data. if(m_buffer.size() == 0) m_buffer.resize(32); m_stream->next_in(buf.data() + offset, buf.size() - offset); m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset); while(true) { m_stream->run(flags); 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); }
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()); }
/* * EMSA-Raw Verify Operation */ bool EMSA_Raw::verify(const secure_vector<byte>& coded, const secure_vector<byte>& raw, size_t) { if(coded.size() == raw.size()) return (coded == raw); if(coded.size() > raw.size()) return false; // handle zero padding differences const size_t leading_zeros_expected = raw.size() - coded.size(); bool same_modulo_leading_zeros = true; for(size_t i = 0; i != leading_zeros_expected; ++i) if(raw[i]) same_modulo_leading_zeros = false; if(!same_mem(coded.data(), raw.data() + leading_zeros_expected, coded.size())) same_modulo_leading_zeros = false; return same_modulo_leading_zeros; }
std::set<std::string> Encrypted_PSK_Database::list_names() const { const std::set<std::string> encrypted_names = kv_get_all(); std::set<std::string> names; for(std::string enc_name : encrypted_names) { try { const secure_vector<uint8_t> raw_name = base64_decode(enc_name); const secure_vector<uint8_t> name_bits = nist_key_unwrap_padded(raw_name.data(), raw_name.size(), *m_cipher); std::string pt_name(cast_uint8_ptr_to_char(name_bits.data()), name_bits.size()); names.insert(pt_name); } catch(Integrity_Failure&) { } } return names; }
secure_vector<uint8_t> rfc3394_keyunwrap(const secure_vector<uint8_t>& key, const SymmetricKey& kek) { BOTAN_ARG_CHECK(kek.size() == 16 || kek.size() == 24 || kek.size() == 32, "Invalid KEK length for NIST key wrap"); BOTAN_ARG_CHECK(key.size() >= 16 && key.size() % 8 == 0, "Bad input key size for NIST key unwrap"); const std::string cipher_name = "AES-" + std::to_string(8*kek.size()); std::unique_ptr<BlockCipher> aes(BlockCipher::create_or_throw(cipher_name)); aes->set_key(kek); return nist_key_unwrap(key.data(), key.size(), *aes); }
Ed25519_PrivateKey::Ed25519_PrivateKey(const secure_vector<uint8_t>& secret_key) { if(secret_key.size() == 64) { m_private = secret_key; m_public.assign(&m_private[32], &m_private[64]); } else if(secret_key.size() == 32) { m_public.resize(32); m_private.resize(64); ed25519_gen_keypair(m_public.data(), m_private.data(), secret_key.data()); } else throw Decoding_Error("Invalid size for Ed25519 private key"); }
BigInt Montgomery_Params::redc(const BigInt& x, secure_vector<word>& ws) const { const size_t output_size = 2*m_p_words + 2; if(ws.size() < output_size) ws.resize(output_size); BigInt z = x; z.grow_to(output_size); bigint_monty_redc(z.mutable_data(), m_p.data(), m_p_words, m_p_dash, ws.data(), ws.size()); return z; }
//static size_t SHA_3::absorb(size_t bitrate, secure_vector<uint64_t>& S, size_t S_pos, const uint8_t input[], size_t length) { while(length > 0) { size_t to_take = std::min(length, bitrate / 8 - S_pos); length -= to_take; while(to_take && S_pos % 8) { S[S_pos / 8] ^= static_cast<uint64_t>(input[0]) << (8 * (S_pos % 8)); ++S_pos; ++input; --to_take; } while(to_take && to_take % 8 == 0) { S[S_pos / 8] ^= load_le<uint64_t>(input, 0); S_pos += 8; input += 8; to_take -= 8; } while(to_take) { S[S_pos / 8] ^= static_cast<uint64_t>(input[0]) << (8 * (S_pos % 8)); ++S_pos; ++input; --to_take; } if(S_pos == bitrate / 8) { SHA_3::permute(S.data()); S_pos = 0; } } return S_pos; }
secure_vector<uint8_t> rfc3394_keywrap(const secure_vector<uint8_t>& key, const SymmetricKey& kek) { if(key.size() % 8 != 0) throw Invalid_Argument("Bad input key size for NIST key wrap"); if(kek.size() != 16 && kek.size() != 24 && kek.size() != 32) throw Invalid_Argument("Bad KEK length " + std::to_string(kek.size()) + " for NIST key wrap"); const std::string cipher_name = "AES-" + std::to_string(8*kek.size()); std::unique_ptr<BlockCipher> aes(BlockCipher::create_or_throw(cipher_name)); aes->set_key(kek); const size_t n = key.size() / 8; secure_vector<uint8_t> R((n + 1) * 8); secure_vector<uint8_t> A(16); for(size_t i = 0; i != 8; ++i) A[i] = 0xA6; copy_mem(&R[8], key.data(), key.size()); for(size_t j = 0; j <= 5; ++j) { for(size_t i = 1; i <= n; ++i) { const uint32_t t = static_cast<uint32_t>((n * j) + i); copy_mem(&A[8], &R[8*i], 8); aes->encrypt(A.data()); copy_mem(&R[8*i], &A[8], 8); uint8_t t_buf[4] = { 0 }; store_be(t, t_buf); xor_buf(&A[4], t_buf, 4); } } copy_mem(R.data(), A.data(), 8); return R; }
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; } }
secure_vector<uint8_t> Encrypted_PSK_Database::get(const std::string& name) const { const std::vector<uint8_t> wrapped_name = nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()), name.size(), *m_cipher); const std::string val_base64 = kv_get(base64_encode(wrapped_name)); if(val_base64.empty()) throw Invalid_Argument("Named PSK not located"); const secure_vector<uint8_t> val = base64_decode(val_base64); std::unique_ptr<BlockCipher> wrap_cipher(m_cipher->clone()); wrap_cipher->set_key(m_hmac->process(wrapped_name)); return nist_key_unwrap_padded(val.data(), val.size(), *wrap_cipher); }
//static void SHA_3::expand(size_t bitrate, secure_vector<uint64_t>& S, uint8_t output[], size_t output_length) { BOTAN_ARG_CHECK(bitrate % 64 == 0, "SHA-3 bitrate must be multiple of 64"); const size_t byterate = bitrate / 8; while(output_length > 0) { const size_t copying = std::min(byterate, output_length); copy_out_vec_le(output, copying, S); output += copying; output_length -= copying; if(output_length > 0) { SHA_3::permute(S.data()); } } }
secure_vector<byte> mceies_encrypt(const McEliece_PublicKey& pubkey, const byte pt[], size_t pt_len, const byte ad[], size_t ad_len, RandomNumberGenerator& rng, const std::string& algo) { McEliece_KEM_Encryptor kem_op(pubkey); const std::pair<secure_vector<byte>,secure_vector<byte>> mce_ciphertext__key = kem_op.encrypt(rng); const secure_vector<byte>& mce_ciphertext = mce_ciphertext__key.first; const secure_vector<byte>& mce_key = mce_ciphertext__key.second; const size_t mce_code_bytes = (pubkey.get_code_length() + 7) / 8; BOTAN_ASSERT(mce_ciphertext.size() == mce_code_bytes, "Unexpected size"); std::unique_ptr<AEAD_Mode> aead(get_aead(algo, ENCRYPTION)); if(!aead) throw std::runtime_error("mce_encrypt unable to create AEAD instance '" + algo + "'"); const size_t nonce_len = aead->default_nonce_length(); aead->set_key(aead_key(mce_key, *aead)); aead->set_associated_data(ad, ad_len); const secure_vector<byte> nonce = rng.random_vec(nonce_len); secure_vector<byte> msg(mce_ciphertext.size() + nonce.size() + pt_len); copy_mem(msg.data(), mce_ciphertext.data(), mce_ciphertext.size()); copy_mem(msg.data() + mce_ciphertext.size(), nonce.data(), nonce.size()); copy_mem(msg.data() + mce_ciphertext.size() + nonce.size(), pt, pt_len); aead->start(nonce); aead->finish(msg, mce_ciphertext.size() + nonce.size()); return msg; }