All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Cédric Le Goater" <clg@redhat.com>
To: qemu-arm@nongnu.org, qemu-devel@nongnu.org
Cc: "Jamin Lin" <jamin_lin@aspeedtech.com>,
	"Daniel P. Berrangé" <berrange@redhat.com>,
	"Cédric Le Goater" <clg@redhat.com>
Subject: [PULL 15/40] tests/crypto: Add ECDSA sign/verify tests
Date: Sun,  6 Sep 2026 19:09:56 +0200	[thread overview]
Message-ID: <20260906171021.26568-16-clg@redhat.com> (raw)
In-Reply-To: <20260906171021.26568-1-clg@redhat.com>

From: Jamin Lin <jamin_lin@aspeedtech.com>

Add unit tests for ECDSA prime256v1 and secp384r1, using the
RFC 6979 (Deterministic ECDSA) "sample" known-answer vectors. The same
key pair drives the verify test (against the RFC's known signature) and
the sign test, which signs the digest with the private key and checks
that the result verifies with the public key. Each test skips
(g_test_skip) when the crypto backend does not support ECDSA, via
qcrypto_akcipher_supports().

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260901085238.995968-5-jamin_lin@aspeedtech.com
Signed-off-by: Cédric Le Goater <clg@redhat.com>
---
 tests/unit/test-crypto-akcipher.c | 236 ++++++++++++++++++++++++++++++
 1 file changed, 236 insertions(+)

diff --git a/tests/unit/test-crypto-akcipher.c b/tests/unit/test-crypto-akcipher.c
index 53c2211ba8ea..fb1a6163e926 100644
--- a/tests/unit/test-crypto-akcipher.c
+++ b/tests/unit/test-crypto-akcipher.c
@@ -658,6 +658,117 @@ static const uint8_t rsa_public_key_extra_elem[] = {
     0x02, 0x06, 0xe1, 0x22, 0xdb, 0xe1, 0x22, 0xdb,
 };
 
+/*
+ * ECDSA known-answer vectors from RFC 6979 (Deterministic ECDSA), message
+ * "sample", decoded into raw big-endian form. The same key pair drives both
+ * the verify test (against the RFC's known signature) and the sign round-trip
+ * test:
+ *   - private key: the scalar d (curve size)
+ *   - public key: Qx || Qy (2 * curve size)
+ *   - dgst: the message digest (curve size)
+ *   - signature: r || s (2 * curve size)
+ *
+ * prime256v1 / SHA-256 (RFC 6979 A.2.5):
+ */
+static const uint8_t ecdsa_p256_privkey[32] = {
+    0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16,
+    0x6b, 0x5c, 0x21, 0x57, 0x67, 0xb1, 0xd6, 0x93,
+    0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12,
+    0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21,
+};
+
+static const uint8_t ecdsa_p256_pubkey[64] = {
+    0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31,
+    0xc9, 0x61, 0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68,
+    0xc0, 0x49, 0xb8, 0x92, 0x3b, 0x61, 0xfa, 0x6c,
+    0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6,
+    0x79, 0x03, 0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99,
+    0xa4, 0x1a, 0xe9, 0xe9, 0x56, 0x28, 0xbc, 0x64,
+    0xf2, 0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51,
+    0x77, 0xa3, 0xc2, 0x94, 0xd4, 0x46, 0x22, 0x99,
+};
+
+static const uint8_t ecdsa_p256_dgst[32] = {
+    0xaf, 0x2b, 0xdb, 0xe1, 0xaa, 0x9b, 0x6e, 0xc1,
+    0xe2, 0xad, 0xe1, 0xd6, 0x94, 0xf4, 0x1f, 0xc7,
+    0x1a, 0x83, 0x1d, 0x02, 0x68, 0xe9, 0x89, 0x15,
+    0x62, 0x11, 0x3d, 0x8a, 0x62, 0xad, 0xd1, 0xbf,
+};
+
+static const uint8_t ecdsa_p256_signature[64] = {
+    0xef, 0xd4, 0x8b, 0x2a, 0xac, 0xb6, 0xa8, 0xfd,
+    0x11, 0x40, 0xdd, 0x9c, 0xd4, 0x5e, 0x81, 0xd6,
+    0x9d, 0x2c, 0x87, 0x7b, 0x56, 0xaa, 0xf9, 0x91,
+    0xc3, 0x4d, 0x0e, 0xa8, 0x4e, 0xaf, 0x37, 0x16,
+    0xf7, 0xcb, 0x1c, 0x94, 0x2d, 0x65, 0x7c, 0x41,
+    0xd4, 0x36, 0xc7, 0xa1, 0xb6, 0xe2, 0x9f, 0x65,
+    0xf3, 0xe9, 0x00, 0xdb, 0xb9, 0xaf, 0xf4, 0x06,
+    0x4d, 0xc4, 0xab, 0x2f, 0x84, 0x3a, 0xcd, 0xa8,
+};
+
+/* secp384r1 / SHA-384 (RFC 6979 A.2.6): */
+static const uint8_t ecdsa_p384_privkey[48] = {
+    0x6b, 0x9d, 0x3d, 0xad, 0x2e, 0x1b, 0x8c, 0x1c,
+    0x05, 0xb1, 0x98, 0x75, 0xb6, 0x65, 0x9f, 0x4d,
+    0xe2, 0x3c, 0x3b, 0x66, 0x7b, 0xf2, 0x97, 0xba,
+    0x9a, 0xa4, 0x77, 0x40, 0x78, 0x71, 0x37, 0xd8,
+    0x96, 0xd5, 0x72, 0x4e, 0x4c, 0x70, 0xa8, 0x25,
+    0xf8, 0x72, 0xc9, 0xea, 0x60, 0xd2, 0xed, 0xf5,
+};
+
+static const uint8_t ecdsa_p384_pubkey[96] = {
+    0xec, 0x3a, 0x4e, 0x41, 0x5b, 0x4e, 0x19, 0xa4,
+    0x56, 0x86, 0x18, 0x02, 0x9f, 0x42, 0x7f, 0xa5,
+    0xda, 0x9a, 0x8b, 0xc4, 0xae, 0x92, 0xe0, 0x2e,
+    0x06, 0xaa, 0xe5, 0x28, 0x6b, 0x30, 0x0c, 0x64,
+    0xde, 0xf8, 0xf0, 0xea, 0x90, 0x55, 0x86, 0x60,
+    0x64, 0xa2, 0x54, 0x51, 0x54, 0x80, 0xbc, 0x13,
+    0x80, 0x15, 0xd9, 0xb7, 0x2d, 0x7d, 0x57, 0x24,
+    0x4e, 0xa8, 0xef, 0x9a, 0xc0, 0xc6, 0x21, 0x89,
+    0x67, 0x08, 0xa5, 0x93, 0x67, 0xf9, 0xdf, 0xb9,
+    0xf5, 0x4c, 0xa8, 0x4b, 0x3f, 0x1c, 0x9d, 0xb1,
+    0x28, 0x8b, 0x23, 0x1c, 0x3a, 0xe0, 0xd4, 0xfe,
+    0x73, 0x44, 0xfd, 0x25, 0x33, 0x26, 0x47, 0x20,
+};
+
+static const uint8_t ecdsa_p384_dgst[48] = {
+    0x9a, 0x90, 0x83, 0x50, 0x5b, 0xc9, 0x22, 0x76,
+    0xae, 0xc4, 0xbe, 0x31, 0x26, 0x96, 0xef, 0x7b,
+    0xf3, 0xbf, 0x60, 0x3f, 0x4b, 0xbd, 0x38, 0x11,
+    0x96, 0xa0, 0x29, 0xf3, 0x40, 0x58, 0x53, 0x12,
+    0x31, 0x3b, 0xca, 0x4a, 0x9b, 0x5b, 0x89, 0x0e,
+    0xfe, 0xe4, 0x2c, 0x77, 0xb1, 0xee, 0x25, 0xfe,
+};
+
+static const uint8_t ecdsa_p384_signature[96] = {
+    0x94, 0xed, 0xbb, 0x92, 0xa5, 0xec, 0xb8, 0xaa,
+    0xd4, 0x73, 0x6e, 0x56, 0xc6, 0x91, 0x91, 0x6b,
+    0x3f, 0x88, 0x14, 0x06, 0x66, 0xce, 0x9f, 0xa7,
+    0x3d, 0x64, 0xc4, 0xea, 0x95, 0xad, 0x13, 0x3c,
+    0x81, 0xa6, 0x48, 0x15, 0x2e, 0x44, 0xac, 0xf9,
+    0x6e, 0x36, 0xdd, 0x1e, 0x80, 0xfa, 0xbe, 0x46,
+    0x99, 0xef, 0x4a, 0xeb, 0x15, 0xf1, 0x78, 0xce,
+    0xa1, 0xfe, 0x40, 0xdb, 0x26, 0x03, 0x13, 0x8f,
+    0x13, 0x0e, 0x74, 0x0a, 0x19, 0x62, 0x45, 0x26,
+    0x20, 0x3b, 0x63, 0x51, 0xd0, 0xa3, 0xa9, 0x4f,
+    0xa3, 0x29, 0xc1, 0x45, 0x78, 0x6e, 0x67, 0x9e,
+    0x7b, 0x82, 0xc7, 0x1a, 0x38, 0x62, 0x8a, 0xc8,
+};
+
+typedef struct QCryptoECDSATestData QCryptoECDSATestData;
+struct QCryptoECDSATestData {
+    const char *path;
+    QCryptoCurveID curve_id;
+    const uint8_t *priv_key;
+    size_t priv_key_len;
+    const uint8_t *pub_key;
+    size_t pub_key_len;
+    const uint8_t *dgst;
+    size_t dlen;
+    const uint8_t *signature;
+    size_t slen;
+};
+
 typedef struct QCryptoRSAKeyTestData QCryptoRSAKeyTestData;
 struct QCryptoRSAKeyTestData {
     const char *path;
@@ -969,6 +1080,120 @@ static void test_rsakey(const void *opaque)
     g_assert(qcrypto_akcipher_max_dgst_len(key) == data->exp_key_len);
 }
 
+static QCryptoECDSATestData ecdsa_test_data[] = {
+    {
+        .path = "/crypto/akcipher/ecdsa-prime256v1",
+        .curve_id = QCRYPTO_CURVE_ID_PRIME256V1,
+        .priv_key = ecdsa_p256_privkey,
+        .priv_key_len = sizeof(ecdsa_p256_privkey),
+        .pub_key = ecdsa_p256_pubkey,
+        .pub_key_len = sizeof(ecdsa_p256_pubkey),
+        .dgst = ecdsa_p256_dgst,
+        .dlen = sizeof(ecdsa_p256_dgst),
+        .signature = ecdsa_p256_signature,
+        .slen = sizeof(ecdsa_p256_signature),
+    },
+    {
+        .path = "/crypto/akcipher/ecdsa-secp384r1",
+        .curve_id = QCRYPTO_CURVE_ID_SECP384R1,
+        .priv_key = ecdsa_p384_privkey,
+        .priv_key_len = sizeof(ecdsa_p384_privkey),
+        .pub_key = ecdsa_p384_pubkey,
+        .pub_key_len = sizeof(ecdsa_p384_pubkey),
+        .dgst = ecdsa_p384_dgst,
+        .dlen = sizeof(ecdsa_p384_dgst),
+        .signature = ecdsa_p384_signature,
+        .slen = sizeof(ecdsa_p384_signature),
+    },
+    /* Add more curves here as they gain backend support. */
+};
+
+static void test_ecdsa_verify(const void *opaque)
+{
+    const QCryptoECDSATestData *data = opaque;
+    QCryptoAkCipherOptions opt = {
+        .alg = QCRYPTO_AK_CIPHER_ALGO_ECDSA,
+        .u.ecdsa = {
+            .curve_id = data->curve_id,
+        },
+    };
+    g_autoptr(QCryptoAkCipher) pub_key = NULL;
+    g_autofree uint8_t *signature = NULL;
+    g_autofree uint8_t *dgst = NULL;
+
+    if (!qcrypto_akcipher_supports(&opt)) {
+        g_test_skip("ECDSA is not supported by the crypto backend");
+        return;
+    }
+
+    pub_key = qcrypto_akcipher_new(&opt, QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC,
+                                   data->pub_key, data->pub_key_len,
+                                   &error_abort);
+    g_assert(pub_key != NULL);
+    g_assert(qcrypto_akcipher_max_signature_len(pub_key) == data->slen);
+    g_assert(qcrypto_akcipher_max_dgst_len(pub_key) == data->dlen);
+
+    /* A valid signature must verify. */
+    g_assert(qcrypto_akcipher_verify(pub_key, data->signature, data->slen,
+                                     data->dgst, data->dlen,
+                                     &error_abort) == 0);
+
+    /* A tampered signature must fail (error is expected, so ignore it). */
+    signature = g_memdup2(data->signature, data->slen);
+    signature[0]++;
+    g_assert(qcrypto_akcipher_verify(pub_key, signature, data->slen,
+                                     data->dgst, data->dlen, NULL) != 0);
+
+    /* A tampered digest must also fail. */
+    dgst = g_memdup2(data->dgst, data->dlen);
+    dgst[0]++;
+    g_assert(qcrypto_akcipher_verify(pub_key, data->signature, data->slen,
+                                     dgst, data->dlen, NULL) != 0);
+}
+
+static void test_ecdsa_sign(const void *opaque)
+{
+    const QCryptoECDSATestData *data = opaque;
+    QCryptoAkCipherOptions opt = {
+        .alg = QCRYPTO_AK_CIPHER_ALGO_ECDSA,
+        .u.ecdsa = {
+            .curve_id = data->curve_id,
+        },
+    };
+    g_autoptr(QCryptoAkCipher) priv_key = NULL;
+    g_autoptr(QCryptoAkCipher) pub_key = NULL;
+    g_autofree uint8_t *signature = NULL;
+    int slen;
+
+    if (!qcrypto_akcipher_supports(&opt)) {
+        g_test_skip("ECDSA is not supported by the crypto backend");
+        return;
+    }
+
+    priv_key = qcrypto_akcipher_new(&opt, QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE,
+                                    data->priv_key, data->priv_key_len,
+                                    &error_abort);
+    g_assert(priv_key != NULL);
+
+    /*
+     * Sign the digest.
+     * ECDSA signatures are randomized so only length is fixed.
+     */
+    slen = qcrypto_akcipher_max_signature_len(priv_key);
+    signature = g_new0(uint8_t, slen);
+    g_assert(qcrypto_akcipher_sign(priv_key, data->dgst, data->dlen,
+                                   signature, slen, &error_abort) == slen);
+
+    /* The freshly produced signature must verify with the public key. */
+    pub_key = qcrypto_akcipher_new(&opt, QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC,
+                                   data->pub_key, data->pub_key_len,
+                                   &error_abort);
+    g_assert(pub_key != NULL);
+    g_assert(qcrypto_akcipher_verify(pub_key, signature, slen,
+                                     data->dgst, data->dlen,
+                                     &error_abort) == 0);
+}
+
 int main(int argc, char **argv)
 {
     size_t i;
@@ -985,6 +1210,17 @@ int main(int argc, char **argv)
                              &rsakey_test_data[i],
                              test_rsakey);
     }
+    for (i = 0; i < G_N_ELEMENTS(ecdsa_test_data); i++) {
+        g_autofree char *verify_path =
+            g_strdup_printf("%s-verify", ecdsa_test_data[i].path);
+        g_autofree char *sign_path =
+            g_strdup_printf("%s-sign", ecdsa_test_data[i].path);
+
+        g_test_add_data_func(verify_path, &ecdsa_test_data[i],
+                             test_ecdsa_verify);
+        g_test_add_data_func(sign_path, &ecdsa_test_data[i],
+                             test_ecdsa_sign);
+    }
 
     return g_test_run();
 }
-- 
2.55.0



  parent reply	other threads:[~2026-09-06 17:12 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-06 17:09 [PULL 00/40] aspeed queue Cédric Le Goater
2026-09-06 17:09 ` [PULL 01/40] hw/misc/aspeed_scu: Set both AST2600 protection key registers on reset Cédric Le Goater
2026-09-06 17:09 ` [PULL 02/40] hw/i2c/aspeed_i2c: Latch received bytes for SMBus block reads Cédric Le Goater
2026-09-06 17:09 ` [PULL 03/40] hw/usb/hcd-ehci: Check for DMA errors in get_dwords()/put_dwords() Cédric Le Goater
2026-09-06 17:09 ` [PULL 04/40] hw/usb/hcd-ehci: Make get_dwords() return bool Cédric Le Goater
2026-09-06 17:09 ` [PULL 05/40] hw/usb/hcd-ehci: Handle get_dwords() failures in async writeback Cédric Le Goater
2026-09-06 17:09 ` [PULL 06/40] hw/misc/aspeed_scu: Support the second random number generator Cédric Le Goater
2026-09-06 17:09 ` [PULL 07/40] tests/qtest/aspeed-hace-utils: Replace g_printerr() with g_test_skip() Cédric Le Goater
2026-09-06 17:09 ` [PULL 08/40] hw/misc/aspeed_acry: Add ASPEED ACRY model Cédric Le Goater
2026-09-06 17:09 ` [PULL 09/40] hw/arm/aspeed_ast2600: Introduce the ACRY SRAM Cédric Le Goater
2026-09-06 17:09 ` [PULL 10/40] hw/arm/aspeed_ast2600: Wire up the ACRY model Cédric Le Goater
2026-09-06 17:09 ` [PULL 11/40] tests/qtest/aspeed-acry-test: Add RSA ModExp tests Cédric Le Goater
2026-09-06 17:09 ` [PULL 12/40] qapi/crypto: Add ECDSA algorithm and curve id Cédric Le Goater
2026-09-06 17:09 ` [PULL 13/40] crypto/akcipher: Support ECDSA sign/verify with gcrypt Cédric Le Goater
2026-09-06 17:09 ` [PULL 14/40] crypto/akcipher: Support ECDSA sign/verify with nettle Cédric Le Goater
2026-09-06 17:09 ` Cédric Le Goater [this message]
2026-09-06 17:09 ` [PULL 16/40] hw/arm/aspeed_ast10x0: Remove obsolete unimplemented SBC mapping Cédric Le Goater
2026-09-06 17:09 ` [PULL 17/40] hw/misc/aspeed_sbc: Increase register space to 0x1000 Cédric Le Goater
2026-09-06 17:09 ` [PULL 18/40] hw/arm/aspeed_ast10x0: Wire SEC SRAM to the SBC model Cédric Le Goater
2026-09-06 17:10 ` [PULL 19/40] hw/misc/aspeed_sbc: Support the ECDSA verify command Cédric Le Goater
2026-09-06 17:10 ` [PULL 20/40] tests/qtest: Add ASPEED SBC ECDSA engine test Cédric Le Goater
2026-09-06 17:10 ` [PULL 21/40] hw/usb/aspeed-udc: Add ASPEED UDC device controller Cédric Le Goater
2026-09-06 17:10 ` [PULL 22/40] hw/usb/aspeed-udc: Add ASPEED UDC gadget USB device Cédric Le Goater
2026-09-06 17:10 ` [PULL 23/40] hw/usb/aspeed-udc: Add programmable endpoint DMA transfers Cédric Le Goater
2026-09-06 17:10 ` [PULL 24/40] hw/arm/aspeed_ast2600: Wire up the UDC Cédric Le Goater
2026-09-06 17:10 ` [PULL 25/40] hw/sensor: tmp105: make device state private to the implementation Cédric Le Goater
2026-09-06 17:10 ` [PULL 26/40] hw/sensor: tmp105: name the parent object field parent_obj Cédric Le Goater
2026-09-06 17:10 ` [PULL 27/40] hw/sensor: tmp105: implement Resettable reset Cédric Le Goater
2026-09-06 17:10 ` [PULL 28/40] hw/sensor: tmp105: enforce the configurable fault queue Cédric Le Goater
2026-09-06 17:10 ` [PULL 29/40] hw/sensor: tmp105: describe the temperature property Cédric Le Goater
2026-09-06 17:10 ` [PULL 30/40] hw/arm: aspeed: guard board-local temperature-sensor aliases Cédric Le Goater
2026-09-06 17:10 ` [PULL 31/40] hw/sensor: tmp105: add TMP75, TMP175 and LM75B variants Cédric Le Goater
2026-09-06 17:10 ` [PULL 32/40] tests/qtest: tmp105: cover the ALERT fault queue Cédric Le Goater
2026-09-06 17:10 ` [PULL 33/40] tests/qtest: tmp105: cover the TMP75, TMP175 and LM75B variants Cédric Le Goater
2026-09-06 17:10 ` [PULL 34/40] tests/qtest: tmp105: cover one-shot and fault-queue write immunity Cédric Le Goater
2026-09-06 17:10 ` [PULL 35/40] tests/qtest: tmp105: cover shutdown clearing the ALERT across variants Cédric Le Goater
2026-09-06 17:10 ` [PULL 36/40] hw/arm: sanmiguel: add Facebook SanMiguel BMC machine Cédric Le Goater
2026-09-06 17:10 ` [PULL 37/40] hw/arm: sanmiguel: populate EEPROM data Cédric Le Goater
2026-09-06 17:10 ` [PULL 38/40] hw/arm: catalina: use the real TMP75 model Cédric Le Goater
2026-09-06 17:10 ` [PULL 39/40] hw/arm: fuji: use the real TMP75 and LM75B temperature sensors Cédric Le Goater
2026-09-06 17:10 ` [PULL 40/40] tests/functional: aspeed: optionally check the device tree model on boot Cédric Le Goater
2026-09-07 16:35 ` [PULL 00/40] aspeed queue Peter Maydell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260906171021.26568-16-clg@redhat.com \
    --to=clg@redhat.com \
    --cc=berrange@redhat.com \
    --cc=jamin_lin@aspeedtech.com \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.