parse_public_key: RSAPublicKey vs SubjectPublicKeyInfo

Bug #1851862 reported by Andreas Weigel
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
dkimpy
Fix Released
Low
Scott Kitterman

Bug Description

If I haven't missed any important update to the DKIM spec, I consider the following as valid:

RFC6376, Section 3.6.1:
  The "rsa" key type
  indicates that an ASN.1 DER-encoded [ITU-X660-1997] RSAPublicKey
  (see [RFC3447], Sections 3.1 and A.1.1) is being used in the "p="
  tag. (Note: the "p=" tag further encodes the value using the
  base64 algorithm.) Unrecognized key types MUST be ignored.

Errata exist (https://www.rfc-editor.org/errata/eid3017) that extend this to
  The "rsa" key type
  indicates that an ASN.1 DER-encoded [ITU-X660-1997] RSAPublicKey
  (see [RFC3447], Sections 3.1 and A.1.1), which MAY be contained in
  a SubjectPublicKeyInfo (see [RFC5280], Section A.1), is being used
  in the "p=" tag.

I'm aware that appearantly the whole world uses SubjectPublicKeyInfo in published DNS records and I also noticed that, e.g. libopendkim is in its current state is also not able to parse a RSAPublicKey which is not contained in a SubjectPublicKeyInfo from the 'p=' tag in a TXT record. But still, the RFC defines the RSAPublicKey-represenation definitely as valid and therefore I think it would be somehow correct to support it.

Fortunately, dkimpy does the ASN1-parsing all by itself, so a patch to support parsing of RSAPublicKey Syntax directly could be as trivial as:

diff --git a/dkim/crypto.py b/dkim/crypto.py
index 144bbde..f2c7e89 100644
--- a/dkim/crypto.py
+++ b/dkim/crypto.py
@@ -119,7 +119,10 @@ def parse_public_key(data):
         x = asn1_parse(ASN1_Object, data)
         pkd = asn1_parse(ASN1_RSAPublicKey, x[0][1][1:])
     except ASN1FormatError as e:
- raise UnparsableKeyError('Unparsable public key: ' + str(e))
+ try:
+ pkd = asn1_parse(ASN1_RSAPublicKey, data)
+ except:
+ raise UnparsableKeyError('Unparsable public key: ' + str(e))
     pk = {
         'modulus': pkd[0][0],
         'publicExponent': pkd[0][1],

I successfully tested this patch agains a mixture of both key represenations.

Revision history for this message
Andreas Weigel (andwei) wrote :

I should probably have added, that dkimpy fails to parse a non-nested RSAPublicKey-Structure like this:

ERROR:dkimverify:could not parse public key (b'MIIBCgKCAQEAuHeX9e8a+ZnkCuv45Zuli5D+8k69i1HK0KhyVo7j+ZBXIhrGO0e5pf62ZJ4ly7OL99oSiAt5riAfbxHigi59CWy8Nqu0Dhul7anAEkiEuKL8eKsjkgBfYiQzrCxuNh/+VG5B6G8D8xnKFPrvdt8m0D8ek/HZCgntkpkicBWe2Eeq3WZb+E9/m8IdA+rY+XZBkOLF8kro8lSZANLaNstddWj58LjTk949YfkJHQtekUJYV6t4SzsQ2Ox3gMSZE//ZP4S3trJhDocUNQdiJDY9Rc4bwbrI4vAn2opRZDIj0Ms0cK3L+9+1969K5jOeR5m0Gl6pC1WQy9xLKDsUISEYaQIDAQAB'): Unparsable public key: Unexpected tag (got 02, expecting 30)

Revision history for this message
Andreas Weigel (andwei) wrote :
Revision history for this message
Scott Kitterman (kitterman) wrote :

Thanks for the report and the patch.

If I read this correctly, it seems like approximately everyone just follows the example in the appendix and it's not clear how the erratum gets resolved the next time the spec is updated. Its status is Held for Document Update, which means:

"The erratum is not a necessary update to the RFC. However, any future update of the document might consider this erratum, and determine whether it is correct and merits including in the update.”

There's no guarantee that the proposed change is the one that would be adopted. It might be that the "which MAY be contained in" part of the change is a SHOULD or a MUST. Given the preponderance of that mode of representation I think it would almost certainly be at least a SHOULD.

That said, it seems trivial to support, so there's no harm. The patch looks fine, but I want to make sure we have test coverage for this. If you would please provde (as attachments) a sample private key and the related public key in both representations, then I'd have what I need to add it to the test suite.

Changed in dkimpy:
importance: Undecided → Low
milestone: none → future
status: New → Incomplete
Revision history for this message
Andreas Weigel (andwei) wrote :
Revision history for this message
Andreas Weigel (andwei) wrote :
Revision history for this message
Andreas Weigel (andwei) wrote :
Revision history for this message
Andreas Weigel (andwei) wrote :
Revision history for this message
Andreas Weigel (andwei) wrote :
Revision history for this message
Andreas Weigel (andwei) wrote :
Revision history for this message
Andreas Weigel (andwei) wrote :
Revision history for this message
Andreas Weigel (andwei) wrote :
Revision history for this message
Andreas Weigel (andwei) wrote :
Revision history for this message
Andreas Weigel (andwei) wrote :
Revision history for this message
Andreas Weigel (andwei) wrote :

There you go. Thank you for the instant reply. Sorry for the spam, I could not find a way to add multiple attachments with one post :-/ I hope everything is self-explaining.

With regard to the RFC, yep, it somehow looks like the example actually defined the format, even though it actually contradicts the definition of the 'k=' and 'p=' tag.

Revision history for this message
Scott Kitterman (kitterman) wrote :

Thanks. That should do it.

Changed in dkimpy:
status: Incomplete → Triaged
assignee: nobody → Scott Kitterman (kitterman)
milestone: future → 1.0.0
Revision history for this message
Scott Kitterman (kitterman) wrote :

ate: Fri Nov 8 23:12:31 2019 -0500

        - Support signature verification with SubjectPublicKeyInfo formatted keys
          since, although rare, they are RFC 6376 specified (LP: #1851862)

Date: Fri Nov 8 21:21:35 2019 -0500

    New keys and test cases for RSA key format variants like RSAPublicKey

Changed in dkimpy:
status: Triaged → Fix Committed
Revision history for this message
Scott Kitterman (kitterman) wrote :

Fixed to say RSAPublicKey instead ...

Revision history for this message
Scott Kitterman (kitterman) wrote :

2019-12-09 Version 1.0.0
    - Add support for RFC 8460 tlsrpt DKIM signature processing (LP: #1847020)
    - Add async support with aiodns for DKIM verification (ARC not supported)
      (LP: #1847002)
    - Add new timeout parameter to enable DNS lookup timeouts to be adjusted
    - Add new DKIM.present function to allow applications to test if a DKIM
      signature is present without doing validation (LP: #1851141)
    - Support signature verification with RSAPublicKey formatted keys
      since, although rare, they are RFC 6376 specified (LP: #1851862)
    - Drop usage of pymilter Milter.dns in dnsplug since it doesn't support
      havine a timeout passed to it
    - Catch binascii related key format errors (LP: #1854477)

Changed in dkimpy:
status: Fix Committed → Fix Released
To post a comment you must log in.