From f247f407de99378b38f760ae7590756ef3267b14 Mon Sep 17 00:00:00 2001 From: GimmeHardware <23285878+jcastle-gh@users.noreply.github.com> Date: Sat, 15 Mar 2025 03:22:36 -0400 Subject: [PATCH] Fix ed25519 key comparison in dkim_test_key dkim_test_key() compares a public DKIM key retrieved from DNS with the public key generated by i2d_PKEY_BIO() from a known private key. The output of i2d_PKEY_BIO is in SubjectPublicKey ASN encoded format. That works for RSA where the key in DNS is also in that format but for ed25519 the key in DNS is the DER encoded key by itself. The difference boils down to a 12-byte constant ASN prefix in the i2d_PKEY_BIO() output that is not in the DNS version. Fix it by verifying that the i2d_PKEY_BIO() output for ed25519 keys has the expected 12-byte prefix and then comparing what's left to the key from DNS. --- libopendkim/dkim-test.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/libopendkim/dkim-test.c b/libopendkim/dkim-test.c index 3985db83..dbb2acdd 100644 --- a/libopendkim/dkim-test.c +++ b/libopendkim/dkim-test.c @@ -301,6 +301,7 @@ dkim_test_key(DKIM_LIB *lib, char *selector, char *domain, BIO *outkey; #endif /* USE_GNUTLS */ void *ptr; + long pubkey_bio_bytes; struct dkim_crypto *crypto; char buf[BUFRSZ]; @@ -407,7 +408,6 @@ dkim_test_key(DKIM_LIB *lib, char *selector, char *domain, #endif /* USE_GNUTLS */ sig->sig_signature = (void *) crypto; - sig->sig_keytype = DKIM_KEYTYPE_RSA; #ifdef USE_GNUTLS if (err != NULL) @@ -465,15 +465,42 @@ dkim_test_key(DKIM_LIB *lib, char *selector, char *domain, (void) dkim_free(dkim); if (err != NULL) { - strlcpy(err, "i2d_RSA_PUBKEY_bio() failed", + strlcpy(err, "i2d_PUBKEY_bio() failed", errlen); } return -1; } - (void) BIO_get_mem_data(outkey, &ptr); + pubkey_bio_bytes = BIO_get_mem_data(outkey, &ptr); - if (BIO_number_written(outkey) == sig->sig_keylen) + /* The ed25519 pubkey in DNS doesn't have the 12-byte ASN */ + /* prefix that RSA keys in DNS and the i2d_PUBKEY output do. */ + /* Verify and skip over it for the comparison. */ + if(EVP_PKEY_id(crypto->crypto_pkey) == EVP_PKEY_ED25519) + { + const unsigned char asnhdr[12] = + { 0x30, 0x2a, 0x30, 0x05, + 0x06, 0x03, 0x2b, 0x65, + 0x70, 0x03, 0x21, 0x00 }; + if(pubkey_bio_bytes != 44 || + memcmp(ptr, asnhdr, sizeof(asnhdr))) + { + BIO_free(keybuf); + BIO_free(outkey); + (void) dkim_free(dkim); + if (err != NULL) + { + strlcpy(err, + "invalid Ed25519 PUBKEY conversion", + errlen); + } + return -1; + } + ptr += sizeof(asnhdr); + pubkey_bio_bytes -= sizeof(asnhdr); + } + + if (pubkey_bio_bytes == sig->sig_keylen) status = memcmp(ptr, sig->sig_key, sig->sig_keylen); else status = 1;