diff options
author | Jack Lloyd <[email protected]> | 2017-06-12 15:32:08 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-06-12 15:32:08 -0400 |
commit | 40feca3a3235d9ca49e7443619451205ccb3fd06 (patch) | |
tree | 653d281ad9417c7186a8a881687a61dc01a37d9a | |
parent | 6149a8a7ac9dd940c34af7f41e10610f75e8995b (diff) | |
parent | 5ba835fd0469f1b52d2dedfc64bbd63fe0f8e5b8 (diff) |
Merge GH #1074 Add ability to load ECDSA/ECDH via FFI
-rw-r--r-- | src/lib/ffi/ffi.cpp | 155 | ||||
-rw-r--r-- | src/lib/ffi/ffi.h | 21 | ||||
-rw-r--r-- | src/tests/test_ffi.cpp | 144 |
3 files changed, 265 insertions, 55 deletions
diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp index d7f164b86..f46d7ec11 100644 --- a/src/lib/ffi/ffi.cpp +++ b/src/lib/ffi/ffi.cpp @@ -330,7 +330,59 @@ Botan::BigInt privkey_get_field(const Botan::Private_Key& key, throw Botan::Exception("Unsupported algorithm type for botan_privkey_get_field"); } -} +#if defined(BOTAN_HAS_ECDSA) || defined(BOTAN_HAS_ECDH) +template<class ECPrivateKey_t> +int privkey_load_ec(std::unique_ptr<ECPrivateKey_t>& key, + const Botan::BigInt& scalar, + const char* curve_name) + { + + if(curve_name == nullptr) + return -1; + + try + { + Botan::Null_RNG null_rng; + Botan::EC_Group grp(curve_name); + key.reset(new ECPrivateKey_t(null_rng, grp, scalar)); + return 0; + } + catch(std::exception& e) + { + log_exception(BOTAN_CURRENT_FUNCTION, e.what()); + } + return -1; + } +#endif + +#if defined(BOTAN_HAS_ECDSA) || defined(BOTAN_HAS_ECDH) +template<class ECPublicKey_t> +int pubkey_load_ec( std::unique_ptr<ECPublicKey_t>& key, + const Botan::BigInt& public_x, + const Botan::BigInt& public_y, + const char* curve_name) + { + + if(curve_name == nullptr) + return -1; + + try + { + Botan::Null_RNG null_rng; + Botan::EC_Group grp(curve_name); + Botan::PointGFp uncompressed_point(grp.get_curve(), public_x, public_y); + key.reset(new ECPublicKey_t(grp, uncompressed_point)); + return 0; + } + catch(std::exception& e) + { + log_exception(BOTAN_CURRENT_FUNCTION, e.what()); + } + return -1; + } +#endif + +} // closes anonymous namespace extern "C" { @@ -1699,6 +1751,107 @@ int botan_pubkey_ed25519_get_pubkey(botan_pubkey_t key, #endif } +int botan_pubkey_load_ecdsa(botan_pubkey_t* key, + const botan_mp_t public_x, + const botan_mp_t public_y, + const char* curve_name) + { +#if defined(BOTAN_HAS_ECDSA) + std::unique_ptr<Botan::ECDSA_PublicKey> p_key; + try + { + if(!pubkey_load_ec(p_key, safe_get(public_x), safe_get(public_y), curve_name)) + { + *key = new botan_pubkey_struct(p_key.release()); + return 0; + } + } + catch(std::exception& exn) + { + log_exception(BOTAN_CURRENT_FUNCTION, exn.what()); + } + return -1; +#else + BOTAN_UNUSED(key, public_x, public_y, curve_name); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + +int botan_pubkey_load_ecdh(botan_pubkey_t* key, + const botan_mp_t public_x, + const botan_mp_t public_y, + const char* curve_name) + { +#if defined(BOTAN_HAS_ECDH) + std::unique_ptr<Botan::ECDH_PublicKey> p_key; + try + { + if(!pubkey_load_ec(p_key, safe_get(public_x), safe_get(public_y), curve_name)) + { + *key = new botan_pubkey_struct(p_key.release()); + return 0; + } + } + catch(std::exception& exn) + { + log_exception(BOTAN_CURRENT_FUNCTION, exn.what()); + } + return -1; +#else + BOTAN_UNUSED(key, public_x, public_y, curve_name); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + +int botan_privkey_load_ecdsa(botan_privkey_t* key, + const botan_mp_t scalar, + const char* curve_name) + { +#if defined(BOTAN_HAS_ECDSA) + std::unique_ptr<Botan::ECDSA_PrivateKey> p_key; + try + { + if(!privkey_load_ec(p_key, safe_get(scalar), curve_name)) + { + *key = new botan_privkey_struct(p_key.release()); + return 0; + } + } + catch(std::exception& exn) + { + log_exception(BOTAN_CURRENT_FUNCTION, exn.what()); + } + return -1; +#else + BOTAN_UNUSED(key, scalar, curve_name); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } +int botan_privkey_load_ecdh(botan_privkey_t* key, + const botan_mp_t scalar, + const char* curve_name) + { +#if defined(BOTAN_HAS_ECDH) + std::unique_ptr<Botan::ECDH_PrivateKey> p_key; + try + { + if(!privkey_load_ec(p_key, safe_get(scalar), curve_name)) + { + *key = new botan_privkey_struct(p_key.release()); + return 0; + } + } + catch(std::exception& exn) + { + log_exception(BOTAN_CURRENT_FUNCTION, exn.what()); + } + return -1; +#else + BOTAN_UNUSED(key, scalar, curve_name); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + int botan_pubkey_get_field(botan_mp_t output, botan_pubkey_t key, const char* field_name_cstr) diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index 3e64c69cd..a179e5fa4 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -862,6 +862,27 @@ BOTAN_DLL int botan_pubkey_ed25519_get_pubkey(botan_pubkey_t key, uint8_t pubkey[32]); /* +* Algorithm specific key operations: ECDSA and ECDH +*/ +BOTAN_DLL int botan_privkey_load_ecdsa(botan_privkey_t* key, + const botan_mp_t scalar, + const char* curve_name); + +BOTAN_DLL int botan_pubkey_load_ecdsa(botan_pubkey_t* key, + const botan_mp_t public_x, + const botan_mp_t public_y, + const char* curve_name); + +BOTAN_DLL int botan_pubkey_load_ecdh(botan_pubkey_t* key, + const botan_mp_t public_x, + const botan_mp_t public_y, + const char* curve_name); + +BOTAN_DLL int botan_privkey_load_ecdh(botan_privkey_t* key, + const botan_mp_t scalar, + const char* curve_name); + +/* * Public Key Encryption */ typedef struct botan_pk_op_encrypt_struct* botan_pk_op_encrypt_t; diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 75fe337f3..e9d9b6e20 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -1196,71 +1196,87 @@ class FFI_Unit_Tests : public Test Test::Result ffi_test_ecdsa(botan_rng_t rng) { Test::Result result("FFI ECDSA"); - + static const char* kCurve = "secp384r1"; botan_privkey_t priv; + botan_pubkey_t pub; + botan_privkey_t loaded_privkey; + botan_pubkey_t loaded_pubkey; - if(TEST_FFI_OK(botan_privkey_create_ecdsa, (&priv, rng, "secp384r1"))) + REQUIRE_FFI_OK(botan_privkey_create_ecdsa, (&priv, rng, kCurve)); + TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); + ffi_test_pubkey_export(result, pub, priv, rng); + + // Check key load functions + botan_mp_t private_scalar, public_x, public_y; + botan_mp_init(&private_scalar); + botan_mp_init(&public_x); + botan_mp_init(&public_y); + + TEST_FFI_OK(botan_privkey_get_field, (private_scalar, priv, "x")); + TEST_FFI_OK(botan_pubkey_get_field, (public_x, pub, "public_x")); + TEST_FFI_OK(botan_pubkey_get_field, (public_y, pub, "public_y")); + TEST_FFI_OK(botan_privkey_load_ecdsa, (&loaded_privkey, private_scalar, kCurve)); + TEST_FFI_OK(botan_pubkey_load_ecdsa, (&loaded_pubkey, public_x, public_y, kCurve)); + TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0)); + TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0)); + + char namebuf[32] = { 0 }; + size_t name_len = sizeof(namebuf); + + TEST_FFI_OK(botan_pubkey_algo_name, (pub, &namebuf[0], &name_len)); + result.test_eq(namebuf, namebuf, "ECDSA"); + + std::vector<uint8_t> message(1280), signature; + TEST_FFI_OK(botan_rng_get, (rng, message.data(), message.size())); + botan_pk_op_sign_t signer; + if(TEST_FFI_OK(botan_pk_op_sign_create, (&signer, loaded_privkey, "EMSA1(SHA-384)", 0))) { - botan_pubkey_t pub; - TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); - - ffi_test_pubkey_export(result, pub, priv, rng); - - char namebuf[32] = { 0 }; - size_t name_len = sizeof(namebuf); - TEST_FFI_OK(botan_pubkey_algo_name, (pub, &namebuf[0], &name_len)); - - result.test_eq(namebuf, namebuf, "ECDSA"); - - std::vector<uint8_t> message(1280), signature; - TEST_FFI_OK(botan_rng_get, (rng, message.data(), message.size())); - - botan_pk_op_sign_t signer; - - if(TEST_FFI_OK(botan_pk_op_sign_create, (&signer, priv, "EMSA1(SHA-384)", 0))) - { - // TODO: break input into multiple calls to update - TEST_FFI_OK(botan_pk_op_sign_update, (signer, message.data(), message.size())); - - signature.resize(96); // TODO: no way to derive this from API - size_t sig_len = signature.size(); - TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &sig_len)); - signature.resize(sig_len); + // TODO: break input into multiple calls to update + TEST_FFI_OK(botan_pk_op_sign_update, (signer, message.data(), message.size())); - TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); - } + signature.resize(96); // TODO: no way to derive this from API + size_t sig_len = signature.size(); + TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &sig_len)); + signature.resize(sig_len); - botan_pk_op_verify_t verifier; + TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); + } - if(TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, "EMSA1(SHA-384)", 0))) - { - TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size())); - TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); + botan_pk_op_verify_t verifier; - // TODO: randomize this - signature[0] ^= 1; - TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size())); - TEST_FFI_FAIL("bad signature", botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); + if(TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, "EMSA1(SHA-384)", 0))) + { + TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size())); + TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); - message[0] ^= 1; - TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size())); - TEST_FFI_FAIL("bad signature", botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); + // TODO: randomize this + signature[0] ^= 1; + TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size())); + TEST_FFI_FAIL("bad signature", botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); - signature[0] ^= 1; - TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size())); - TEST_FFI_FAIL("bad signature", botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); + message[0] ^= 1; + TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size())); + TEST_FFI_FAIL("bad signature", botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); - message[0] ^= 1; - TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size())); - TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); + signature[0] ^= 1; + TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size())); + TEST_FFI_FAIL("bad signature", botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); - TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier)); - } + message[0] ^= 1; + TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size())); + TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); - TEST_FFI_OK(botan_pubkey_destroy, (pub)); - TEST_FFI_OK(botan_privkey_destroy, (priv)); + TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier)); } + TEST_FFI_OK(botan_mp_destroy, (private_scalar)); + TEST_FFI_OK(botan_mp_destroy, (public_x)); + TEST_FFI_OK(botan_mp_destroy, (public_y)); + TEST_FFI_OK(botan_pubkey_destroy, (pub)); + TEST_FFI_OK(botan_privkey_destroy, (priv)); + TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey)); + TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey)); + return result; } @@ -1268,6 +1284,13 @@ class FFI_Unit_Tests : public Test { Test::Result result("FFI ECDH"); + botan_mp_t private_scalar, public_x, public_y; + botan_privkey_t loaded_privkey1; + botan_pubkey_t loaded_pubkey1; + botan_mp_init(&private_scalar); + botan_mp_init(&public_x); + botan_mp_init(&public_y); + botan_privkey_t priv1; REQUIRE_FFI_OK(botan_privkey_create_ecdh, (&priv1, rng, "secp256r1")); @@ -1280,11 +1303,20 @@ class FFI_Unit_Tests : public Test botan_pubkey_t pub2; REQUIRE_FFI_OK(botan_privkey_export_pubkey, (&pub2, priv2)); - ffi_test_pubkey_export(result, pub1, priv1, rng); + /* Reload key-pair1 in order to test functions for key loading */ + TEST_FFI_OK(botan_privkey_get_field, (private_scalar, priv1, "x")); + TEST_FFI_OK(botan_pubkey_get_field, (public_x, pub1, "public_x")); + TEST_FFI_OK(botan_pubkey_get_field, (public_y, pub1, "public_y")); + TEST_FFI_OK(botan_privkey_load_ecdh, (&loaded_privkey1, private_scalar, "secp256r1")); + TEST_FFI_OK(botan_pubkey_load_ecdh, (&loaded_pubkey1, public_x, public_y, "secp256r1")); + TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey1, rng, 0)); + TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey1, rng, 0)); + + ffi_test_pubkey_export(result, loaded_pubkey1, priv1, rng); ffi_test_pubkey_export(result, pub2, priv2, rng); botan_pk_op_ka_t ka1; - REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka1, priv1, "KDF2(SHA-256)", 0)); + 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)); @@ -1317,13 +1349,17 @@ class FFI_Unit_Tests : public Test result.test_eq("shared ECDH key", key1, key2); + TEST_FFI_OK(botan_mp_destroy, (private_scalar)); + TEST_FFI_OK(botan_mp_destroy, (public_x)); + TEST_FFI_OK(botan_mp_destroy, (public_y)); TEST_FFI_OK(botan_pk_op_key_agreement_destroy, (ka1)); TEST_FFI_OK(botan_pk_op_key_agreement_destroy, (ka2)); TEST_FFI_OK(botan_privkey_destroy, (priv1)); TEST_FFI_OK(botan_privkey_destroy, (priv2)); TEST_FFI_OK(botan_pubkey_destroy, (pub1)); TEST_FFI_OK(botan_pubkey_destroy, (pub2)); - + TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey1)); + TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey1)); return result; } |