]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
openssl: add ecc_edch()
authorDan Streetman <ddstreet@ieee.org>
Fri, 7 Jul 2023 14:11:07 +0000 (10:11 -0400)
committerDan Streetman <ddstreet@ieee.org>
Thu, 28 Sep 2023 20:44:42 +0000 (16:44 -0400)
Add function to perform ECC EDCH.

src/shared/openssl-util.c
src/shared/openssl-util.h
src/test/test-openssl.c

index 58e4ab02e9e3e592e2d5427a00c9d619a599fea7..0015aa5b98cf50a413d3c4cc50d632e9638e60a1 100644 (file)
@@ -894,6 +894,48 @@ int ecc_pkey_new(int curve_id, EVP_PKEY **ret) {
         return 0;
 }
 
+/* Perform ECDH to derive an ECC shared secret between the provided private key and public peer key. For two
+ * keys, this will result in the same shared secret in either direction; ECDH using Alice's private key and
+ * Bob's public (peer) key will result in the same shared secret as ECDH using Bob's private key and Alice's
+ * public (peer) key. On success, this returns 0 and provides the shared secret; otherwise this returns an
+ * error. */
+int ecc_ecdh(const EVP_PKEY *private_pkey,
+             const EVP_PKEY *peer_pkey,
+             void **ret_shared_secret,
+             size_t *ret_shared_secret_size) {
+
+        assert(private_pkey);
+        assert(peer_pkey);
+        assert(ret_shared_secret);
+        assert(ret_shared_secret_size);
+
+        _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new((EVP_PKEY*) private_pkey, NULL);
+        if (!ctx)
+                return log_openssl_errors("Failed to create new EVP_PKEY_CTX");
+
+        if (EVP_PKEY_derive_init(ctx) <= 0)
+                return log_openssl_errors("Failed to initialize EVP_PKEY_CTX");
+
+        if (EVP_PKEY_derive_set_peer(ctx, (EVP_PKEY*) peer_pkey) <= 0)
+                return log_openssl_errors("Failed to set ECC derive peer");
+
+        size_t shared_secret_size;
+        if (EVP_PKEY_derive(ctx, NULL, &shared_secret_size) <= 0)
+                return log_openssl_errors("Failed to get ECC shared secret size");
+
+        _cleanup_free_ void *shared_secret = malloc(shared_secret_size);
+        if (!shared_secret)
+                return log_oom_debug();
+
+        if (EVP_PKEY_derive(ctx, (unsigned char*) shared_secret, &shared_secret_size) <= 0)
+                return log_openssl_errors("Failed to derive ECC shared secret");
+
+        *ret_shared_secret = TAKE_PTR(shared_secret);
+        *ret_shared_secret_size = shared_secret_size;
+
+        return 0;
+}
+
 int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size) {
         _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* m = NULL;
         _cleanup_free_ void *d = NULL, *h = NULL;
index 9715545c1fbf936842fb4214d5a6940f894aa978..a06ad5b388b94dc6887b33e566e69e83fa101615 100644 (file)
@@ -104,6 +104,8 @@ int ecc_pkey_to_curve_x_y(const EVP_PKEY *pkey, int *ret_curve_id, void **ret_x,
 
 int ecc_pkey_new(int curve_id, EVP_PKEY **ret);
 
+int ecc_ecdh(const EVP_PKEY *private_pkey, const EVP_PKEY *peer_pkey, void **ret_shared_secret, size_t *ret_shared_secret_size);
+
 int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size);
 
 int digest_and_sign(const EVP_MD *md, EVP_PKEY *privkey, const void *data, size_t size, void **ret, size_t *ret_size);
index cb25b7a9c3ef00873455cd57a1a337afdea5cc9c..586c090c03057fcf0c583f61ca1298786d24e5c5 100644 (file)
@@ -424,4 +424,23 @@ TEST(openssl_cipher) {
                 /* expected= */ NULL);
 }
 
+TEST(ecc_ecdh) {
+        _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkeyA = NULL, *pkeyB = NULL, *pkeyC = NULL;
+        _cleanup_free_ void *secretAB = NULL, *secretBA = NULL, *secretAC = NULL, *secretCA = NULL;
+        size_t secretAB_size, secretBA_size, secretAC_size, secretCA_size;
+
+        assert_se(ecc_pkey_new(NID_X9_62_prime256v1, &pkeyA) >= 0);
+        assert_se(ecc_pkey_new(NID_X9_62_prime256v1, &pkeyB) >= 0);
+        assert_se(ecc_pkey_new(NID_X9_62_prime256v1, &pkeyC) >= 0);
+
+        assert_se(ecc_ecdh(pkeyA, pkeyB, &secretAB, &secretAB_size) >= 0);
+        assert_se(ecc_ecdh(pkeyB, pkeyA, &secretBA, &secretBA_size) >= 0);
+        assert_se(ecc_ecdh(pkeyA, pkeyC, &secretAC, &secretAC_size) >= 0);
+        assert_se(ecc_ecdh(pkeyC, pkeyA, &secretCA, &secretCA_size) >= 0);
+
+        assert_se(memcmp_nn(secretAB, secretAB_size, secretBA, secretBA_size) == 0);
+        assert_se(memcmp_nn(secretAC, secretAC_size, secretCA, secretCA_size) == 0);
+        assert_se(memcmp_nn(secretAC, secretAC_size, secretAB, secretAB_size) != 0);
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);