diff options
Diffstat (limited to 'tests/unit/test-crypto-akcipher.c')
| -rw-r--r-- | tests/unit/test-crypto-akcipher.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/tests/unit/test-crypto-akcipher.c b/tests/unit/test-crypto-akcipher.c index 53c2211ba8..fb1a6163e9 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(); } |
