aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-06-12 15:32:08 -0400
committerJack Lloyd <[email protected]>2017-06-12 15:32:08 -0400
commit40feca3a3235d9ca49e7443619451205ccb3fd06 (patch)
tree653d281ad9417c7186a8a881687a61dc01a37d9a
parent6149a8a7ac9dd940c34af7f41e10610f75e8995b (diff)
parent5ba835fd0469f1b52d2dedfc64bbd63fe0f8e5b8 (diff)
Merge GH #1074 Add ability to load ECDSA/ECDH via FFI
-rw-r--r--src/lib/ffi/ffi.cpp155
-rw-r--r--src/lib/ffi/ffi.h21
-rw-r--r--src/tests/test_ffi.cpp144
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;
}