summaryrefslogtreecommitdiff
path: root/crypto/akcipher-nettle.c.inc
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/akcipher-nettle.c.inc')
-rw-r--r--crypto/akcipher-nettle.c.inc272
1 files changed, 272 insertions, 0 deletions
diff --git a/crypto/akcipher-nettle.c.inc b/crypto/akcipher-nettle.c.inc
index 1d4bd6960e..a34845af5d 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,254 @@ 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;
+
+ if (ecdsa->type != QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC) {
+ error_setg(errp, "ECDSA verify requires a public key");
+ return ret;
+ }
+
+ /* 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 +707,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;
}