On Fri, Oct 25, 2024 at 08:27:21PM +0200, Matus UHLAR - fantomas via mailop 
wrote:

> you have spaces in your TXT recors which I believe makes it invalid:
> 
> default._domainkey.valar.uk.net. 300 IN TXT     "v=DKIM1; k=rsa; 
> p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr51Z83Plk5XDJOCp8wk7 
> +yHeXUGfim4y8HoQbfCN5IPns+kM1k+UeG7tYA9+VeFrzDf+3m7kD2AMfHERhmC9 
> jTF6hwW1e6xhzhxxn/WgEzH/5/pdgiVsZt5hl94IIY5pCPxCRqeorv9k+gKQeLSj 
> 8qyKfUCIHB60ulQIHunfUQDgA3igB6ecvb8u+HTF9n" "ydkfyMIe6FXPBXiTTKVBuk 
> XA2EBg0qdalzoLuLTWUeifDTQRtNqTJNiIaE4EhV/FnhWhryaaqsEVSfIoI+wYV1 
> q9QvD5JVcK2q5K49VooFwgV9I3WV6J8E+jNBtT6fdr86TJ5Vb11GiXMKlkV+W5/O VQIDAQAB"

> Try fixing this first.

Per RFC6376 folding whitespace is allowed in the base64string construct:

    https://datatracker.ietf.org/doc/html/rfc6376#section-2.10

      base64string    =  ALPHADIGITPS *([FWS] ALPHADIGITPS)
                         [ [FWS] "=" [ [FWS] "=" ] ]

    https://datatracker.ietf.org/doc/html/rfc6376#section-3.6.1

      key-p-tag    = %x70 [FWS] "=" [ [FWS] base64string]

         INFORMATIVE NOTE: A base64string is permitted to include
         whitespace (FWS) at arbitrary places; however, any CRLFs must
         be followed by at least one WSP character.  Implementers and
         administrators are cautioned to ensure that selector TXT RRs
         conform to this specification.

Compliant implementations decoding the base64string must ignore
whitespace.  In this case there is whitespace present, internal to the
two "character string" fragments of the TXT record.  Isolating the
"base64string" after joining the two fragments yields:

    $ dig +short -t txt default._domainkey.valar.uk.net |
        perl -lpe '
            # Drop first/last quotes
            s/\A"|"\z//g;
            # Unescape and drop internal close/open quote pairs
            s{([^\\"]+)|\\\d{3}|\\(.)|" "}{"$1$2".($3?chr(int($3)):"")}eg;
            # Isolate the public key
            s/.*?;\s+p\s*=\s*//; s/;.*//
            '
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr51Z83Plk5XDJOCp8wk7 
+yHeXUGfim4y8HoQbfCN5IPns+kM1k+UeG7tYA9+VeFrzDf+3m7kD2AMfHERhmC9 
jTF6hwW1e6xhzhxxn/WgEzH/5/pdgiVsZt5hl94IIY5pCPxCRqeorv9k+gKQeLSj 
8qyKfUCIHB60ulQIHunfUQDgA3igB6ecvb8u+HTF9nydkfyMIe6FXPBXiTTKVBuk 
XA2EBg0qdalzoLuLTWUeifDTQRtNqTJNiIaE4EhV/FnhWhryaaqsEVSfIoI+wYV1 
q9QvD5JVcK2q5K49VooFwgV9I3WV6J8E+jNBtT6fdr86TJ5Vb11GiXMKlkV+W5/O VQIDAQAB

Which is valid, though perhaps prone to tickling bugs in non-compliant
implementations.  The key is valid.  Running it through:

    tr -d ' ' | openssl base64 -A -d | openssl pkey -pubin -inform DER -text

yields:

    -----BEGIN PUBLIC KEY-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr51Z83Plk5XDJOCp8wk7
    +yHeXUGfim4y8HoQbfCN5IPns+kM1k+UeG7tYA9+VeFrzDf+3m7kD2AMfHERhmC9
    jTF6hwW1e6xhzhxxn/WgEzH/5/pdgiVsZt5hl94IIY5pCPxCRqeorv9k+gKQeLSj
    8qyKfUCIHB60ulQIHunfUQDgA3igB6ecvb8u+HTF9nydkfyMIe6FXPBXiTTKVBuk
    XA2EBg0qdalzoLuLTWUeifDTQRtNqTJNiIaE4EhV/FnhWhryaaqsEVSfIoI+wYV1
    q9QvD5JVcK2q5K49VooFwgV9I3WV6J8E+jNBtT6fdr86TJ5Vb11GiXMKlkV+W5/O
    VQIDAQAB
    -----END PUBLIC KEY-----
    Public-Key: (2048 bit)
    Modulus:
        00:af:9d:59:f3:73:e5:93:95:c3:24:e0:a9:f3:09:
        3b:fb:21:de:5d:41:9f:8a:6e:32:f0:7a:10:6d:f0:
        8d:e4:83:e7:b3:e9:0c:d6:4f:94:78:6e:ed:60:0f:
        7e:55:e1:6b:cc:37:fe:de:6e:e4:0f:60:0c:7c:71:
        11:86:60:bd:8d:31:7a:87:05:b5:7b:ac:61:ce:1c:
        71:9f:f5:a0:13:31:ff:e7:fa:5d:82:25:6c:66:de:
        61:97:de:08:21:8e:69:08:fc:42:46:a7:a8:ae:ff:
        64:fa:02:90:78:b4:a3:f2:ac:8a:7d:40:88:1c:1e:
        b4:ba:54:08:1e:e9:df:51:00:e0:03:78:a0:07:a7:
        9c:bd:bf:2e:f8:74:c5:f6:7c:9d:91:fc:8c:21:ee:
        85:5c:f0:57:89:34:ca:54:1b:a4:5c:0d:84:06:0d:
        2a:75:a9:73:a0:bb:8b:4d:65:1e:89:f0:d3:41:1b:
        4d:a9:32:4d:88:86:84:e0:48:55:fc:59:e1:5a:1a:
        f2:69:aa:ac:11:54:9f:22:82:3e:c1:85:75:ab:d4:
        2f:0f:92:55:70:ad:aa:e4:ae:3d:56:8a:05:c2:05:
        7d:23:75:95:e8:9f:04:fa:33:41:b5:3e:9f:76:bf:
        3a:4c:9e:55:6f:5d:46:89:73:0a:96:45:7e:5b:9f:
        ce:55
    Exponent: 65537 (0x10001)

On Fri, Oct 25, 2024 at 05:46:37PM -0400, John Levine via mailop wrote:

> There are three spaces embedded in the part of the key in the first
> string in the DKIM record, and three more in the second string.  Check
> it yourself.  I can imagine how unhelpful line wrapping and copy/paste
> did that.
> 
> If he gets rid of those, the DKIM signatures will work a lot better.

Perhaps, but validator implementations that mind the spaces are
incorrect.

On Fri, Oct 25, 2024 at 06:05:08PM -0400, Bill Cole via mailop wrote:

> Indeed. I stand corrected.

Your larger point stands, even whitespace *inside* the text fragments
should not matter.  With recent improvements in OpenSSL's "Base64 BIO",
you don't need "tr -d ' '" to trim the internal whitespace:

    $ dig +short -t txt default._domainkey.valar.uk.net |
        perl -lpe '
            # Drop first/last quotes
            s/\A"|"\z//g;
            # Unescape and drop internal close/open quote pairs
            s{([^\\"]+)|\\\d{3}|\\(.)|" "}{"$1$2".($3?chr(int($3)):"")}eg;
            # Isolate the public key
            s/.*?;\s+p\s*=\s*//; s/;.*//
            ' |
        /opt/openssl/3.4/bin/openssl base64 -A -d |
        openssl asn1parse -i -inform DER
    0:d=0  hl=4 l= 290 cons: SEQUENCE          
    4:d=1  hl=2 l=  13 cons:  SEQUENCE          
    6:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
   17:d=2  hl=2 l=   0 prim:   NULL              
   19:d=1  hl=4 l= 271 prim:  BIT STRING        

At the API level, this corresponds to setting the BIO_FLAGS_BASE64_NO_NL
flag before reading the Base64 BIO.  Eliding return value checks, the
C code could be something along the lines of:

    EVP_PKEY *key;
    BIO *buf = BIO_new(BIO_s_mem())
    BIO *b64 = BIO_new(BIO_f_base64());

    BIO_set_mem_eof_return(buf, 0);
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
    BIO_push(b64, buf);

    /* DKIM RSA keys are in SPKI form, but ... */
    if (... DKIM algorithm is ed25519 ...) {
        /*
         * Ed25519 DKIM p= value is raw Ed25519 key, rather than an
         * X.509 SPKI.  Luckily, the ASN.1 SPKI prefix ( inclduing the
         * leading "0" of the BITSTRING, is exactly 12 bytes, so it can
         * be prepended without affecing the encoding of the key value.
         */
        BIO_write(buf, "MCowBQYDK2VwAyEA", 16);
    }

    /*
     * Prior to OpenSSL 3.4, the application needs to take care to not
     * write whitespace into the base64-encoded memory buffer.
     */
    BIO_write(buf, base64string, len);
    key = d2i_PUBKEY_bio(bio, NULL);
    BIO_free_all(b64);

    return key;

Future DKIM algorithms after "ed25519" should probably stick to SPKI
formats, multiple ad hoc key encodings add complexity.

-- 
    Viktor.
_______________________________________________
mailop mailing list
mailop@mailop.org
https://list.mailop.org/listinfo/mailop

Reply via email to