53ad01bd创建于 2024年11月1日历史提交
From d2d35d1acb89d4113647a9aad2d049808112b935 Mon Sep 17 00:00:00 2001
From: Michael Schroeder <mls@suse.de>
Date: Wed, 17 Apr 2024 14:07:53 +0200
Subject: [PATCH] Add ECDSA support to digest_openssl

Conflict:modify digest_openssl.c in rpmio; adapt context
Reference:https://github.com/rpm-software-management/rpmpgp_legacy/commit/783a5ea3851b8509eb11a4998d6e4ea41cc7ba38
---
 rpmio/digest_openssl.c | 208 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 207 insertions(+), 1 deletion(-)

diff --git a/rpmio/digest_openssl.c b/rpmio/digest_openssl.c
index 4d930c9..c8eb15f 100644
--- a/rpmio/digest_openssl.c
+++ b/rpmio/digest_openssl.c
@@ -6,6 +6,7 @@
 #endif
 #include <openssl/rsa.h>
 #include <openssl/dsa.h>
+#include <openssl/ec.h>
 
 #include <rpm/rpmcrypto.h>
 #include "rpmio/rpmpgp_internal.h"
@@ -801,6 +802,181 @@ done:
     return rc;
 }
 
+/****************************** ECDSA ***************************************/
+
+struct pgpDigKeyECDSA_s {
+    EVP_PKEY *evp_pkey; /* Fully constructed key */
+    unsigned char *q;	/* compressed point */
+    int qlen;
+};
+
+static int constructECDSASigningKey(struct pgpDigKeyECDSA_s *key, int curve)
+{
+    if (key->evp_pkey)
+	return 1;	/* We've already constructed it, so just reuse it */
+
+#if OPENSSL_VERSION_MAJOR >= 3
+    if (curve == PGPCURVE_NIST_P_256) {
+	OSSL_PARAM params[] = {
+	    OSSL_PARAM_utf8_string("group", "P-256", 5),
+	    OSSL_PARAM_octet_string("pub", key->q, key->qlen),
+	    OSSL_PARAM_END
+	};
+	key->evp_pkey = construct_pkey_from_param(EVP_PKEY_EC, params);
+    } else if (curve == PGPCURVE_NIST_P_384) {
+	OSSL_PARAM params[] = {
+	    OSSL_PARAM_utf8_string("group", "P-384", 5),
+	    OSSL_PARAM_octet_string("pub", key->q, key->qlen),
+	    OSSL_PARAM_END
+	};
+	key->evp_pkey = construct_pkey_from_param(EVP_PKEY_EC, params);
+    }
+    return key->evp_pkey ? 1 : 0;
+#else
+    /* Create the EC key */
+    EC_KEY *ec = NULL;
+    if (curve == PGPCURVE_NIST_P_256)
+	ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+    else if (curve == PGPCURVE_NIST_P_384)
+	ec = EC_KEY_new_by_curve_name(NID_secp384r1);
+    if (!ec)
+	return 0;
+
+    if (!EC_KEY_oct2key(ec, key->q, key->qlen, NULL))
+        goto exit;
+
+    /* Create an EVP_PKEY container to abstract the key-type. */
+    if (!(key->evp_pkey = EVP_PKEY_new()))
+	goto exit;
+
+    /* Assign the EC key to the EVP_PKEY structure.
+       This will take over memory management of the RSA key */
+    if (!EVP_PKEY_assign_EC_KEY(key->evp_pkey, ec)) {
+        EVP_PKEY_free(key->evp_pkey);
+        key->evp_pkey = NULL;
+	goto exit;
+    }
+    return 1;
+
+exit:
+    EC_KEY_free(ec);
+    return 0;
+#endif
+}
+
+static int pgpSetKeyMpiECDSA(pgpDigAlg pgpkey, int num, const uint8_t *p)
+{
+    size_t mlen = pgpMpiLen(p) - 2;
+    struct pgpDigKeyECDSA_s *key = pgpkey->data;
+    int rc = 1;
+
+    if (!key)
+	key = pgpkey->data = xcalloc(1, sizeof(*key));
+    if (num == 0 && !key->q && mlen > 1 && p[2] == 0x04) {
+	key->qlen = mlen;
+	key->q = xmalloc(key->qlen);
+	memcpy(key->q, p + 2, key->qlen),
+	rc = 0;
+    }
+    return rc;
+}
+
+static void pgpFreeKeyECDSA(pgpDigAlg pgpkey)
+{
+    struct pgpDigKeyECDSA_s *key = pgpkey->data;
+    if (key) {
+	if (key->q)
+	    free(key->q);
+	if (key->evp_pkey)
+	    EVP_PKEY_free(key->evp_pkey);
+	free(key);
+    }
+}
+
+struct pgpDigSigECDSA_s {
+    unsigned char *r;
+    int rlen;
+    unsigned char *s;
+    int slen;
+};
+
+static int pgpSetSigMpiECDSA(pgpDigAlg pgpsig, int num, const uint8_t *p)
+{
+    int mlen = pgpMpiLen(p) - 2;
+    int rc = 1;
+
+    struct pgpDigSigECDSA_s *sig = pgpsig->data;
+    if (!sig) {
+        sig = xcalloc(1, sizeof(*sig));
+	pgpsig->data = sig;
+    }
+
+    switch (num) {
+    case 0:
+        if (sig->r)
+            return 1;	/* This should only ever happen once per signature */
+	sig->rlen = mlen;
+        sig->r = memcpy(xmalloc(mlen), p + 2, mlen);
+        rc = 0;
+        break;
+    case 1:
+        if (sig->s)
+            return 1;	/* This should only ever happen once per signature */
+	sig->slen = mlen;
+        sig->s = memcpy(xmalloc(mlen), p + 2, mlen);
+        rc = 0;
+        break;
+    }
+
+    return rc;
+}
+
+static void pgpFreeSigECDSA(pgpDigAlg pgpsig)
+{
+    struct pgpDigSigECDSA_s *sig = pgpsig->data;
+    if (sig) {
+	free(sig->r);
+	free(sig->s);
+    }
+    free(pgpsig->data);
+}
+
+static int pgpVerifySigECDSA(pgpDigAlg pgpkey, pgpDigAlg pgpsig,
+                           uint8_t *hash, size_t hashlen, int hash_algo)
+{
+    int rc = 1; /* assume failure */
+    struct pgpDigSigECDSA_s *sig = pgpsig->data;
+    struct pgpDigKeyECDSA_s *key = pgpkey->data;
+    unsigned char *xsig = NULL;		/* signature encoded for X509 */
+    size_t xsig_len = 0;
+    EVP_PKEY_CTX *pkey_ctx = NULL;
+
+    if (!constructECDSASigningKey(key, pgpkey->curve))
+        goto done;
+
+    xsig = constructDSASignature(sig->r, sig->rlen, sig->s, sig->slen, &xsig_len);
+    if (!xsig)
+        goto done;
+
+    pkey_ctx = EVP_PKEY_CTX_new(key->evp_pkey, NULL);
+    if (!pkey_ctx)
+        goto done;
+
+    if (EVP_PKEY_verify_init(pkey_ctx) != 1)
+        goto done;
+
+    if (EVP_PKEY_verify(pkey_ctx, xsig, xsig_len, hash, hashlen) == 1)
+    {
+        /* Success */
+        rc = 0;
+    }
+
+done:
+    if (pkey_ctx)
+	EVP_PKEY_CTX_free(pkey_ctx);
+    free(xsig);
+    return rc;
+}
 
 /****************************** EDDSA ***************************************/
 
@@ -912,6 +1088,19 @@ static int pgpVerifyNULL(pgpDigAlg pgpkey, pgpDigAlg pgpsig,
     return 1;
 }
 
+static int pgpSupportedCurve(int algo, int curve)
+{
+#ifdef EVP_PKEY_ED25519
+    if (algo == PGPPUBKEYALGO_EDDSA && curve == PGPCURVE_ED25519)
+	return 1;
+#endif
+    if (algo == PGPPUBKEYALGO_ECDSA && curve == PGPCURVE_NIST_P_256)
+	return 1;
+    if (algo == PGPPUBKEYALGO_ECDSA && curve == PGPCURVE_NIST_P_384)
+	return 1;
+    return 0;
+}
+
 /****************************** PGP **************************************/
 pgpDigAlg pgpPubkeyNew(int algo, int curve)
 {
@@ -928,9 +1117,20 @@ pgpDigAlg pgpPubkeyNew(int algo, int curve)
         ka->free = pgpFreeKeyDSA;
         ka->mpis = 4;
         break;
+    case PGPPUBKEYALGO_ECDSA:
+	if (!pgpSupportedCurve(algo, curve)) {
+	    ka->setmpi = pgpSetMpiNULL;
+	    ka->mpis = -1;
+	    break;
+	}
+        ka->setmpi = pgpSetKeyMpiECDSA;
+        ka->free = pgpFreeKeyECDSA;
+        ka->mpis = 1;
+        ka->curve = curve;
+	break;
 #ifdef EVP_PKEY_ED25519
     case PGPPUBKEYALGO_EDDSA:
-	if (curve != PGPCURVE_ED25519) {
+	if (!pgpSupportedCurve(algo, curve)) {
 	    ka->setmpi = pgpSetMpiNULL;	/* unsupported curve */
 	    ka->mpis = -1;
 	    break;
@@ -969,6 +1169,12 @@ pgpDigAlg pgpSignatureNew(int algo)
         sa->verify = pgpVerifySigDSA;
         sa->mpis = 2;
         break;
+    case PGPPUBKEYALGO_ECDSA:
+        sa->setmpi = pgpSetSigMpiECDSA;
+        sa->free = pgpFreeSigECDSA;
+        sa->verify = pgpVerifySigECDSA;
+        sa->mpis = 2;
+        break;
 #ifdef EVP_PKEY_ED25519
     case PGPPUBKEYALGO_EDDSA:
         sa->setmpi = pgpSetSigMpiEDDSA;
-- 
2.23.0