Implement ECDSA signing and verification for the nettle/hogweed backend,
for the prime256v1 (NIST P-256) and secp384r1 (NIST P-384) curves.
A public key (Qx || Qy) is loaded into an ecc_point for verify;
a private key (the scalar d) into an ecc_scalar for sign,
using nettle's ecdsa_sign() / ecdsa_verify().

The encrypt/decrypt driver ops return an error.

Signed-off-by: Jamin Lin <[email protected]>
---
 crypto/akcipher-nettle.c.inc | 267 +++++++++++++++++++++++++++++++++++
 1 file changed, 267 insertions(+)

diff --git a/crypto/akcipher-nettle.c.inc b/crypto/akcipher-nettle.c.inc
index 1d4bd6960e..34bdc3de27 100644
--- a/crypto/akcipher-nettle.c.inc
+++ b/crypto/akcipher-nettle.c.inc
@@ -20,6 +20,10 @@
  */
 
 #include <nettle/rsa.h>
+#include <nettle/ecdsa.h>
+#include <nettle/ecc-curve.h>
+#include <nettle/ecc.h>
+#include <nettle/bignum.h>
 
 #include "qemu/osdep.h"
 #include "qemu/host-utils.h"
@@ -55,6 +59,12 @@ static QCryptoAkCipher *qcrypto_nettle_rsa_new(
     const uint8_t *key,  size_t keylen,
     Error **errp);
 
+static QCryptoAkCipher *qcrypto_nettle_ecdsa_new(
+    const QCryptoAkCipherOptionsECDSA *opt,
+    QCryptoAkCipherKeyType type,
+    const uint8_t *key,  size_t keylen,
+    Error **errp);
+
 QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
                                       QCryptoAkCipherKeyType type,
                                       const uint8_t *key, size_t keylen,
@@ -64,6 +74,10 @@ QCryptoAkCipher *qcrypto_akcipher_new(const 
QCryptoAkCipherOptions *opts,
     case QCRYPTO_AK_CIPHER_ALGO_RSA:
         return qcrypto_nettle_rsa_new(&opts->u.rsa, type, key, keylen, errp);
 
+    case QCRYPTO_AK_CIPHER_ALGO_ECDSA:
+        return qcrypto_nettle_ecdsa_new(&opts->u.ecdsa, type, key, keylen,
+                                        errp);
+
     default:
         error_setg(errp, "Unsupported algorithm: %u", opts->alg);
         return NULL;
@@ -422,6 +436,249 @@ error:
 }
 
 
+/*
+ * ECDSA support (sign and verify)
+ *
+ * Keys and signatures use raw big-endian formats:
+ *   - public key: Qx || Qy, each 'coord_len' bytes
+ *   - private key: the scalar d, 'coord_len' bytes
+ *   - signature: r || s, each 'coord_len' bytes
+ *   - the input to sign/verify is a raw message digest
+ */
+typedef struct QCryptoNettleECDSA {
+    QCryptoAkCipher akcipher;
+    QCryptoAkCipherKeyType type;
+    struct ecc_point pub;
+    struct ecc_scalar priv;
+    QCryptoCurveID curve_id;
+} QCryptoNettleECDSA;
+
+static const struct ecc_curve *qcrypto_nettle_ecdsa_curve(
+    QCryptoCurveID curve_id)
+{
+    switch (curve_id) {
+    case QCRYPTO_CURVE_ID_PRIME256V1:
+        return nettle_get_secp_256r1();
+
+    case QCRYPTO_CURVE_ID_SECP384R1:
+        return nettle_get_secp_384r1();
+
+    default:
+        return NULL;
+    }
+}
+
+static size_t qcrypto_nettle_ecdsa_coord_len(QCryptoCurveID curve_id)
+{
+    switch (curve_id) {
+    case QCRYPTO_CURVE_ID_PRIME256V1:
+        return 32;
+
+    case QCRYPTO_CURVE_ID_SECP384R1:
+        return 48;
+
+    default:
+        return 0;
+    }
+}
+
+static void qcrypto_nettle_ecdsa_free(QCryptoAkCipher *akcipher)
+{
+    QCryptoNettleECDSA *ecdsa = (QCryptoNettleECDSA *)akcipher;
+    if (!ecdsa) {
+        return;
+    }
+
+    if (ecdsa->type == QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE) {
+        ecc_scalar_clear(&ecdsa->priv);
+    } else {
+        ecc_point_clear(&ecdsa->pub);
+    }
+    g_free(ecdsa);
+}
+
+static int qcrypto_nettle_ecdsa_encrypt(QCryptoAkCipher *akcipher,
+                                        const void *in, size_t in_len,
+                                        void *out, size_t out_len,
+                                        Error **errp)
+{
+    error_setg(errp, "ECDSA does not support encryption");
+    return -1;
+}
+
+static int qcrypto_nettle_ecdsa_decrypt(QCryptoAkCipher *akcipher,
+                                        const void *in, size_t in_len,
+                                        void *out, size_t out_len,
+                                        Error **errp)
+{
+    error_setg(errp, "ECDSA does not support decryption");
+    return -1;
+}
+
+static int qcrypto_nettle_ecdsa_sign(QCryptoAkCipher *akcipher,
+                                     const void *in, size_t in_len,
+                                     void *out, size_t out_len,
+                                     Error **errp)
+{
+    QCryptoNettleECDSA *ecdsa = (QCryptoNettleECDSA *)akcipher;
+    size_t coord_len = qcrypto_nettle_ecdsa_coord_len(ecdsa->curve_id);
+    struct dsa_signature sig;
+
+    if (ecdsa->type != QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE) {
+        error_setg(errp, "ECDSA sign requires a private key");
+        return -1;
+    }
+
+    if (in_len == 0 || in_len > akcipher->max_dgst_len) {
+        error_setg(errp, "Invalid digest length %zu", in_len);
+        return -1;
+    }
+
+    if (out_len < coord_len * 2) {
+        error_setg(errp, "Signature buffer length %zu is less than %zu",
+                   out_len, coord_len * 2);
+        return -1;
+    }
+
+    dsa_signature_init(&sig);
+    ecdsa_sign(&ecdsa->priv, NULL, wrap_nettle_random_func, in_len, in, &sig);
+
+    /* output is r || s, each zero-padded to the curve size */
+    nettle_mpz_get_str_256(coord_len, out, sig.r);
+    nettle_mpz_get_str_256(coord_len, (uint8_t *)out + coord_len, sig.s);
+
+    dsa_signature_clear(&sig);
+    return coord_len * 2;
+}
+
+static int qcrypto_nettle_ecdsa_verify(QCryptoAkCipher *akcipher,
+                                       const void *in, size_t in_len,
+                                       const void *in2, size_t in2_len,
+                                       Error **errp)
+{
+    QCryptoNettleECDSA *ecdsa = (QCryptoNettleECDSA *)akcipher;
+    size_t coord_len = qcrypto_nettle_ecdsa_coord_len(ecdsa->curve_id);
+    struct dsa_signature sig;
+    int ret = -1;
+
+    /* signature is r || s */
+    if (in_len != coord_len * 2) {
+        error_setg(errp, "Signature length %zu is not %zu",
+                   in_len, coord_len * 2);
+        return ret;
+    }
+
+    if (in2_len == 0 || in2_len > akcipher->max_dgst_len) {
+        error_setg(errp, "Invalid digest length %zu", in2_len);
+        return ret;
+    }
+
+    dsa_signature_init(&sig);
+    nettle_mpz_set_str_256_u(sig.r, coord_len, in);
+    nettle_mpz_set_str_256_u(sig.s, coord_len, (const uint8_t *)in + 
coord_len);
+
+    if (ecdsa_verify(&ecdsa->pub, in2_len, in2, &sig) == 1) {
+        ret = 0;
+    } else {
+        error_setg(errp, "Failed to verify signature");
+    }
+
+    dsa_signature_clear(&sig);
+    return ret;
+}
+
+QCryptoAkCipherDriver nettle_ecdsa = {
+    .encrypt = qcrypto_nettle_ecdsa_encrypt,
+    .decrypt = qcrypto_nettle_ecdsa_decrypt,
+    .sign = qcrypto_nettle_ecdsa_sign,
+    .verify = qcrypto_nettle_ecdsa_verify,
+    .free = qcrypto_nettle_ecdsa_free,
+};
+
+static QCryptoAkCipher *qcrypto_nettle_ecdsa_new(
+    const QCryptoAkCipherOptionsECDSA *opt,
+    QCryptoAkCipherKeyType type,
+    const uint8_t *key, size_t keylen,
+    Error **errp)
+{
+    QCryptoNettleECDSA *ecdsa;
+    const struct ecc_curve *curve = qcrypto_nettle_ecdsa_curve(opt->curve_id);
+    size_t coord_len = qcrypto_nettle_ecdsa_coord_len(opt->curve_id);
+    mpz_t x;
+    mpz_t y;
+
+    if (!curve || coord_len == 0) {
+        error_setg(errp, "Unsupported curve id: %u", opt->curve_id);
+        return NULL;
+    }
+
+    switch (type) {
+    case QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC:
+        if (keylen != coord_len * 2) {
+            error_setg(errp, "Public key length %zu is not %zu",
+                       keylen, coord_len * 2);
+            return NULL;
+        }
+        break;
+
+    case QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE:
+        if (keylen != coord_len) {
+            error_setg(errp, "Private key length %zu is not %zu",
+                       keylen, coord_len);
+            return NULL;
+        }
+        break;
+
+    default:
+        error_setg(errp, "Unknown akcipher key type %d", type);
+        return NULL;
+    }
+
+    ecdsa = g_new0(QCryptoNettleECDSA, 1);
+    ecdsa->akcipher.driver = &nettle_ecdsa;
+    ecdsa->type = type;
+    ecdsa->curve_id = opt->curve_id;
+    ecdsa->akcipher.max_dgst_len = coord_len;
+    ecdsa->akcipher.max_signature_len = coord_len * 2;
+
+    if (type == QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC) {
+        /* public key: Qx || Qy */
+        ecc_point_init(&ecdsa->pub, curve);
+        mpz_init(x);
+        mpz_init(y);
+        nettle_mpz_set_str_256_u(x, coord_len, key);
+        nettle_mpz_set_str_256_u(y, coord_len,
+                                 (const uint8_t *)key + coord_len);
+
+        if (!ecc_point_set(&ecdsa->pub, x, y)) {
+            error_setg(errp, "Invalid ECDSA public key (not on curve)");
+            mpz_clear(x);
+            mpz_clear(y);
+            qcrypto_nettle_ecdsa_free((QCryptoAkCipher *)ecdsa);
+            return NULL;
+        }
+
+        mpz_clear(x);
+        mpz_clear(y);
+    } else {
+        /* private key: the scalar d */
+        ecc_scalar_init(&ecdsa->priv, curve);
+        mpz_init(x);
+        nettle_mpz_set_str_256_u(x, coord_len, key);
+
+        if (!ecc_scalar_set(&ecdsa->priv, x)) {
+            error_setg(errp, "Invalid ECDSA private key");
+            mpz_clear(x);
+            qcrypto_nettle_ecdsa_free((QCryptoAkCipher *)ecdsa);
+            return NULL;
+        }
+
+        mpz_clear(x);
+    }
+
+    return (QCryptoAkCipher *)ecdsa;
+}
+
 bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
 {
     switch (opts->alg) {
@@ -445,6 +702,16 @@ bool qcrypto_akcipher_supports(QCryptoAkCipherOptions 
*opts)
         }
         break;
 
+    case QCRYPTO_AK_CIPHER_ALGO_ECDSA:
+        switch (opts->u.ecdsa.curve_id) {
+        case QCRYPTO_CURVE_ID_PRIME256V1:
+        case QCRYPTO_CURVE_ID_SECP384R1:
+            return true;
+
+        default:
+            return false;
+        }
+
     default:
         return false;
     }
-- 
2.53.0

Reply via email to