From c5c76c2d4e71f3d2795e00c4bc0b5ac3d1c10c88 Mon Sep 17 00:00:00 2001 From: Dirk Zimmermann Date: Sun, 27 Oct 2024 09:42:55 +0100 Subject: [PATCH 1/8] botan_pk_op_encrypt_create_with_rng --- src/lib/ffi/ffi.h | 4 ++++ src/lib/ffi/ffi_pk_op.cpp | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index c9341501bc..91ac47410f 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -1626,6 +1626,10 @@ typedef struct botan_pk_op_encrypt_struct* botan_pk_op_encrypt_t; BOTAN_FFI_EXPORT(2, 0) int botan_pk_op_encrypt_create(botan_pk_op_encrypt_t* op, botan_pubkey_t key, const char* padding, uint32_t flags); +BOTAN_FFI_EXPORT(3, 7) +int botan_pk_op_encrypt_create_with_rng( + botan_pk_op_encrypt_t* op, botan_rng_t rng, botan_pubkey_t key, const char* padding, uint32_t flags); + /** * @return 0 if success, error if invalid object handle */ diff --git a/src/lib/ffi/ffi_pk_op.cpp b/src/lib/ffi/ffi_pk_op.cpp index f08d9ec569..06a53a0681 100644 --- a/src/lib/ffi/ffi_pk_op.cpp +++ b/src/lib/ffi/ffi_pk_op.cpp @@ -43,6 +43,27 @@ int botan_pk_op_encrypt_create(botan_pk_op_encrypt_t* op, botan_pubkey_t key_obj }); } +int botan_pk_op_encrypt_create_with_rng( + botan_pk_op_encrypt_t* op, botan_rng_t rng_obj, botan_pubkey_t key_obj, const char* padding, uint32_t flags) { + if(op == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + if(flags != 0 && flags != BOTAN_PUBKEY_DER_FORMAT_SIGNATURE) { + return BOTAN_FFI_ERROR_BAD_FLAG; + } + + return ffi_guard_thunk(__func__, [=]() -> int { + *op = nullptr; + + Botan::RandomNumberGenerator &rng = safe_get(rng_obj); + + auto pk = std::make_unique(safe_get(key_obj), rng, padding); + *op = new botan_pk_op_encrypt_struct(std::move(pk)); + return BOTAN_FFI_SUCCESS; + }); +} + int botan_pk_op_encrypt_destroy(botan_pk_op_encrypt_t op) { return BOTAN_FFI_CHECKED_DELETE(op); } From e23b382274e7ba53def543761aeb4828e0e0a43e Mon Sep 17 00:00:00 2001 From: Dirk Zimmermann Date: Sun, 27 Oct 2024 11:01:38 +0100 Subject: [PATCH 2/8] botan_pk_op_encrypt_create_with_rng test --- src/lib/ffi/ffi_pk_op.cpp | 2 +- src/tests/test_ffi.cpp | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/lib/ffi/ffi_pk_op.cpp b/src/lib/ffi/ffi_pk_op.cpp index 06a53a0681..523c464591 100644 --- a/src/lib/ffi/ffi_pk_op.cpp +++ b/src/lib/ffi/ffi_pk_op.cpp @@ -56,7 +56,7 @@ int botan_pk_op_encrypt_create_with_rng( return ffi_guard_thunk(__func__, [=]() -> int { *op = nullptr; - Botan::RandomNumberGenerator &rng = safe_get(rng_obj); + Botan::RandomNumberGenerator& rng = safe_get(rng_obj); auto pk = std::make_unique(safe_get(key_obj), rng, padding); *op = new botan_pk_op_encrypt_struct(std::move(pk)); diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 647e6ad10b..65103fe986 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -2484,9 +2484,7 @@ class FFI_RSA_Test final : public FFI_Test { result.test_eq("algo name", std::string(namebuf), "RSA"); } - botan_pk_op_encrypt_t encrypt; - - if(TEST_FFI_INIT(botan_pk_op_encrypt_create, (&encrypt, loaded_pubkey, "OAEP(SHA-256)", 0))) { + auto test_encrypt_opt_fn = [&result, &rng, &priv](botan_pk_op_encrypt_t encrypt) { std::vector plaintext(32); TEST_FFI_OK(botan_rng_get, (rng, plaintext.data(), plaintext.size())); @@ -2514,6 +2512,16 @@ class FFI_RSA_Test final : public FFI_Test { } TEST_FFI_OK(botan_pk_op_encrypt_destroy, (encrypt)); + }; + + botan_pk_op_encrypt_t encrypt; + + if(TEST_FFI_INIT(botan_pk_op_encrypt_create, (&encrypt, loaded_pubkey, "OAEP(SHA-256)", 0))) { + test_encrypt_opt_fn(encrypt); + } + + if(TEST_FFI_INIT(botan_pk_op_encrypt_create_with_rng, (&encrypt, rng, loaded_pubkey, "OAEP(SHA-256)", 0))) { + test_encrypt_opt_fn(encrypt); } TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey)); From 4a49a89592128e9430cd01e6c1ce3aa8e3dfe6af Mon Sep 17 00:00:00 2001 From: Dirk Zimmermann Date: Sun, 27 Oct 2024 11:32:18 +0100 Subject: [PATCH 3/8] botan_pk_op_decrypt_create_with_rng --- src/lib/ffi/ffi.h | 4 ++++ src/lib/ffi/ffi_pk_op.cpp | 21 +++++++++++++++++++++ src/tests/test_ffi.cpp | 15 ++++++++++++--- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index 91ac47410f..6f0177aa4c 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -1654,6 +1654,10 @@ typedef struct botan_pk_op_decrypt_struct* botan_pk_op_decrypt_t; BOTAN_FFI_EXPORT(2, 0) int botan_pk_op_decrypt_create(botan_pk_op_decrypt_t* op, botan_privkey_t key, const char* padding, uint32_t flags); +BOTAN_FFI_EXPORT(3, 7) +int botan_pk_op_decrypt_create_with_rng( + botan_pk_op_decrypt_t* op, botan_rng_t rng, botan_privkey_t key, const char* padding, uint32_t flags); + /** * @return 0 if success, error if invalid object handle */ diff --git a/src/lib/ffi/ffi_pk_op.cpp b/src/lib/ffi/ffi_pk_op.cpp index 523c464591..5dfdc14144 100644 --- a/src/lib/ffi/ffi_pk_op.cpp +++ b/src/lib/ffi/ffi_pk_op.cpp @@ -110,6 +110,27 @@ int botan_pk_op_decrypt_create(botan_pk_op_decrypt_t* op, }); } +int botan_pk_op_decrypt_create_with_rng( + botan_pk_op_decrypt_t* op, botan_rng_t rng_obj, botan_privkey_t key_obj, const char* padding, uint32_t flags) { + if(op == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + if(flags != 0) { + return BOTAN_FFI_ERROR_BAD_FLAG; + } + + return ffi_guard_thunk(__func__, [=]() -> int { + *op = nullptr; + + Botan::RandomNumberGenerator& rng = safe_get(rng_obj); + + auto pk = std::make_unique(safe_get(key_obj), rng, padding); + *op = new botan_pk_op_decrypt_struct(std::move(pk)); + return BOTAN_FFI_SUCCESS; + }); +} + int botan_pk_op_decrypt_destroy(botan_pk_op_decrypt_t op) { return BOTAN_FFI_CHECKED_DELETE(op); } diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 65103fe986..d688c7a4ef 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -2497,7 +2497,7 @@ class FFI_RSA_Test final : public FFI_Test { ciphertext.resize(ctext_len); botan_pk_op_decrypt_t decrypt; - if(TEST_FFI_OK(botan_pk_op_decrypt_create, (&decrypt, priv, "OAEP(SHA-256)", 0))) { + if(TEST_FFI_OK(botan_pk_op_decrypt_create_with_rng, (&decrypt, rng, priv, "OAEP(SHA-256)", 0))) { size_t decrypted_len; TEST_FFI_OK(botan_pk_op_decrypt_output_length, (decrypt, ciphertext.size(), &decrypted_len)); std::vector decrypted(decrypted_len); @@ -3893,8 +3893,7 @@ class FFI_ElGamal_Test final : public FFI_Test { } // Test decryption - botan_pk_op_decrypt_t op_dec; - if(TEST_FFI_OK(botan_pk_op_decrypt_create, (&op_dec, loaded_privkey, "Raw", 0))) { + auto test_decrypt_fn = [&result, &decryption, &ciphertext](botan_pk_op_decrypt_t op_dec) { size_t ptext_len; TEST_FFI_OK(botan_pk_op_decrypt_output_length, (op_dec, ciphertext.size(), &ptext_len)); decryption.resize(ptext_len); @@ -3902,6 +3901,16 @@ class FFI_ElGamal_Test final : public FFI_Test { (op_dec, decryption.data(), &ptext_len, ciphertext.data(), ciphertext.size())); decryption.resize(ptext_len); TEST_FFI_OK(botan_pk_op_decrypt_destroy, (op_dec)); + }; + + botan_pk_op_decrypt_t op_dec; + + if(TEST_FFI_OK(botan_pk_op_decrypt_create, (&op_dec, loaded_privkey, "Raw", 0))) { + test_decrypt_fn(op_dec); + } + + if(TEST_FFI_OK(botan_pk_op_decrypt_create_with_rng, (&op_dec, rng, loaded_privkey, "Raw", 0))) { + test_decrypt_fn(op_dec); } result.test_eq("decryption worked", decryption, plaintext); From 905ce6933c597b079d5f52916be56c6022e00d8b Mon Sep 17 00:00:00 2001 From: Dirk Zimmermann Date: Sun, 27 Oct 2024 11:56:35 +0100 Subject: [PATCH 4/8] botan_pk_op_sign_create_with_rng --- src/lib/ffi/ffi.h | 4 + src/lib/ffi/ffi_pk_op.cpp | 24 ++++++ src/tests/test_ffi.cpp | 177 +++++++++++++++++++++----------------- 3 files changed, 127 insertions(+), 78 deletions(-) diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index 6f0177aa4c..b480e923a1 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -1681,6 +1681,10 @@ typedef struct botan_pk_op_sign_struct* botan_pk_op_sign_t; BOTAN_FFI_EXPORT(2, 0) int botan_pk_op_sign_create(botan_pk_op_sign_t* op, botan_privkey_t key, const char* hash_and_padding, uint32_t flags); +BOTAN_FFI_EXPORT(3, 7) +int botan_pk_op_sign_create_with_rng( + botan_pk_op_sign_t* op, botan_rng_t rng, botan_privkey_t key, const char* hash_and_padding, uint32_t flags); + /** * @return 0 if success, error if invalid object handle */ diff --git a/src/lib/ffi/ffi_pk_op.cpp b/src/lib/ffi/ffi_pk_op.cpp index 5dfdc14144..ac21e14d23 100644 --- a/src/lib/ffi/ffi_pk_op.cpp +++ b/src/lib/ffi/ffi_pk_op.cpp @@ -172,6 +172,30 @@ int botan_pk_op_sign_create(botan_pk_op_sign_t* op, botan_privkey_t key_obj, con }); } +int botan_pk_op_sign_create_with_rng( + botan_pk_op_sign_t* op, botan_rng_t rng_obj, botan_privkey_t key_obj, const char* hash, uint32_t flags) { + if(op == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + if(flags != 0 && flags != BOTAN_PUBKEY_DER_FORMAT_SIGNATURE) { + return BOTAN_FFI_ERROR_BAD_FLAG; + } + + return ffi_guard_thunk(__func__, [=]() -> int { + *op = nullptr; + + auto format = (flags & BOTAN_PUBKEY_DER_FORMAT_SIGNATURE) ? Botan::Signature_Format::DerSequence + : Botan::Signature_Format::Standard; + + Botan::RandomNumberGenerator& rng = safe_get(rng_obj); + + auto pk = std::make_unique(safe_get(key_obj), rng, hash, format); + *op = new botan_pk_op_sign_struct(std::move(pk)); + return BOTAN_FFI_SUCCESS; + }); +} + int botan_pk_op_sign_destroy(botan_pk_op_sign_t op) { return BOTAN_FFI_CHECKED_DELETE(op); } diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index d688c7a4ef..b50509f35e 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -3161,7 +3161,7 @@ class FFI_Ed448_Test final : public FFI_Test { botan_pk_op_sign_t signer; std::vector signature; - if(TEST_FFI_OK(botan_pk_op_sign_create, (&signer, priv, "Pure", 0))) { + auto sign_fn = [&result, &rng, &msg, &signature](botan_pk_op_sign_t signer) { TEST_FFI_OK(botan_pk_op_sign_update, (signer, msg.data(), msg.size())); size_t sig_len; @@ -3173,6 +3173,16 @@ class FFI_Ed448_Test final : public FFI_Test { signature.resize(sig_len); TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); + }; + + if(TEST_FFI_OK(botan_pk_op_sign_create, (&signer, priv, "Pure", 0))) { + sign_fn(signer); + } + + result.test_eq("Expected signature", signature, sig_ref); + + if(TEST_FFI_OK(botan_pk_op_sign_create_with_rng, (&signer, rng, priv, "Pure", 0))) { + sign_fn(signer); } result.test_eq("Expected signature", signature, sig_ref); @@ -3498,91 +3508,102 @@ class FFI_Signature_Roundtrip_Test : public FFI_Test { const std::vector message2 = {'W', 'o', 'r', 'l', 'd', '!'}; for(auto mode : modes()) { - // generate a key pair - botan_privkey_t priv; - botan_pubkey_t pub; - if(!TEST_FFI_INIT(botan_privkey_create, (&priv, algo(), mode, rng))) { - continue; - } - TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); - - // raw-encode the key pair - ViewBytesSink priv_bytes; - ViewBytesSink pub_bytes; - TEST_FFI_OK(botan_privkey_view_raw, (priv, priv_bytes.delegate(), priv_bytes.callback())); - TEST_FFI_OK(botan_pubkey_view_raw, (pub, pub_bytes.delegate(), pub_bytes.callback())); - - // decode the key pair from raw encoding - botan_privkey_t priv_loaded; - botan_pubkey_t pub_loaded; - TEST_FFI_OK(private_key_load_function(), - (&priv_loaded, priv_bytes.get().data(), priv_bytes.get().size(), mode)); - TEST_FFI_OK(public_key_load_function(), - (&pub_loaded, pub_bytes.get().data(), pub_bytes.get().size(), mode)); - - // re-encode and compare to the first round - ViewBytesSink priv_bytes2; - ViewBytesSink pub_bytes2; - TEST_FFI_OK(botan_privkey_view_raw, (priv_loaded, priv_bytes2.delegate(), priv_bytes2.callback())); - TEST_FFI_OK(botan_pubkey_view_raw, (pub_loaded, pub_bytes2.delegate(), pub_bytes2.callback())); - result.test_eq("private key encoding", priv_bytes.get(), priv_bytes2.get()); - result.test_eq("public key encoding", pub_bytes.get(), pub_bytes2.get()); + for(auto specify_signature_rng : {true, false}) { + // generate a key pair + botan_privkey_t priv; + botan_pubkey_t pub; + if(!TEST_FFI_INIT(botan_privkey_create, (&priv, algo(), mode, rng))) { + continue; + } + TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); + + // raw-encode the key pair + ViewBytesSink priv_bytes; + ViewBytesSink pub_bytes; + TEST_FFI_OK(botan_privkey_view_raw, (priv, priv_bytes.delegate(), priv_bytes.callback())); + TEST_FFI_OK(botan_pubkey_view_raw, (pub, pub_bytes.delegate(), pub_bytes.callback())); + + // decode the key pair from raw encoding + botan_privkey_t priv_loaded; + botan_pubkey_t pub_loaded; + TEST_FFI_OK(private_key_load_function(), + (&priv_loaded, priv_bytes.get().data(), priv_bytes.get().size(), mode)); + TEST_FFI_OK(public_key_load_function(), + (&pub_loaded, pub_bytes.get().data(), pub_bytes.get().size(), mode)); + + // re-encode and compare to the first round + ViewBytesSink priv_bytes2; + ViewBytesSink pub_bytes2; + TEST_FFI_OK(botan_privkey_view_raw, (priv_loaded, priv_bytes2.delegate(), priv_bytes2.callback())); + TEST_FFI_OK(botan_pubkey_view_raw, (pub_loaded, pub_bytes2.delegate(), pub_bytes2.callback())); + result.test_eq("private key encoding", priv_bytes.get(), priv_bytes2.get()); + result.test_eq("public key encoding", pub_bytes.get(), pub_bytes2.get()); + + // Signature Creation (using the loaded private key) + botan_pk_op_sign_t signer; + if(specify_signature_rng) { + TEST_FFI_OK(botan_pk_op_sign_create_with_rng, (&signer, rng, priv_loaded, hash_algo_or_padding(), 0)); + } else { + TEST_FFI_OK(botan_pk_op_sign_create, (&signer, priv_loaded, hash_algo_or_padding(), 0)); + } - // Signature Creation (using the loaded private key) - botan_pk_op_sign_t signer; - TEST_FFI_OK(botan_pk_op_sign_create, (&signer, priv_loaded, hash_algo_or_padding(), 0)); + // explicitly query the signature output length + size_t sig_output_length = 0; + TEST_FFI_OK(botan_pk_op_sign_output_length, (signer, &sig_output_length)); - // explicitly query the signature output length - size_t sig_output_length = 0; - TEST_FFI_OK(botan_pk_op_sign_output_length, (signer, &sig_output_length)); + // pass a message to the signer + TEST_FFI_OK(botan_pk_op_sign_update, (signer, message1.data(), message1.size())); + TEST_FFI_OK(botan_pk_op_sign_update, (signer, message2.data(), message2.size())); - // pass a message to the signer - TEST_FFI_OK(botan_pk_op_sign_update, (signer, message1.data(), message1.size())); - TEST_FFI_OK(botan_pk_op_sign_update, (signer, message2.data(), message2.size())); + // check that insufficient buffer space is handled correctly + size_t sig_output_length_out = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, + botan_pk_op_sign_finish, + (signer, rng, nullptr, &sig_output_length_out)); + result.test_eq("reported sig lengths are equal", sig_output_length, sig_output_length_out); - // check that insufficient buffer space is handled correctly - size_t sig_output_length_out = 0; - TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, - botan_pk_op_sign_finish, - (signer, rng, nullptr, &sig_output_length_out)); - result.test_eq("reported sig lengths are equal", sig_output_length, sig_output_length_out); - - // Recreate signer and try again - TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); - TEST_FFI_OK(botan_pk_op_sign_create, (&signer, priv_loaded, hash_algo_or_padding(), 0)); - TEST_FFI_OK(botan_pk_op_sign_update, (signer, message1.data(), message1.size())); - TEST_FFI_OK(botan_pk_op_sign_update, (signer, message2.data(), message2.size())); - - // allocate buffers (with additional space) and perform the actual signing - sig_output_length_out = sig_output_length * 2; - Botan::secure_vector signature(sig_output_length_out); - TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &sig_output_length_out)); - result.test_eq("signature length", sig_output_length, sig_output_length_out); - signature.resize(sig_output_length_out); - TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); + // Recreate signer and try again + TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); + if(specify_signature_rng) { + TEST_FFI_OK(botan_pk_op_sign_create_with_rng, (&signer, rng, priv_loaded, hash_algo_or_padding(), 0)); + } else { + TEST_FFI_OK(botan_pk_op_sign_create, (&signer, priv_loaded, hash_algo_or_padding(), 0)); + } + TEST_FFI_OK(botan_pk_op_sign_update, (signer, message1.data(), message1.size())); + TEST_FFI_OK(botan_pk_op_sign_update, (signer, message2.data(), message2.size())); + + // allocate buffers (with additional space) and perform the actual signing + sig_output_length_out = sig_output_length * 2; + Botan::secure_vector signature(sig_output_length_out); + TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &sig_output_length_out)); + result.test_eq("signature length", sig_output_length, sig_output_length_out); + signature.resize(sig_output_length_out); + TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); - // Signature verification (using the generated public key) - botan_pk_op_verify_t verifier; - TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, hash_algo_or_padding(), 0)); - TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message1.data(), message1.size())); - TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message2.data(), message2.size())); + // Signature verification (using the generated public key) + botan_pk_op_verify_t verifier; + TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, hash_algo_or_padding(), 0)); + TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message1.data(), message1.size())); + TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message2.data(), message2.size())); - // Verify signature - TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); - TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier)); + // Verify signature + TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); + TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier)); - // Verify signature with wrong message (only first half) - TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, hash_algo_or_padding(), 0)); - TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message1.data(), message1.size())); - TEST_FFI_RC( - BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); - TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier)); + // Verify signature with wrong message (only first half) + TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, hash_algo_or_padding(), 0)); + TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message1.data(), message1.size())); + TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, + botan_pk_op_verify_finish, + (verifier, signature.data(), signature.size())); + TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier)); - // Cleanup - TEST_FFI_OK(botan_pubkey_destroy, (pub)); - TEST_FFI_OK(botan_pubkey_destroy, (pub_loaded)); - TEST_FFI_OK(botan_privkey_destroy, (priv)); - TEST_FFI_OK(botan_privkey_destroy, (priv_loaded)); + // Cleanup + TEST_FFI_OK(botan_pubkey_destroy, (pub)); + TEST_FFI_OK(botan_pubkey_destroy, (pub_loaded)); + TEST_FFI_OK(botan_privkey_destroy, (priv)); + TEST_FFI_OK(botan_privkey_destroy, (priv_loaded)); + } } } }; From dbe0ae8a5f79a2b91b6ee2179eb0dc64f68bfe4c Mon Sep 17 00:00:00 2001 From: Dirk Zimmermann Date: Sun, 27 Oct 2024 12:09:46 +0100 Subject: [PATCH 5/8] botan_pk_op_key_agreement_create_with_rng --- src/lib/ffi/ffi.h | 4 ++++ src/lib/ffi/ffi_pk_op.cpp | 19 +++++++++++++++++++ src/tests/test_ffi.cpp | 4 ++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index b480e923a1..70152b45c6 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -1724,6 +1724,10 @@ typedef struct botan_pk_op_ka_struct* botan_pk_op_ka_t; BOTAN_FFI_EXPORT(2, 0) int botan_pk_op_key_agreement_create(botan_pk_op_ka_t* op, botan_privkey_t key, const char* kdf, uint32_t flags); +BOTAN_FFI_EXPORT(3, 7) +int botan_pk_op_key_agreement_create_with_rng( + botan_pk_op_ka_t* op, botan_rng_t rng, botan_privkey_t key, const char* kdf, uint32_t flags); + /** * @return 0 if success, error if invalid object handle */ diff --git a/src/lib/ffi/ffi_pk_op.cpp b/src/lib/ffi/ffi_pk_op.cpp index ac21e14d23..f552a32307 100644 --- a/src/lib/ffi/ffi_pk_op.cpp +++ b/src/lib/ffi/ffi_pk_op.cpp @@ -271,6 +271,25 @@ int botan_pk_op_key_agreement_create(botan_pk_op_ka_t* op, botan_privkey_t key_o }); } +int botan_pk_op_key_agreement_create_with_rng( + botan_pk_op_ka_t* op, botan_rng_t rng_obj, botan_privkey_t key_obj, const char* kdf, uint32_t flags) { + if(op == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + if(flags != 0) { + return BOTAN_FFI_ERROR_BAD_FLAG; + } + + return ffi_guard_thunk(__func__, [=]() -> int { + Botan::RandomNumberGenerator& rng = safe_get(rng_obj); + *op = nullptr; + auto pk = std::make_unique(safe_get(key_obj), rng, kdf); + *op = new botan_pk_op_ka_struct(std::move(pk)); + return BOTAN_FFI_SUCCESS; + }); +} + int botan_pk_op_key_agreement_destroy(botan_pk_op_ka_t op) { return BOTAN_FFI_CHECKED_DELETE(op); } diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index b50509f35e..3a72b2f23e 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -2983,7 +2983,7 @@ class FFI_ECDH_Test final : public FFI_Test { botan_pk_op_ka_t ka1; REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka1, loaded_privkey1, "KDF2(SHA-256)", 0)); botan_pk_op_ka_t ka2; - REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka2, priv2, "KDF2(SHA-256)", 0)); + REQUIRE_FFI_OK(botan_pk_op_key_agreement_create_with_rng, (&ka2, rng, priv2, "KDF2(SHA-256)", 0)); size_t pubkey1_len = 0; TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, @@ -4004,7 +4004,7 @@ class FFI_DH_Test final : public FFI_Test { result.confirm("bigint_mp_cmp(y, y)", cmp == 0); botan_pk_op_ka_t ka1; - REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka1, loaded_privkey1, "Raw", 0)); + REQUIRE_FFI_OK(botan_pk_op_key_agreement_create_with_rng, (&ka1, rng, loaded_privkey1, "Raw", 0)); botan_pk_op_ka_t ka2; REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka2, priv2, "Raw", 0)); From da37371888620f2c68d43f580be4cf89540d43ae Mon Sep 17 00:00:00 2001 From: Dirk Zimmermann Date: Sun, 27 Oct 2024 12:17:50 +0100 Subject: [PATCH 6/8] Avoid shadowing --- src/tests/test_ffi.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 3a72b2f23e..68a5dc534e 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -3158,7 +3158,6 @@ class FFI_Ed448_Test final : public FFI_Test { TEST_FFI_OK(botan_pubkey_destroy, (pub)); TEST_FFI_OK(botan_pubkey_load_ed448, (&pub, pk_ref.data())); - botan_pk_op_sign_t signer; std::vector signature; auto sign_fn = [&result, &rng, &msg, &signature](botan_pk_op_sign_t signer) { @@ -3175,6 +3174,8 @@ class FFI_Ed448_Test final : public FFI_Test { TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); }; + botan_pk_op_sign_t signer; + if(TEST_FFI_OK(botan_pk_op_sign_create, (&signer, priv, "Pure", 0))) { sign_fn(signer); } From 02d2100aaf1ae2ddc51f966c7d69d8c3d2b02724 Mon Sep 17 00:00:00 2001 From: Dirk Zimmermann Date: Sun, 27 Oct 2024 13:22:09 +0100 Subject: [PATCH 7/8] botan_pk_op_kem_decrypt_create_with_rng --- src/lib/ffi/ffi.h | 6 ++++++ src/lib/ffi/ffi_pk_op.cpp | 16 ++++++++++++++++ src/tests/test_ffi.cpp | 9 ++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index 70152b45c6..3ed339ecbb 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -1787,6 +1787,12 @@ typedef struct botan_pk_op_kem_decrypt_struct* botan_pk_op_kem_decrypt_t; BOTAN_FFI_EXPORT(3, 0) int botan_pk_op_kem_decrypt_create(botan_pk_op_kem_decrypt_t* op, botan_privkey_t key, const char* kdf); +BOTAN_FFI_EXPORT(3, 7) +int botan_pk_op_kem_decrypt_create_with_rng(botan_pk_op_kem_decrypt_t* op, + botan_rng_t rng, + botan_privkey_t key, + const char* kdf); + /** * @return 0 if success, error if invalid object handle */ diff --git a/src/lib/ffi/ffi_pk_op.cpp b/src/lib/ffi/ffi_pk_op.cpp index f552a32307..254b4aa954 100644 --- a/src/lib/ffi/ffi_pk_op.cpp +++ b/src/lib/ffi/ffi_pk_op.cpp @@ -403,6 +403,22 @@ int botan_pk_op_kem_decrypt_create(botan_pk_op_kem_decrypt_t* op, botan_privkey_ }); } +int botan_pk_op_kem_decrypt_create_with_rng(botan_pk_op_kem_decrypt_t* op, + botan_rng_t rng_obj, + botan_privkey_t key_obj, + const char* padding) { + if(op == nullptr || padding == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + return ffi_guard_thunk(__func__, [=]() -> int { + Botan::RandomNumberGenerator& rng = safe_get(rng_obj); + auto pk = std::make_unique(safe_get(key_obj), rng, padding); + *op = new botan_pk_op_kem_decrypt_struct(std::move(pk)); + return BOTAN_FFI_SUCCESS; + }); +} + int botan_pk_op_kem_decrypt_shared_key_length(botan_pk_op_kem_decrypt_t op, size_t desired_shared_key_length, size_t* output_shared_key_length) { diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 68a5dc534e..1b7c554136 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -3359,6 +3359,7 @@ class FFI_KEM_Roundtrip_Test : public FFI_Test { public: void ffi_test(Test::Result& result, botan_rng_t rng) override { + bool use_explicit_rng = true; for(auto mode : modes()) { // generate a key pair botan_privkey_t priv; @@ -3442,7 +3443,11 @@ class FFI_KEM_Roundtrip_Test : public FFI_Test { // KEM decryption (using the generated private key) botan_pk_op_kem_decrypt_t kem_dec; - TEST_FFI_OK(botan_pk_op_kem_decrypt_create, (&kem_dec, priv, "Raw")); + if(use_explicit_rng) { + TEST_FFI_OK(botan_pk_op_kem_decrypt_create_with_rng, (&kem_dec, rng, priv, "Raw")); + } else { + TEST_FFI_OK(botan_pk_op_kem_decrypt_create, (&kem_dec, priv, "Raw")); + } size_t shared_key_length2 = 0; TEST_FFI_OK(botan_pk_op_kem_decrypt_shared_key_length, (kem_dec, shared_key_length, &shared_key_length2)); result.test_eq("shared key lengths are consistent", shared_key_length, shared_key_length2); @@ -3484,6 +3489,8 @@ class FFI_KEM_Roundtrip_Test : public FFI_Test { TEST_FFI_OK(botan_pubkey_destroy, (pub_loaded)); TEST_FFI_OK(botan_privkey_destroy, (priv)); TEST_FFI_OK(botan_privkey_destroy, (priv_loaded)); + + use_explicit_rng = !use_explicit_rng; } } }; From 598a45eff4a6b828e8e92cf8ad3b58751b93edde Mon Sep 17 00:00:00 2001 From: Dirk Zimmermann Date: Sun, 27 Oct 2024 13:27:53 +0100 Subject: [PATCH 8/8] Less intrusive test switch between rng/non-rng --- src/tests/test_ffi.cpp | 174 ++++++++++++++++++++--------------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 1b7c554136..f4e59757aa 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -3515,103 +3515,103 @@ class FFI_Signature_Roundtrip_Test : public FFI_Test { const std::vector message1 = {'H', 'e', 'l', 'l', 'o', ' '}; const std::vector message2 = {'W', 'o', 'r', 'l', 'd', '!'}; + bool use_explicit_rng = true; for(auto mode : modes()) { - for(auto specify_signature_rng : {true, false}) { - // generate a key pair - botan_privkey_t priv; - botan_pubkey_t pub; - if(!TEST_FFI_INIT(botan_privkey_create, (&priv, algo(), mode, rng))) { - continue; - } - TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); - - // raw-encode the key pair - ViewBytesSink priv_bytes; - ViewBytesSink pub_bytes; - TEST_FFI_OK(botan_privkey_view_raw, (priv, priv_bytes.delegate(), priv_bytes.callback())); - TEST_FFI_OK(botan_pubkey_view_raw, (pub, pub_bytes.delegate(), pub_bytes.callback())); - - // decode the key pair from raw encoding - botan_privkey_t priv_loaded; - botan_pubkey_t pub_loaded; - TEST_FFI_OK(private_key_load_function(), - (&priv_loaded, priv_bytes.get().data(), priv_bytes.get().size(), mode)); - TEST_FFI_OK(public_key_load_function(), - (&pub_loaded, pub_bytes.get().data(), pub_bytes.get().size(), mode)); - - // re-encode and compare to the first round - ViewBytesSink priv_bytes2; - ViewBytesSink pub_bytes2; - TEST_FFI_OK(botan_privkey_view_raw, (priv_loaded, priv_bytes2.delegate(), priv_bytes2.callback())); - TEST_FFI_OK(botan_pubkey_view_raw, (pub_loaded, pub_bytes2.delegate(), pub_bytes2.callback())); - result.test_eq("private key encoding", priv_bytes.get(), priv_bytes2.get()); - result.test_eq("public key encoding", pub_bytes.get(), pub_bytes2.get()); - - // Signature Creation (using the loaded private key) - botan_pk_op_sign_t signer; - if(specify_signature_rng) { - TEST_FFI_OK(botan_pk_op_sign_create_with_rng, (&signer, rng, priv_loaded, hash_algo_or_padding(), 0)); - } else { - TEST_FFI_OK(botan_pk_op_sign_create, (&signer, priv_loaded, hash_algo_or_padding(), 0)); - } + // generate a key pair + botan_privkey_t priv; + botan_pubkey_t pub; + if(!TEST_FFI_INIT(botan_privkey_create, (&priv, algo(), mode, rng))) { + continue; + } + TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); - // explicitly query the signature output length - size_t sig_output_length = 0; - TEST_FFI_OK(botan_pk_op_sign_output_length, (signer, &sig_output_length)); + // raw-encode the key pair + ViewBytesSink priv_bytes; + ViewBytesSink pub_bytes; + TEST_FFI_OK(botan_privkey_view_raw, (priv, priv_bytes.delegate(), priv_bytes.callback())); + TEST_FFI_OK(botan_pubkey_view_raw, (pub, pub_bytes.delegate(), pub_bytes.callback())); - // pass a message to the signer - TEST_FFI_OK(botan_pk_op_sign_update, (signer, message1.data(), message1.size())); - TEST_FFI_OK(botan_pk_op_sign_update, (signer, message2.data(), message2.size())); + // decode the key pair from raw encoding + botan_privkey_t priv_loaded; + botan_pubkey_t pub_loaded; + TEST_FFI_OK(private_key_load_function(), + (&priv_loaded, priv_bytes.get().data(), priv_bytes.get().size(), mode)); + TEST_FFI_OK(public_key_load_function(), + (&pub_loaded, pub_bytes.get().data(), pub_bytes.get().size(), mode)); - // check that insufficient buffer space is handled correctly - size_t sig_output_length_out = 0; - TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, - botan_pk_op_sign_finish, - (signer, rng, nullptr, &sig_output_length_out)); - result.test_eq("reported sig lengths are equal", sig_output_length, sig_output_length_out); + // re-encode and compare to the first round + ViewBytesSink priv_bytes2; + ViewBytesSink pub_bytes2; + TEST_FFI_OK(botan_privkey_view_raw, (priv_loaded, priv_bytes2.delegate(), priv_bytes2.callback())); + TEST_FFI_OK(botan_pubkey_view_raw, (pub_loaded, pub_bytes2.delegate(), pub_bytes2.callback())); + result.test_eq("private key encoding", priv_bytes.get(), priv_bytes2.get()); + result.test_eq("public key encoding", pub_bytes.get(), pub_bytes2.get()); - // Recreate signer and try again - TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); - if(specify_signature_rng) { - TEST_FFI_OK(botan_pk_op_sign_create_with_rng, (&signer, rng, priv_loaded, hash_algo_or_padding(), 0)); - } else { - TEST_FFI_OK(botan_pk_op_sign_create, (&signer, priv_loaded, hash_algo_or_padding(), 0)); - } - TEST_FFI_OK(botan_pk_op_sign_update, (signer, message1.data(), message1.size())); - TEST_FFI_OK(botan_pk_op_sign_update, (signer, message2.data(), message2.size())); - - // allocate buffers (with additional space) and perform the actual signing - sig_output_length_out = sig_output_length * 2; - Botan::secure_vector signature(sig_output_length_out); - TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &sig_output_length_out)); - result.test_eq("signature length", sig_output_length, sig_output_length_out); - signature.resize(sig_output_length_out); - TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); + // Signature Creation (using the loaded private key) + botan_pk_op_sign_t signer; + if(use_explicit_rng) { + TEST_FFI_OK(botan_pk_op_sign_create_with_rng, (&signer, rng, priv_loaded, hash_algo_or_padding(), 0)); + } else { + TEST_FFI_OK(botan_pk_op_sign_create, (&signer, priv_loaded, hash_algo_or_padding(), 0)); + } - // Signature verification (using the generated public key) - botan_pk_op_verify_t verifier; - TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, hash_algo_or_padding(), 0)); - TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message1.data(), message1.size())); - TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message2.data(), message2.size())); + // explicitly query the signature output length + size_t sig_output_length = 0; + TEST_FFI_OK(botan_pk_op_sign_output_length, (signer, &sig_output_length)); - // Verify signature - TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); - TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier)); + // pass a message to the signer + TEST_FFI_OK(botan_pk_op_sign_update, (signer, message1.data(), message1.size())); + TEST_FFI_OK(botan_pk_op_sign_update, (signer, message2.data(), message2.size())); - // Verify signature with wrong message (only first half) - TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, hash_algo_or_padding(), 0)); - TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message1.data(), message1.size())); - TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, - botan_pk_op_verify_finish, - (verifier, signature.data(), signature.size())); - TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier)); + // check that insufficient buffer space is handled correctly + size_t sig_output_length_out = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, + botan_pk_op_sign_finish, + (signer, rng, nullptr, &sig_output_length_out)); + result.test_eq("reported sig lengths are equal", sig_output_length, sig_output_length_out); - // Cleanup - TEST_FFI_OK(botan_pubkey_destroy, (pub)); - TEST_FFI_OK(botan_pubkey_destroy, (pub_loaded)); - TEST_FFI_OK(botan_privkey_destroy, (priv)); - TEST_FFI_OK(botan_privkey_destroy, (priv_loaded)); + // Recreate signer and try again + TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); + if(use_explicit_rng) { + TEST_FFI_OK(botan_pk_op_sign_create_with_rng, (&signer, rng, priv_loaded, hash_algo_or_padding(), 0)); + } else { + TEST_FFI_OK(botan_pk_op_sign_create, (&signer, priv_loaded, hash_algo_or_padding(), 0)); } + TEST_FFI_OK(botan_pk_op_sign_update, (signer, message1.data(), message1.size())); + TEST_FFI_OK(botan_pk_op_sign_update, (signer, message2.data(), message2.size())); + + // allocate buffers (with additional space) and perform the actual signing + sig_output_length_out = sig_output_length * 2; + Botan::secure_vector signature(sig_output_length_out); + TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &sig_output_length_out)); + result.test_eq("signature length", sig_output_length, sig_output_length_out); + signature.resize(sig_output_length_out); + TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); + + // Signature verification (using the generated public key) + botan_pk_op_verify_t verifier; + TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, hash_algo_or_padding(), 0)); + TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message1.data(), message1.size())); + TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message2.data(), message2.size())); + + // Verify signature + TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); + TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier)); + + // Verify signature with wrong message (only first half) + TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, hash_algo_or_padding(), 0)); + TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message1.data(), message1.size())); + TEST_FFI_RC( + BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); + TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier)); + + // Cleanup + TEST_FFI_OK(botan_pubkey_destroy, (pub)); + TEST_FFI_OK(botan_pubkey_destroy, (pub_loaded)); + TEST_FFI_OK(botan_privkey_destroy, (priv)); + TEST_FFI_OK(botan_privkey_destroy, (priv_loaded)); + + use_explicit_rng = !use_explicit_rng; } } };