From 321ef002e23ff171922075988bcd8e77bae884b7 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Fri, 13 Apr 2018 11:51:50 +0100 Subject: [PATCH] DKIM: add support for the SubjectPublicKeyInfo wrapped form of pubkey --- doc/doc-docbook/spec.xfpt | 5 ++++ doc/doc-txt/ChangeLog | 4 ++++ src/src/pdkim/pdkim.c | 27 +++++++++++++++++++++- test/dnszones-src/db.test.ex | 8 ++++++- test/log/4540 | 16 ++++++++----- test/scripts/4540-DKIM-Ed25519/4540 | 36 ++++++++++++++++++++++++++++- 6 files changed, 87 insertions(+), 9 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index d8f1573c9..a35a8bf26 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -39067,6 +39067,11 @@ To produce the required public key value for a DNS record: openssl pkey -outform DER -pubout -in dkim_ed25519.private | tail -c +13 | base64 certtool --load_privkey=dkim_ed25519.private --pubkey_info --outder | tail -c +13 | base64 .endd + +Note that the format +of Ed25519 keys in DNS has not yet been decided; this release supports +both of the leading candidates at this time, a future release will +probably drop support for whichever proposal loses .wen .option dkim_hash smtp string&!! sha256 diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 9fc466365..83a37d515 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -206,6 +206,10 @@ JH/37 Bug 2255: Revert the disable of the OpenSSL session caching. This PP/03 Add util/renew-opendmarc-tlds.sh script for safe renewal of public suffix list. +JH/38 DKIM: accept Ed25519 pubkeys in SubjectPublicKeyInfo-wrapped form, + since the IETF WG has not yet settled on that versus the original + "bare" representation. + Exim version 4.90 ----------------- diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 149cff86c..5688c78a8 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -80,7 +80,7 @@ const pdkim_hashtype pdkim_hashes[] = { const uschar * pdkim_keytypes[] = { [KEYTYPE_RSA] = US"rsa", #ifdef SIGN_HAVE_ED25519 - [KEYTYPE_ED25519] = US"ed25519", /* Works for 3.6.0 GnuTLS */ + [KEYTYPE_ED25519] = US"ed25519", /* Works for 3.6.0 GnuTLS, OpenSSL 1.1.1 */ #endif #ifdef notyet_EC_dkim_extensions /* https://tools.ietf.org/html/draft-srose-dkim-ecc-00 */ @@ -1336,6 +1336,28 @@ return string_from_gstring(hdr); /* -------------------------------------------------------------------------- */ +/* According to draft-ietf-dcrup-dkim-crypto-07 "keys are 256 bits" (referring +to DNS, hence the pubkey). Check for more than 32 bytes; if so assume the +alternate possible representation (still) being discussed: a +SubjectPublickeyInfo wrapped key - and drop all but the trailing 32-bytes (it +should be a DER, with exactly 12 leading bytes - but we could accept a BER also, +which could be any size). We still rely on the crypto library for checking for +undersize. + +When the RFC is published this should be re-addressed. */ + +static void +check_bare_ed25519_pubkey(pdkim_pubkey * p) +{ +int excess = p->key.len - 32; +if (excess > 0) + { + DEBUG(D_acl) debug_printf("PDKIM: unexpected pubkey len %lu\n", p->key.len); + p->key.data += excess; p->key.len = 32; + } +} + + static pdkim_pubkey * pdkim_key_from_dns(pdkim_ctx * ctx, pdkim_signature * sig, ev_ctx * vctx, const uschar ** errstr) @@ -1408,6 +1430,9 @@ if (sig->keytype < 0) } k_ok: +if (sig->keytype == KEYTYPE_ED25519) + check_bare_ed25519_pubkey(p); + if ((*errstr = exim_dkim_verify_init(&p->key, sig->keytype == KEYTYPE_ED25519 ? KEYFMT_ED25519_BARE : KEYFMT_DER, vctx))) diff --git a/test/dnszones-src/db.test.ex b/test/dnszones-src/db.test.ex index b8abd2845..492ee5df8 100644 --- a/test/dnszones-src/db.test.ex +++ b/test/dnszones-src/db.test.ex @@ -556,9 +556,15 @@ sel2._domainkey TXT "v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXRFf+VhT+ ; EC signing, using Ed25519 ; - needs GnuTLS 3.6.0 (fedora rawhide has that) ; certtool --generate-privkey --key-type=ed25519 --outfile=dkim_ed25519.private -; ../src/util/ed25519_privkey_pem_to_pubkey_raw_b64 dkim_ed25519.private +; certtool --load_privkey=dkim_ed25519.private --pubkey_info --outder | tail -c +13 | base64 sed._domainkey TXT "v=DKIM1; k=ed25519; p=sPs07Vu29FpHT/80UXUcYHFOHifD4o2ZlP2+XUh9g6E=" +; version of the above wrapped in SubjectPublicKeyInfo, in case the WG plumps in that direction +; certtool --load_privkey=aux-fixed/dkim/dkim_ed25519.private --pubkey_info +; (and grab the b64 content from between the pem headers) + +sedw._domainkey TXT "v=DKIM1; k=ed25519; p=MCowBQYDK2VwAyEAsPs07Vu29FpHT/80UXUcYHFOHifD4o2ZlP2+XUh9g6E=" + ; End diff --git a/test/log/4540 b/test/log/4540 index 58039465a..7d0c92bd7 100644 --- a/test/log/4540 +++ b/test/log/4540 @@ -5,9 +5,13 @@ 1999-03-02 09:44:33 10HmaX-0005vi-00 DKIM: d=test.ex s=sed c=relaxed/relaxed a=ed25519-sha256 b=512 [verification succeeded] 1999-03-02 09:44:33 10HmaX-0005vi-00 Authentication-Results: myhost.test.ex;\n dkim=pass header.d=test.ex header.s=sed header.a=ed25519-sha256 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss DKIM=test.ex id=E10HmaY-0005vi-00@myhost.test.ex -1999-03-02 09:44:33 10HmaZ-0005vi-00 signer: kitterman.org bits: 512 -1999-03-02 09:44:33 10HmaZ-0005vi-00 DKIM: d=kitterman.org s=ed25519 c=relaxed/simple a=ed25519-sha256 b=512 i=@kitterman.org t=1517847601 [verification succeeded] -1999-03-02 09:44:33 10HmaZ-0005vi-00 signer: @kitterman.org bits: 512 -1999-03-02 09:44:33 10HmaZ-0005vi-00 DKIM: d=kitterman.org s=ed25519 c=relaxed/simple a=ed25519-sha256 b=512 i=@kitterman.org t=1517847601 [verification succeeded] -1999-03-02 09:44:33 10HmaZ-0005vi-00 Authentication-Results: myhost.test.ex;\n dkim=pass header.d=kitterman.org header.i=@kitterman.org header.s=ed25519 header.a=ed25519-sha256 -1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss DKIM=kitterman.org id=example@example.com +1999-03-02 09:44:33 10HmaZ-0005vi-00 signer: test.ex bits: 512 +1999-03-02 09:44:33 10HmaZ-0005vi-00 DKIM: d=test.ex s=sedw c=relaxed/relaxed a=ed25519-sha256 b=512 [verification succeeded] +1999-03-02 09:44:33 10HmaZ-0005vi-00 Authentication-Results: myhost.test.ex;\n dkim=pass header.d=test.ex header.s=sedw header.a=ed25519-sha256 +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss DKIM=test.ex id=E10HmaY-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbA-0005vi-00 signer: kitterman.org bits: 512 +1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: d=kitterman.org s=ed25519 c=relaxed/simple a=ed25519-sha256 b=512 i=@kitterman.org t=1517847601 [verification succeeded] +1999-03-02 09:44:33 10HmbA-0005vi-00 signer: @kitterman.org bits: 512 +1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: d=kitterman.org s=ed25519 c=relaxed/simple a=ed25519-sha256 b=512 i=@kitterman.org t=1517847601 [verification succeeded] +1999-03-02 09:44:33 10HmbA-0005vi-00 Authentication-Results: myhost.test.ex;\n dkim=pass header.d=kitterman.org header.i=@kitterman.org header.s=ed25519 header.a=ed25519-sha256 +1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss DKIM=kitterman.org id=example@example.com diff --git a/test/scripts/4540-DKIM-Ed25519/4540 b/test/scripts/4540-DKIM-Ed25519/4540 index 0be08ea31..504676e7c 100644 --- a/test/scripts/4540-DKIM-Ed25519/4540 +++ b/test/scripts/4540-DKIM-Ed25519/4540 @@ -6,7 +6,7 @@ exim -DSERVER=server -bd -oX PORT_D # This should pass, only Mail::DKIM::Signer does not handle ed25519-sha256 yet # # Mail original (will be)in aux-fixed/4500.msg1.txt -# Sig generated by: perl aux-fixed/dkim/sign.pl --algorithm=ed255190sha256 \ +# Sig (would be) generated by: perl aux-fixed/dkim/sign.pl --algorithm=ed255190sha256 \ # --method=simple/simple < aux-fixed/4500.msg1.txt # # TODO - until we have that we can only test internal consistency, @@ -44,6 +44,40 @@ QUIT **** # # +# Duplicate of the above, but referencing a pubkey in "wrapped-in-SubjectPublicKeyInfo" +# format. Should pass also. +client 127.0.0.1 PORT_D +??? 220 +HELO xxx +??? 250 +MAIL FROM: +??? 250 +RCPT TO: +??? 250 +DATA +??? 354 +DKIM-Signature: v=1; a=ed25519-sha256; q=dns/txt; c=relaxed/relaxed; d=test.ex + ; s=sedw; h=From:To:Subject; bh=/Ab0giHZitYQbDhFszoqQRUkgqueaX9zatJttIU/plc=; + b=g0aVl5sI4fFLWDwXj9SnLgENXg2u8H8kKgK5/bXBZ7DKAImkm2+4tRzz1UOveu/Navis53Bg/C + 9nPxsspzb/Dg==; +Received: from jgh by myhost.test.ex with local (Exim x.yz) + envelope-from ) + 1dtXln-0000YP-Hb + a@test.ex; Sun, 17 Sep 2017 12:29:51 +0100 +From: nobody@example.com +Message-Id: +Sender: CALLER_NAME +Date: Sun, 17 Sep 2017 12:29:51 +0100 + +content +. +??? 250 +QUIT +??? 221 +**** +# +# + # This should pass, an independently-generated sample from Scott Kitterman. # I don't want to retain this longterm as it hits an external DNS record, # not under the testsuite. -- 2.30.2