From 8032276b576b69894cc21daf5a198a50c171c271 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Tue, 30 Oct 2018 11:29:09 -0400 Subject: [PATCH] Bump version to 0.9.0 since this is now a feature release - Update oversigned (frozen) header field list to reduce signature fragility (removes 'date' and 'subject' fields from being oversigned by default - see usage section of README for information on how to restore the previous behavior) - Added new add_should_not for DKIM/ARC classes to prevent additional header fields from being signed - Added 'from' to should sign list (to prevent it from not being signed at all in the unusual event that 'from' is locally removed from the frozen header field set (LP: #1525048) - Updates for experimental ARC support: - Specified that for ARC, Authentication-Results should not be signed --- ChangeLog | 12 +++++++++++- README | 15 +++++++++++++-- dkim/__init__.py | 26 +++++++++++++++++++++++--- dkim/tests/test_arc.py | 5 +++-- setup.py | 2 +- 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 17a0a55..94f12c0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,19 @@ -UNRELEASED Version 0.8.2 +UNRELEASED Version 0.9.0 + - Update oversigned (frozen) header field list to reduce signature + fragility (removes 'date' and 'subject' fields from being oversigned by + default - see usage section of README for information on how to restore + the previous behavior) + - Added new add_should_not for DKIM/ARC classes to prevent additional + header fields from being signed + - Added 'from' to should sign list (to prevent it from not being signed at + all in the unusual event that 'from' is locally removed from the frozen + header field set (LP: #1525048) - Updates for experimental ARC support: - Limit to rsa-sha256, rsa-sha1 not used by ARC and multi-signature design TBD - Raise error when ARC signing if i= instance limit value of 50 is exceeded + - Specified that for ARC, Authentication-Results should not be signed - Fix DNS lookups to be compatible with EAI addresses in domains and selectors (John Levine) - Add type Hinting for sign and verify functions (LP: #1782596) diff --git a/README b/README index 2c21495..7a8bfc2 100644 --- a/README +++ b/README @@ -11,7 +11,7 @@ signing and verification. VERSION -This is dkimpy 0.8.0. +This is dkimpy 0.9.0. REQUIREMENTS @@ -91,6 +91,17 @@ function takes an RFC822 formatted message, and returns True or False depending on whether the signature verifies correctly. There is also a DKIM class which can be used to perform these functions in a more modern way. +In version 0.9.0, the default set of header fields that are oversigned was +changed from 'from', 'subject', 'date' to 'from' to reduce fragility of +signatures. To restore the previous behavior, you can add them back after +instantiating your DKIM class using the add_frozen function as shown in the +following example: + +>>> dkim = DKIM() +>>> dkim.add_frozen((b'date',b'subject')) +>>> [text(x) for x in sorted(dkim.frozen_sign)] +['date', 'from', 'subject'] + RFC8301 updated DKIM requirements in two ways: 1. It set the minimum valid RSA key size to 1024 bits. @@ -145,7 +156,7 @@ code 1. As of version 0.6.0, dkimpy provides experimental support for ARC (Authenticated Received Chain): -https://tools.ietf.org/html/draft-ietf-dmarc-arc-protocol-01 +https://tools.ietf.org/html/draft-ietf-dmarc-arc-protocol-18 This new functionality is marked experimental because the protocol is still under development. There are no guarantees about API stability or diff --git a/dkim/__init__.py b/dkim/__init__.py index f467d8a..fb2b5a9 100644 --- a/dkim/__init__.py +++ b/dkim/__init__.py @@ -486,12 +486,12 @@ class DomainSigner(object): #: #: The short list below is the result more of instinct than logic. #: @since: 0.5 - FROZEN = (b'from',b'date',b'subject') + FROZEN = (b'from',) #: The rfc6376 recommended header fields to sign #: @since: 0.5 SHOULD = ( - b'sender', b'reply-to', b'subject', b'date', b'message-id', b'to', b'cc', + b'from', b'sender', b'reply-to', b'subject', b'date', b'message-id', b'to', b'cc', b'mime-version', b'content-type', b'content-transfer-encoding', b'content-id', b'content-description', b'resent-date', b'resent-from', b'resent-sender', b'resent-to', b'resent-cc', b'resent-message-id', @@ -526,11 +526,30 @@ class DomainSigner(object): >>> dkim = DKIM() >>> dkim.add_frozen(DKIM.RFC5322_SINGLETON) >>> [text(x) for x in sorted(dkim.frozen_sign)] - ['cc', 'date', 'from', 'in-reply-to', 'message-id', 'references', 'reply-to', 'sender', 'subject', 'to'] + ['cc', 'date', 'from', 'in-reply-to', 'message-id', 'references', 'reply-to', 'sender', 'to'] + >>> dkim2 = DKIM() + >>> dkim2.add_frozen((b'date',b'subject')) + >>> [text(x) for x in sorted(dkim2.frozen_sign)] + ['date', 'from', 'subject'] """ self.frozen_sign.update(x.lower() for x in s if x.lower() not in self.should_not_sign) + + def add_should_not(self,s): + """ Add headers not in should_not_sign to frozen_sign. + @param s: list of headers to add to frozen_sign + @since: 0.5 + + >>> dkim = DKIM() + >>> dkim.add_should_not(DKIM.RFC5322_SINGLETON) + >>> [text(x) for x in sorted(dkim.should_not_sign)] + ['bcc', 'cc', 'comments', 'date', 'dkim-signature', 'in-reply-to', 'keywords', 'message-id', 'received', 'references', 'reply-to', 'resent-bcc', 'return-path', 'sender', 'to'] + """ + self.should_not_sign.update(x.lower() for x in s + if x.lower() not in self.frozen_sign) + + #: Load a new message to be signed or verified. #: @param message: an RFC822 formatted message to be signed or verified #: (with either \\n or \\r\\n line endings) @@ -900,6 +919,7 @@ class ARC(DomainSigner): timestamp=None, standardize=False): INSTANCE_LIMIT = 50 # Maximum allowed i= value + self.add_should_not(('Authentication-Results',)) # check if authres has been imported try: AuthenticationResultsHeader diff --git a/dkim/tests/test_arc.py b/dkim/tests/test_arc.py index 2ddcb88..e02f2eb 100644 --- a/dkim/tests/test_arc.py +++ b/dkim/tests/test_arc.py @@ -69,11 +69,12 @@ Y+vtSBczUiKERHv1yRbcaQtZFh5wtiRrN04BLUTD21MycBX5jYchHjPY/wIDAQAB""" return _dns_responses[domain] def test_signs_and_verifies(self): - # A message verifies after being signed. + # A message verifies after being signed + self.maxDiff = None sig_lines = dkim.arc_sign( self.message, b"test", b"example.com", self.key, b"lists.example.org", timestamp="12345") - expected_sig = [b'ARC-Seal: i=1; cv=none; a=rsa-sha256; d=example.com; s=test; t=12345; \r\n b=3jOfBfTKcq+3r3Xv158DybT4mWFxrGcop+cgyLUX2ETCMHqNXYwGx2h+NY46tr\r\n k0Lg6R8i+560+KC8PLcCURYYJNJUHLHPIifhddy1aMNL9l4CoI+Oz+rocd2IZeb/\r\n I9V5amOUOWnAlOvyrSt0XfzLJRTS8qJW3Is1CRkkgyLoI=\r\n', b'ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; \r\n d=example.com; s=test; t=12345; h=message-id : \r\n date : from : to : subject : date : from : \r\n subject; \r\n bh=wE7NXSkgnx9PGiavN4OZhJztvkqPDlemV3OGuEnLwNo=; \r\n b=Bj/AEKhmzMbltWXrfLA8UZNp6/5cj8/IzqbgQec4vGobDZRsa\r\n C0YIPM4tcqK2uTS62kwh40cndXTDsCppvRsBy1sIO3eRNyuLUOh\r\n 0XGrz0AdLQMv+IOdyQqZfMVkq8DuQ4Qdl7ee99uYf3D8S+L7GuD\r\n wJSk7dyH+P2BKxz2nyB0=\r\n', b'ARC-Authentication-Results: i=1; lists.example.org; arc=none;\r\n spf=pass smtp.mfrom=jqd@d1.example;\r\n dkim=pass (1024-bit key) header.i=@d1.example;\r\n dmarc=pass\r\n'] + expected_sig = [b'ARC-Seal: i=1; cv=none; a=rsa-sha256; d=example.com; s=test; t=12345; \r\n b=2DTLaiAcKznJwwNOWoOG8WBsdTq+/S92TZbURDxkgjGCmsSw8czQiisf02sC92\r\n 0nswz3JItA80l70iguM00onrj3eCe41yRDzB8lQL3kbrDyM+wUewmyhPoifRTsng\r\n t9ELTFrax4kCeHv6SdNz3uJfGYwQc+WCFEchXt3szNTRM=\r\n', b'ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; \r\n d=example.com; s=test; t=12345; h=message-id : \r\n date : from : to : subject : from; \r\n bh=wE7NXSkgnx9PGiavN4OZhJztvkqPDlemV3OGuEnLwNo=; \r\n b=a0f6qc3k9eECTSR155A0TQS+LjqPFWfI/brQBA83EUz00SNxj\r\n 1wmWykvs1hhBVeM0r1kEQc6CKbzRYaBNSiFj4q8JBpRIujLz1qL\r\n yGmPuAI6ddu/Z/1hQxgpVcp/odmI1UMV2R+d+yQ7tUp3EQxF/GY\r\n Nt22rV4rNmDmANZVqJ90=\r\n', b'ARC-Authentication-Results: i=1; lists.example.org; arc=none;\r\n spf=pass smtp.mfrom=jqd@d1.example;\r\n dkim=pass (1024-bit key) header.i=@d1.example;\r\n dmarc=pass\r\n'] self.assertEqual(expected_sig, sig_lines) diff --git a/setup.py b/setup.py index e04ab6f..e0c9584 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ from setuptools import setup import os import sys -version = "0.8.2" +version = "0.9.0" kw = {} # Work-around for lack of 'or' requires in setuptools. try: