diff --git a/src/lib/tls/tls13_pqc/hybrid_public_key.cpp b/src/lib/tls/tls13_pqc/hybrid_public_key.cpp index 9a8435e195e..981d9875230 100644 --- a/src/lib/tls/tls13_pqc/hybrid_public_key.cpp +++ b/src/lib/tls/tls13_pqc/hybrid_public_key.cpp @@ -209,26 +209,19 @@ class Hybrid_KEM_Encryption_Operation final : public PK_Ops::KEM_Encryption_with }); } - void raw_kem_encrypt(secure_vector& out_encapsulated_key, - secure_vector& raw_shared_key, + void raw_kem_encrypt(std::span out_encapsulated_key, + std::span raw_shared_key, Botan::RandomNumberGenerator& rng) override { - out_encapsulated_key.resize(encapsulated_key_length()); - raw_shared_key.resize(raw_kem_shared_key_length()); + BOTAN_ASSERT_NOMSG(out_encapsulated_key.size() == encapsulated_key_length()); + BOTAN_ASSERT_NOMSG(raw_shared_key.size() == raw_kem_shared_key_length()); BufferStuffer encaps_key_stuffer(out_encapsulated_key); BufferStuffer shared_key_stuffer(raw_shared_key); for(auto& kem_enc : m_kem_encryptors) { - // TODO: Once PK_KEM_Encryptor uses std::span for its out-params, we - // probably want to pre-allocate the upstream out-params and - // use the BufferStuffer helper to place the downstream KEM - // outputs in the right location. Avoiding lots of copy and alloc. - // See also Hybrid_KEM_Decryption_Operation - secure_vector out_encaps_buffer; - secure_vector out_shared_key_buffer; - kem_enc->encrypt(out_encaps_buffer, out_shared_key_buffer, 0 /* no KDF */, rng); - encaps_key_stuffer.append(out_encaps_buffer); - shared_key_stuffer.append(out_shared_key_buffer); + kem_enc->encrypt(encaps_key_stuffer.next(kem_enc->encapsulated_key_length()), + shared_key_stuffer.next(kem_enc->shared_key_length(0 /* no KDF */)), + rng); } } @@ -328,18 +321,16 @@ class Hybrid_KEM_Decryption final : public PK_Ops::KEM_Decryption_with_KDF { }); } - secure_vector raw_kem_decrypt(const uint8_t encap_key[], size_t len) override { - secure_vector shared_secret(raw_kem_shared_key_length()); + void raw_kem_decrypt(std::span out_shared_key, std::span encap_key) override { + BOTAN_ASSERT_NOMSG(out_shared_key.size() == raw_kem_shared_key_length()); - BufferSlicer encap_key_slicer({encap_key, len}); - BufferStuffer shared_secret_stuffer(shared_secret); + BufferSlicer encap_key_slicer(encap_key); + BufferStuffer shared_secret_stuffer(out_shared_key); for(auto& [decryptor, encapsulation_length] : m_decryptors_with_encapsulation_lengths) { - const auto public_value_part = encap_key_slicer.take(encapsulation_length); - shared_secret_stuffer.append(decryptor->decrypt(public_value_part, 0 /* no KDF */, {})); + decryptor->decrypt(shared_secret_stuffer.next(decryptor->shared_key_length(0 /* no KDF */)), + encap_key_slicer.take(encapsulation_length)); } - - return shared_secret; } size_t raw_kem_shared_key_length() const override { diff --git a/src/lib/tls/tls13_pqc/kex_to_kem_adapter.cpp b/src/lib/tls/tls13_pqc/kex_to_kem_adapter.cpp index 1fba4fb01fc..bf02108a310 100644 --- a/src/lib/tls/tls13_pqc/kex_to_kem_adapter.cpp +++ b/src/lib/tls/tls13_pqc/kex_to_kem_adapter.cpp @@ -141,14 +141,26 @@ class KEX_to_KEM_Adapter_Encryption_Operation final : public PK_Ops::KEM_Encrypt size_t encapsulated_key_length() const override { return kex_public_value(m_public_key).size(); } - void raw_kem_encrypt(secure_vector& out_encapsulated_key, - secure_vector& raw_shared_key, + void raw_kem_encrypt(std::span out_encapsulated_key, + std::span raw_shared_key, Botan::RandomNumberGenerator& rng) override { const auto sk = generate_key_agreement_private_key(m_public_key, rng); - raw_shared_key = PK_Key_Agreement(*sk, rng, "Raw", m_provider) - .derive_key(0 /* no KDF */, kex_public_value(m_public_key)) - .bits_of(); - out_encapsulated_key = lock(sk->public_value()); + const auto shared_key = PK_Key_Agreement(*sk, rng, "Raw", m_provider) + .derive_key(0 /* no KDF */, kex_public_value(m_public_key)) + .bits_of(); + + const auto public_value = sk->public_value(); + + // TODO: perhaps avoid these copies by providing std::span out-params + // for `PK_Key_Agreement::derive_key()` and + // `PK_Key_Agreement_Key::public_value()` + BOTAN_ASSERT_EQUAL(public_value.size(), + out_encapsulated_key.size(), + "KEX-to-KEM Adapter: encapsulated key out-param has correct length"); + BOTAN_ASSERT_EQUAL( + shared_key.size(), raw_shared_key.size(), "KEX-to-KEM Adapter: shared key out-param has correct length"); + std::copy(public_value.begin(), public_value.end(), out_encapsulated_key.begin()); + std::copy(shared_key.begin(), shared_key.end(), raw_shared_key.begin()); } private: @@ -164,8 +176,11 @@ class KEX_to_KEM_Decryption_Operation final : public PK_Ops::KEM_Decryption_with const std::string_view provider) : PK_Ops::KEM_Decryption_with_KDF(kdf), m_operation(key, rng, "Raw", provider) {} - secure_vector raw_kem_decrypt(const uint8_t encap_key[], size_t len) override { - return m_operation.derive_key(0 /* no KDF */, {encap_key, len}).bits_of(); + void raw_kem_decrypt(std::span out_shared_key, std::span encap_key) override { + secure_vector shared_secret = m_operation.derive_key(0 /* no KDF */, encap_key).bits_of(); + BOTAN_ASSERT_EQUAL( + shared_secret.size(), out_shared_key.size(), "KEX-to-KEM Adapter: shared key out-param has correct length"); + std::copy(shared_secret.begin(), shared_secret.end(), out_shared_key.begin()); } size_t raw_kem_shared_key_length() const override { return m_operation.agreed_value_size(); } diff --git a/src/lib/tls/tls_callbacks.h b/src/lib/tls/tls_callbacks.h index 19e784290c6..51f78875792 100644 --- a/src/lib/tls/tls_callbacks.h +++ b/src/lib/tls/tls_callbacks.h @@ -255,11 +255,6 @@ class BOTAN_PUBLIC_API(2, 0) Callbacks { const std::vector& msg, const std::vector& sig); - struct Encapsulation_Result { - std::vector encapsulated_bytes; - secure_vector shared_secret; - }; - /** * Generate an ephemeral KEM key for a TLS 1.3 handshake * diff --git a/src/tests/test_tls_hybrid_kem_key.cpp b/src/tests/test_tls_hybrid_kem_key.cpp index c90de3ef6e9..68c81bf9c72 100644 --- a/src/tests/test_tls_hybrid_kem_key.cpp +++ b/src/tests/test_tls_hybrid_kem_key.cpp @@ -117,24 +117,23 @@ void roundtrip_test(Test::Result& result, Ts... kex_kem_fn) { auto& rng = Test::rng(); Botan::PK_KEM_Encryptor encryptor(hybrid_public_key, "Raw"); - Botan::secure_vector shared_secret; - Botan::secure_vector encapsulated_key; - encryptor.encrypt(encapsulated_key, shared_secret, 0, rng); + const auto kem_result = encryptor.encrypt(rng); const auto expected_shared_secret_length = length_of_hybrid_shared_key(kex_kem_fn...); const auto expected_ciphertext_length = length_of_hybrid_ciphertext(kex_kem_fn...); const auto expected_public_key_length = length_of_hybrid_public_value(kex_kem_fn...); - result.test_eq("ciphertext has expected length", encapsulated_key.size(), expected_ciphertext_length); - result.test_eq("shared secret has expected length", shared_secret.size(), expected_shared_secret_length); + result.test_eq( + "ciphertext has expected length", kem_result.encapsulated_shared_key().size(), expected_ciphertext_length); + result.test_eq("shared secret has expected length", kem_result.shared_key().size(), expected_shared_secret_length); result.test_eq( "expected length of ciphertext is as expected", encryptor.encapsulated_key_length(), expected_ciphertext_length); result.test_eq("shared secret has expected length", encryptor.shared_key_length(0), expected_shared_secret_length); Botan::PK_KEM_Decryptor decryptor(hybrid_key, rng, "Raw"); - Botan::secure_vector decaps_shared_secret = decryptor.decrypt(encapsulated_key, 0, {}); + Botan::secure_vector decaps_shared_secret = decryptor.decrypt(kem_result.encapsulated_shared_key(), 0, {}); - result.test_eq("shared secret after KEM roundtrip matches", decaps_shared_secret, shared_secret); + result.test_eq("shared secret after KEM roundtrip matches", decaps_shared_secret, kem_result.shared_key()); result.test_eq( "expected shared secret has expected length", decryptor.shared_key_length(0), expected_shared_secret_length); result.test_eq("shared secret has expected length", decaps_shared_secret.size(), expected_shared_secret_length); @@ -204,20 +203,20 @@ void kex_to_kem_roundtrip(Test::Result& result, auto& rng = Test::rng(); Botan::PK_KEM_Encryptor encryptor(kexkem_public_key, "Raw"); - Botan::secure_vector shared_secret; - Botan::secure_vector encapsulated_key; - encryptor.encrypt(encapsulated_key, shared_secret, 0, rng); + const auto kem_result = encryptor.encrypt(rng); - result.test_eq("ciphertext has expected length", encapsulated_key.size(), encryptor.encapsulated_key_length()); - result.test_eq("shared secret has expected length", shared_secret.size(), encryptor.shared_key_length(0)); + result.test_eq("ciphertext has expected length", + kem_result.encapsulated_shared_key().size(), + encryptor.encapsulated_key_length()); + result.test_eq("shared secret has expected length", kem_result.shared_key().size(), encryptor.shared_key_length(0)); Botan::PK_KEM_Decryptor decryptor(kexkem_key, rng, "Raw"); - Botan::secure_vector decaps_shared_secret = decryptor.decrypt(encapsulated_key, 0, {}); + Botan::secure_vector decaps_shared_secret = decryptor.decrypt(kem_result.encapsulated_shared_key(), 0, {}); result.test_eq( "decapsulated shared secret has expected length", decaps_shared_secret.size(), decryptor.shared_key_length(0)); - result.test_eq("shared secret after KEM roundtrip matches", decaps_shared_secret, shared_secret); + result.test_eq("shared secret after KEM roundtrip matches", decaps_shared_secret, kem_result.shared_key()); } std::vector kex_to_kem_adapter() {