Version in base suite: 0.9.1-1 Base version: dkimpy_0.9.1-1 Target version: dkimpy_0.9.6-0+deb10u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/d/dkimpy/dkimpy_0.9.1-1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/d/dkimpy/dkimpy_0.9.6-0+deb10u1.dsc ChangeLog | 47 ++++++++++++++ LICENSE | 19 +++++ MANIFEST.in | 2 PKG-INFO | 7 +- README | 52 +++++++-------- debian/changelog | 50 +++++++++++++++ debian/gbp.conf | 3 debian/watch | 2 dkim/__init__.py | 110 +++++++++++++++++++++++---------- dkim/arcsign.py | 16 +++- dkim/canonicalization.py | 22 ++++-- dkim/dkimsign.py | 4 - dkim/dnsplug.py | 5 + dkim/tests/data/rfc6376.signed.rsa.msg | 20 ++++++ dkim/tests/test_arc.py | 5 - dkim/tests/test_dkim.py | 43 ++++++++++++ dkim/tests/test_util.py | 25 +++++++ dkim/util.py | 6 + dkimpy.egg-info/PKG-INFO | 7 +- dkimpy.egg-info/SOURCES.txt | 2 dkimpy.egg-info/entry_points.txt | 6 - dkimpy.egg-info/requires.txt | 8 +- man/arcsign.1 | 14 +--- man/arcverify.1 | 4 - man/dkimsign.1 | 5 - man/dkimverify.1 | 4 - man/dknewkey.1 | 5 - setup.cfg | 3 setup.py | 2 29 files changed, 377 insertions(+), 121 deletions(-) diff -Nru dkimpy-0.9.1/ChangeLog dkimpy-0.9.6/ChangeLog --- dkimpy-0.9.1/ChangeLog 2018-12-09 19:04:26.000000000 +0000 +++ dkimpy-0.9.6/ChangeLog 2019-12-24 15:22:40.000000000 +0000 @@ -1,3 +1,50 @@ +2019-12-24 Version 0.9.6 + - Follow CNAMES when looking up key records when using DNS (pydns) + (LP: #1856421) + - Provide specialized error message when signing or verifying ed25519 + signatures and pynacl is not installed (LP: #1854475) + - Catch binascii related key format errors (LP: #1854477) + +2019-10-07 Version 0.9.5 + - Ignore unknown service types in key records (LP: #1847020) + - This is required by RFC 6376 and predecessors. It becomes important + now that RFC 8460, which defines a new DKIM service type exists. This + change is required to avoid processing tlsrpt keys like regular email + keys, which is incorrect, they have different requirements. + +2019-09-25 Verstion 0.9.4 + - Add LICENSE to MANIFEST.in so it is included in the tarball (LP: + #1845318) + +2019-08-09 Version 0.9.3 + - Fix linesep setting in arcsign script (LP: #1838262) (Thanks to Gowtham + Gopalakrishnan for the report and the patch) + - Fix default canonicalization for DKIM signature verification to be + simple/simple per RFC 6376 (LP: #1839299) (Thanks to Cyril Nicodème for + the report and a suggested fix) + +2019-04-14 Version 0.9.2 + - Fix the arcsign script so it works with the current API (Note: the new + srv_id option is the authserv_id to use in the ARC signatures - Only AR + fields with an authserv-id that matches srv_id will be considered for + ARC signing) + - Fix cv=none processing for initial signature in chain + - Add additional text documenting use of srv_id for ARC signing to + docstrings and man 1 arcsign (LP: #1808301) + - Use same line seperator for output as input in dkimsign/arcsign + (LP: #1808686) + - Refactor canonicalization.py strip_trailing_lines to avoid using re for + more consistent processing across python versions (Thanks to Jonathan + Bastien-Filiatrault for the change) + - Refactor header folding for more consistent results, including reduced + stray whitespace (Also Jonathan Bastien-Filiatrault) + - Don't log message headers and body unless explicitely requested. This + should also reduce memory usage on large messages. (Jonathan + Bastien-Filiatrault) + - Clarify the crlf does not count towards line length in fold + - Adjust fold maxlen to one shorter for lines after the first, since they + already have a leading space (LP: #1823008) + 2018-12-09 Version 0.9.1 - Fixed ARC verification to fail if h= tag is present in Arc-Seal and added tests diff -Nru dkimpy-0.9.1/LICENSE dkimpy-0.9.6/LICENSE --- dkimpy-0.9.1/LICENSE 1970-01-01 00:00:00.000000000 +0000 +++ dkimpy-0.9.6/LICENSE 2019-11-03 17:09:47.000000000 +0000 @@ -0,0 +1,19 @@ +This software is provided 'as-is', without any express or implied +warranty. In no event will the author be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +Copyright (c) 2008 Greg Hewgill http://hewgill.com +See individual files for information about modification to these files and +additional copyright information. diff -Nru dkimpy-0.9.1/MANIFEST.in dkimpy-0.9.6/MANIFEST.in --- dkimpy-0.9.1/MANIFEST.in 2018-03-26 02:37:59.000000000 +0000 +++ dkimpy-0.9.6/MANIFEST.in 2019-12-24 15:21:38.000000000 +0000 @@ -1,8 +1,10 @@ include dkim/* include dkim/tests/* include dkim/tests/data/* +include LICENSE include README include ChangeLog include MANIFEST.in include man/* include test.py +exclude dkim/*.pyc dkim/tests/*.pyc diff -Nru dkimpy-0.9.1/PKG-INFO dkimpy-0.9.6/PKG-INFO --- dkimpy-0.9.1/PKG-INFO 2018-12-09 19:05:46.000000000 +0000 +++ dkimpy-0.9.6/PKG-INFO 2019-12-24 15:23:27.000000000 +0000 @@ -1,6 +1,6 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: dkimpy -Version: 0.9.1 +Version: 0.9.6 Summary: DKIM (DomainKeys Identified Mail) Home-page: https://launchpad.net/dkimpy Author: Scott Kitterman @@ -22,3 +22,6 @@ Classifier: Topic :: Communications :: Email :: Filters Classifier: Topic :: Internet :: Name Service (DNS) Classifier: Topic :: Software Development :: Libraries :: Python Modules +Provides-Extra: ARC +Provides-Extra: ed25519 +Provides-Extra: testing diff -Nru dkimpy-0.9.1/README dkimpy-0.9.6/README --- dkimpy-0.9.1/README 2018-10-30 15:01:24.000000000 +0000 +++ dkimpy-0.9.6/README 2019-12-24 15:21:38.000000000 +0000 @@ -11,7 +11,7 @@ VERSION -This is dkimpy 0.9.0. +This is dkimpy 0.9.6. REQUIREMENTS @@ -36,7 +36,7 @@ setuptools developers decided not being able to do this by default is a feature: -python setup.py install --single-version-externally-managed --record=/dev/null +python3 setup.py install --single-version-externally-managed --record=/dev/null DOCUMENTATION @@ -48,11 +48,11 @@ To run dkimpy's test suite: - PYTHONPATH=. python dkim + PYTHONPATH=. python3 dkim or - python test.py + python3 test.py or - PYTHONPATH=. python -m unittest dkim.tests.test_suite + PYTHONPATH=. python3 -m unittest dkim.tests.test_suite Alternatively, if you have testrepository installed: @@ -67,21 +67,13 @@ The included ARC tests are very limited. The primary testing method for ARC is using the ARC test suite: https://github.com/ValiMail/arc_test_suite -As of 0.6.0, all tests except as_fields_b_512 pass for both python2.7 and -python3.5. The test suite ships with test runners for dkimpy. After -downloading the test suite, you can run the signing and validation tests like -this: +As of 0.6.0, all tests pass for both python2.7 and python3. The test suite + ships with test runners for dkimpy. After downloading the test suite, you + can run the signing and validation tests like this: python2.7 ./testarc.py sign runners/arcsigntest.py python2.7 ./testarc.py validate runners/arcverifytest.py -The reason for the test failure is that the ARC specification (as of 20170120) -sets the minimum key size to 512 bits. This is operationally inappropriate, -so dkimpy sets the default minkey=1024, the same as is used for DKIM. This -can be overridden, but that is not recommended. The minimum key size -requirement for DKIM (and thus ARC) has recently been updated to require at -least a 1024 bit key. See RFC 8301. - USAGE The dkimpy library offers one module called dkim. The sign() function takes an @@ -124,15 +116,18 @@ https://datatracker.ietf.org/doc/draft-ietf-dcrup-dkim-crypto/ -The dkimpy 0.7 implementation matches the -08 revision of the draft, except it -uses Ed25519 vice Ed25519ph (a change to Ed25519 is planned for -09, but that -had not been published yet as of the release of dkimpy 0.7). - -draft-ietf-dcrup-dkim-crypto-09 has been released and dkimpy 0.7 and later are -aligned to its requirements. As of 0.8, ed25519 need not be considered -experimental. The dkimpy implementation has successfully interoperated with -three other implementations and the technical parameters for ed25519-sha256 -are defined and stable. +The RFC that documents ed25519 DKIM signatures, RFC 8463, has been released +and dkimpy 0.7 and later are aligned to its requirements. As of 0.8, ed25519 +need not be considered experimental. The dkimpy implementation has +successfully interoperated with three other implementations and the technical +parameters for ed25519-sha256 are defined and stable. + +To install from pypi with the required optional depenencies, use the ed25519 +option: + +```pip install -e '.[ed25519]'``` + +DKIM SCRIPTS Three helper programs are also supplied: dknewkey, dkimsign and dkimverify @@ -156,15 +151,18 @@ 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-18 +https://tools.ietf.org/html/draft-ietf-dmarc-arc-protocol-23 This new functionality is marked experimental because the protocol is still under development. There are no guarantees about API stability or -compatibility. +compatibility. Since the draft is through IETF last call, further changes are +unlikely. In addition to arcsign and arcverify, the dkim module now provides arc_sign and arc_verify functions as well as an ARC class. +Both DKIM ed25519 and ARC are now considered stable (no longer experimantal). + FEEDBACK Bug reports may be submitted to the bug tracker for the dkimpy project on diff -Nru dkimpy-0.9.1/debian/changelog dkimpy-0.9.6/debian/changelog --- dkimpy-0.9.1/debian/changelog 2018-12-09 20:33:28.000000000 +0000 +++ dkimpy-0.9.6/debian/changelog 2019-12-24 15:41:22.000000000 +0000 @@ -1,3 +1,53 @@ +dkimpy (0.9.6-0+deb10u1) buster; urgency=medium + + * Update debian/watch to only see 0.9 versions for stable updates + * Update debian/gbp.conf to use buster branches + * New upstream bugfix releases: + * 0.9.6 + - Follow CNAMES when looking up key records when using DNS (pydns) + (LP: #1856421) + - Provide specialized error message when signing or verifying ed25519 + signatures and pynacl is not installed (LP: #1854475) + - Catch binascii related key format errors (LP: #1854477) + * 0.9.5 + - Ignore unknown service types in key records (LP: #1847020) + - This is required by RFC 6376 and predecessors. It becomes important + now that RFC 8460, which defines a new DKIM service type exists. This + change is required to avoid processing tlsrpt keys like regular email + keys, which is incorrect, they have different requirements. + * 0.9.4 + - Add LICENSE to MANIFEST.in so it is included in the tarball (LP: + #1845318) + * 0.9.3 + - Fix linesep setting in arcsign script (LP: #1838262) (Thanks to Gowtham + Gopalakrishnan for the report and the patch) + - Fix default canonicalization for DKIM signature verification to be + simple/simple per RFC 6376 (LP: #1839299) (Thanks to Cyril Nicodème for + the report and a suggested fix) + * 0.9.2 + - Fix the arcsign script so it works with the current API (Note: the new + srv_id option is the authserv_id to use in the ARC signatures - Only AR + fields with an authserv-id that matches srv_id will be considered for + ARC signing) + - Fix cv=none processing for initial signature in chain + - Add additional text documenting use of srv_id for ARC signing to + docstrings and man 1 arcsign (LP: #1808301) + - Use same line seperator for output as input in dkimsign/arcsign + (LP: #1808686) + - Refactor canonicalization.py strip_trailing_lines to avoid using re for + more consistent processing across python versions (Thanks to Jonathan + Bastien-Filiatrault for the change) + - Refactor header folding for more consistent results, including reduced + stray whitespace (Also Jonathan Bastien-Filiatrault) + - Don't log message headers and body unless explicitely requested. This + should also reduce memory usage on large messages. (Jonathan + Bastien-Filiatrault) + - Clarify the crlf does not count towards line length in fold + - Adjust fold maxlen to one shorter for lines after the first, since they + already have a leading space (LP: #1823008) + + -- Scott Kitterman Tue, 24 Dec 2019 10:41:22 -0500 + dkimpy (0.9.1-1) unstable; urgency=medium * New upstream release diff -Nru dkimpy-0.9.1/debian/gbp.conf dkimpy-0.9.6/debian/gbp.conf --- dkimpy-0.9.1/debian/gbp.conf 2018-02-07 06:20:55.000000000 +0000 +++ dkimpy-0.9.6/debian/gbp.conf 2019-12-24 15:39:42.000000000 +0000 @@ -1,2 +1,3 @@ [DEFAULT] -debian-branch=debian/master +debian-branch=debian/buster-updates +upstream-branch=upstream-buster diff -Nru dkimpy-0.9.1/debian/watch dkimpy-0.9.6/debian/watch --- dkimpy-0.9.1/debian/watch 2018-12-09 20:28:17.000000000 +0000 +++ dkimpy-0.9.6/debian/watch 2019-12-24 15:32:40.000000000 +0000 @@ -1,4 +1,4 @@ version=3 opts="pgpsigurlmangle=s/$/.asc/" https://launchpad.net/dkimpy/+download \ -https://launchpad.net/dkimpy/.*/.*/dkimpy-(.*)\.tar\.gz +https://launchpad.net/dkimpy/.*/.*/dkimpy-(0.9.*)\.tar\.gz diff -Nru dkimpy-0.9.1/dkim/__init__.py dkimpy-0.9.6/dkim/__init__.py --- dkimpy-0.9.1/dkim/__init__.py 2018-11-09 23:30:35.000000000 +0000 +++ dkimpy-0.9.6/dkim/__init__.py 2019-12-24 15:21:38.000000000 +0000 @@ -24,7 +24,7 @@ # Contact: Brandon Long # # This has been modified from the original software. -# Copyright (c) 2016, 2017, 2018 Scott Kitterman +# Copyright (c) 2016, 2017, 2018, 2019 Scott Kitterman # # This has been modified from the original software. # Copyright (c) 2017 Valimail Inc @@ -37,6 +37,7 @@ import logging import re import time +import binascii # only needed for arc try: @@ -112,13 +113,15 @@ class HashThrough(object): - def __init__(self, hasher): + def __init__(self, hasher, debug=False): self.data = [] self.hasher = hasher self.name = hasher.name + self.debug = debug def update(self, data): - self.data.append(data) + if self.debug: + self.data.append(data) return self.hasher.update(data) def digest(self): @@ -175,7 +178,6 @@ """ Nacl package not installed, needed for ed25119 signatures """ pass - class UnknownKeyTypeError(DKIMException): """ Key type (k tag) is not known (rsa/ed25519) """ @@ -365,13 +367,14 @@ return s.encode('ascii') -def fold(header, namelen=0): - """Fold a header line into multiple crlf-separated lines at column 72. +def fold(header, namelen=0, linesep=b'\r\n'): + """Fold a header line into multiple crlf-separated lines of text at column + 72. The crlf does not count for line length. >>> text(fold(b'foo')) 'foo' >>> text(fold(b'foo '+b'foo'*24).splitlines()[0]) - 'foo ' + 'foo ' >>> text(fold(b'foo'*25).splitlines()[-1]) ' foo' >>> len(fold(b'foo'*25).splitlines()[0]) @@ -380,6 +383,8 @@ 'x' >>> text(fold(b'xyz'*24)) 'xyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyz' + >>> len(fold(b'xyz'*48)) + 150 """ # 72 is the max line length we actually want, but the header field name # has to fit in the first line too (See Debian Bug #863690). @@ -399,11 +404,12 @@ i = header[:maxleng].rfind(b" ") if i == -1: j = maxleng + pre += header[:j] + linesep + b" " else: j = i + 1 - pre += header[:j] + b"\r\n " + pre += header[:i] + linesep + b" " header = header[j:] - namelen = 0 + maxleng = 71 if len(header) > 2: return pre + header else: @@ -431,7 +437,10 @@ pass try: if pub[b'k'] == b'ed25519': - pk = nacl.signing.VerifyKey(pub[b'p'], encoder=nacl.encoding.Base64Encoder) + try: + pk = nacl.signing.VerifyKey(pub[b'p'], encoder=nacl.encoding.Base64Encoder) + except NameError: + raise NaClNotFoundError('pynacl module required for ed25519 signing, see README.md') keysize = 256 ktag = b'ed25519' except KeyError: @@ -447,6 +456,16 @@ ktag = b'rsa' if pub[b'k'] != b'rsa' and pub[b'k'] != b'ed25519': raise KeyFormatError('unknown algorithm in k= tag: {0}'.format(pub[b'k'])) + try: + # Ignore unknown service types, RFC 6376 3.6.1 + if pub[b's'] != b'*' and pub[b's'] != b'email': + pk = None + keysize = None + ktag = None + raise KeyFormatError('unknown service type in s= tag: {0}'.format(pub[b's'])) + except: + # Default is '*' - all service types, so no error if missing from key record + pass return pk, keysize, ktag @@ -459,12 +478,15 @@ #: (with either \\n or \\r\\n line endings) #: @param logger: a logger to which debug info will be written (default None) #: @param signature_algorithm: the signing algorithm to use when signing + #: @param debug_content: log headers and body after canonicalization (default False) + #: @param linesep: use this line seperator for folding the headers def __init__(self,message=None,logger=None,signature_algorithm=b'rsa-sha256', - minkey=1024): + minkey=1024, linesep=b'\r\n', debug_content=False): self.set_message(message) if logger is None: logger = get_default_logger() self.logger = logger + self.debug_content = debug_content and logger.isEnabledFor(logging.DEBUG) if signature_algorithm not in HASH_ALGORITHMS: raise ParameterError( "Unsupported signature algorithm: "+signature_algorithm) @@ -481,6 +503,9 @@ #: Minimum public key size. Shorter keys raise KeyFormatError. The #: default is 1024 self.minkey = minkey + # use this line seperator for output + self.linesep = linesep + #: Header fields to protect from additions by default. #: @@ -607,16 +632,17 @@ header_value = b"; ".join(b"=".join(x) for x in fields) if not standardize: - header_value = fold(header_value, namelen=len(header_name)) + header_value = fold(header_value, namelen=len(header_name), linesep=b'\r\n') header_value = RE_BTAG.sub(b'\\1',header_value) header = (header_name, b' ' + header_value) - h = HashThrough(self.hasher()) + h = HashThrough(self.hasher(), self.debug_content) sig = dict(fields) headers = canon_policy.canonicalize_headers(self.headers) self.signed_headers = hash_headers( h, canon_policy, headers, include_headers, header, sig) - self.logger.debug("sign %s headers: %r" % (header_name, h.hashed())) + if self.debug_content: + self.logger.debug("sign %s headers: %r" % (header_name, h.hashed())) if self.signature_algorithm == b'rsa-sha256' or self.signature_algorithm == b'rsa-sha1': try: @@ -633,10 +659,10 @@ # relaxed/simple (for broken receivers), and fold now. idx = [i for i in range(len(fields)) if fields[i][0] == b'b'][0] fields[idx] = (b'b', base64.b64encode(bytes(sig2))) - header_value = b"; ".join(b"=".join(x) for x in fields) + b"\r\n" + header_value = b"; ".join(b"=".join(x) for x in fields) + self.linesep if not standardize: - header_value = fold(header_value, namelen=len(header_name)) + header_value = fold(header_value, namelen=len(header_name), linesep=self.linesep) return header_value @@ -652,9 +678,12 @@ except KeyFormatError as e: self.logger.error("%s" % e) return False + except binascii.Error as e: + self.logger.error('KeyFormatError: {0}'.format(e)) + return False try: - canon_policy = CanonicalizationPolicy.from_c_value(sig.get(b'c', b'relaxed/relaxed')) + canon_policy = CanonicalizationPolicy.from_c_value(sig.get(b'c', b'simple/simple')) except InvalidCanonicalizationPolicyError as e: raise MessageFormatError("invalid c= value: %s" % e.args[0]) @@ -662,13 +691,14 @@ # validate body if present if b'bh' in sig: - h = HashThrough(hasher()) + h = HashThrough(hasher(), self.debug_content) body = canon_policy.canonicalize_body(self.body) if b'l' in sig: body = body[:int(sig[b'l'])] h.update(body) - self.logger.debug("body hashed: %r" % h.hashed()) + if self.debug_content: + self.logger.debug("body hashed: %r" % h.hashed()) bodyhash = h.digest() self.logger.debug("bh: %s" % base64.b64encode(bodyhash)) @@ -687,12 +717,13 @@ # generalized to check for extras of other singleton headers. if b'from' in include_headers: include_headers.append(b'from') - h = HashThrough(hasher()) + h = HashThrough(hasher(), self.debug_content) headers = canon_policy.canonicalize_headers(self.headers) self.signed_headers = hash_headers( h, canon_policy, headers, include_headers, sig_header, sig) - self.logger.debug("signed for %s: %r" % (sig_header[0], h.hashed())) + if self.debug_content: + self.logger.debug("signed for %s: %r" % (sig_header[0], h.hashed())) signature = base64.b64decode(re.sub(br"\s+", b"", sig[b'b'])) if ktag == b'rsa': try: @@ -761,7 +792,10 @@ except UnparsableKeyError as e: raise KeyFormatError(str(e)) elif self.signature_algorithm == b'ed25519-sha256': - pk = nacl.signing.SigningKey(privkey, encoder=nacl.encoding.Base64Encoder) + try: + pk = nacl.signing.SigningKey(privkey, encoder=nacl.encoding.Base64Encoder) + except NameError: + raise NaClNotFoundError('pynacl module required for ed25519 signing, see README.md') if identity is not None and not identity.endswith(domain): raise ParameterError("identity must end with domain") @@ -946,14 +980,13 @@ results_lists = [raw.replace(srv_id + b';', b'').strip() for (raw, parsed) in auth_headers] results_lists = [tags.split(b';') for tags in results_lists] results = [tag.strip() for sublist in results_lists for tag in sublist] - auth_results = srv_id + b'; ' + b';\r\n '.join(results) + auth_results = srv_id + b'; ' + (b';' + self.linesep + b' ').join(results) # extract cv parsed_auth_results = AuthenticationResultsHeader.parse('Authentication-Results: ' + auth_results.decode('utf-8')) arc_results = [res for res in parsed_auth_results.results if res.method == 'arc'] if len(arc_results) == 0: - self.logger.debug("no AR arc stamps found, chain terminated") - return [] + chain_validation_status = CV_None elif len(arc_results) != 1: self.logger.debug("multiple AR arc stamps found, failing chain") chain_validation_status = CV_Fail @@ -988,7 +1021,8 @@ if instance == 1 and chain_validation_status != CV_None: raise ParameterError("No existing chain found on message, cv should be none") elif instance != 1 and chain_validation_status == CV_None: - raise ParameterError("cv=none not allowed on instance %d" % instance) + self.logger.debug("no previous AR arc results found and instance > 1, chain terminated") + return [] new_arc_set = [] if chain_validation_status != CV_Fail: @@ -1008,9 +1042,10 @@ canon_policy = CanonicalizationPolicy.from_c_value(b'relaxed/relaxed') self.hasher = HASH_ALGORITHMS[self.signature_algorithm] - h = HashThrough(self.hasher()) + h = HashThrough(self.hasher(), self.debug_content) h.update(canon_policy.canonicalize_body(self.body)) - self.logger.debug("sign ams body hashed: %r" % h.hashed()) + if self.debug_content: + self.logger.debug("sign ams body hashed: %r" % h.hashed()) bodyhash = base64.b64encode(h.digest()) # Compute ARC-Message-Signature @@ -1173,6 +1208,9 @@ # and this can use simple canonicalization raw_ams_header = [(x, y) for (x, y) in self.headers if x.lower() == b'arc-message-signature'][0] + # Only relaxed canonicalization used by ARC + if b'c' not in sig: + sig[b'c'] = b'relaxed/relaxed' try: ams_valid = self.verify_sig(sig, include_headers, raw_ams_header, dnsfunc) except DKIMException as e: @@ -1201,6 +1239,9 @@ as_include_headers = [x[0].lower() for x in arc_headers] as_include_headers.reverse() as_header = (b'ARC-Seal', b' ' + as_value) + # Only relaxed canonicalization used by ARC + if b'c' not in sig: + sig[b'c'] = b'relaxed/relaxed' try: as_valid = self.verify_sig(sig, as_include_headers[:-1], as_header, dnsfunc) except DKIMException as e: @@ -1215,7 +1256,8 @@ def sign(message, selector, domain, privkey, identity=None, canonicalize=(b'relaxed', b'simple'), signature_algorithm=b'rsa-sha256', - include_headers=None, length=False, logger=None): + include_headers=None, length=False, logger=None, + linesep=b'\r\n'): # type: (bytes, bytes, bytes, bytes, bytes, tuple, bytes, list, bool, any) -> bytes """Sign an RFC822 message and return the DKIM-Signature header line. @param message: an RFC822 formatted message (with either \\n or \\r\\n line endings) @@ -1228,11 +1270,12 @@ @param include_headers: a list of strings indicating which headers are to be signed (default all headers not listed as SHOULD NOT sign) @param length: true if the l= tag should be included to indicate body length (default False) @param logger: a logger to which debug info will be written (default None) + @param linesep: use this line seperator for folding the headers @return: DKIM-Signature header field terminated by \\r\\n @raise DKIMException: when the message, include_headers, or key are badly formed. """ - d = DKIM(message,logger=logger,signature_algorithm=signature_algorithm) + d = DKIM(message,logger=logger,signature_algorithm=signature_algorithm,linesep=linesep) return d.sign(selector, domain, privkey, identity=identity, canonicalize=canonicalize, include_headers=include_headers, length=length) @@ -1259,23 +1302,24 @@ def arc_sign(message, selector, domain, privkey, srv_id, signature_algorithm=b'rsa-sha256', include_headers=None, timestamp=None, - logger=None, standardize=False): + logger=None, standardize=False, linesep=b'\r\n'): # type: (bytes, bytes, bytes, bytes, bytes, bytes, list, any, any, bool) -> list """Sign an RFC822 message and return the ARC set header lines for the next instance @param message: an RFC822 formatted message (with either \\n or \\r\\n line endings) @param selector: the DKIM selector value for the signature @param domain: the DKIM domain value for the signature @param privkey: a PKCS#1 private key in base64-encoded text form - @param srv_id: the authserv_id used to identify the ADMD's AR headers + @param srv_id: the authserv_id used to identify the ADMD's AR headers and to use for ARC authserv_id @param signature_algorithm: the signing algorithm to use when signing @param include_headers: a list of strings indicating which headers are to be signed (default all headers not listed as SHOULD NOT sign) @param timestamp: the time in integer seconds when the message is sealed (default is int(time.time) based on platform, can be string or int) @param logger: a logger to which debug info will be written (default None) + @param linesep: use this line seperator for folding the headers @return: A list containing the ARC set of header fields for the next instance @raise DKIMException: when the message, include_headers, or key are badly formed. """ - a = ARC(message,logger=logger,signature_algorithm=b'rsa-sha256') + a = ARC(message,logger=logger,signature_algorithm=b'rsa-sha256',linesep=linesep) if not include_headers: include_headers = a.default_sign_headers() return a.sign(selector, domain, privkey, srv_id, include_headers=include_headers, diff -Nru dkimpy-0.9.1/dkim/arcsign.py dkimpy-0.9.6/dkim/arcsign.py --- dkimpy-0.9.1/dkim/arcsign.py 2018-10-28 00:48:51.000000000 +0000 +++ dkimpy-0.9.6/dkim/arcsign.py 2019-11-03 17:09:47.000000000 +0000 @@ -24,6 +24,13 @@ # This has been modified from the original software. # Copyright (c) 2016 Google, Inc. # Contact: Brandon Long +# This has been modified from the original software. +# Copyright (c) 2017, 2018, 2019 Scott Kitterman +# +# This has been modified from the original software. +# Copyright (c) 2017 Valimail Inc +# Contact: Gene Shuman + from __future__ import print_function @@ -37,8 +44,8 @@ def main(): - if len(sys.argv) != 4: - print("Usage: arcsign.py selector domain privatekeyfile", file=sys.stderr) + if len(sys.argv) != 5: + print("Usage: arcsign.py selector domain privatekeyfile srv_id", file=sys.stderr) sys.exit(1) if sys.version_info[0] >= 3: @@ -49,17 +56,18 @@ selector = sys.argv[1].encode('ascii') domain = sys.argv[2].encode('ascii') privatekeyfile = sys.argv[3] + srv_id = sys.argv[4].encode('ascii') message = sys.stdin.read() # Pick a cv status cv = dkim.CV_None - if re.search('arc-seal', message, re.IGNORECASE): + if re.search(b'arc-seal', message, re.IGNORECASE): cv = dkim.CV_Pass #try: sig = dkim.arc_sign(message, selector, domain, open(privatekeyfile, "rb").read(), - domain + ": none", cv) + srv_id, cv, linesep=dkim.util.get_linesep(message)) for line in sig: sys.stdout.write(line) sys.stdout.write(message) diff -Nru dkimpy-0.9.1/dkim/canonicalization.py dkimpy-0.9.6/dkim/canonicalization.py --- dkimpy-0.9.1/dkim/canonicalization.py 2018-10-29 23:42:01.000000000 +0000 +++ dkimpy-0.9.6/dkim/canonicalization.py 2019-11-03 17:09:47.000000000 +0000 @@ -41,15 +41,21 @@ def strip_trailing_lines(content): - content = re.sub(b"(\r\n)*$", b"\r\n", content) - # Yes, this is horrible, but regex processing changed in python3.7 and it - # is the least horrible solution I came up with for python2.7, python3.7, - # and python3 << 3.7 combined support. Better solution welcome. - if len(content) >= 4: - if (content[len(content)-4:] == b'\r\n\r\n'): - content = content[:len(content)-2] - return content + end = None + while content.endswith(b"\r\n", 0, end): + if end is None: + end = -2 + else: + end -= 2 + if end is None: + return content + b"\r\n" + + end += 2 + if end == 0: + return content + + return content[:end] def unfold_header_value(content): return re.sub(b"\r\n", b"", content) diff -Nru dkimpy-0.9.1/dkim/dkimsign.py dkimpy-0.9.6/dkim/dkimsign.py --- dkimpy-0.9.1/dkim/dkimsign.py 2018-10-28 00:48:51.000000000 +0000 +++ dkimpy-0.9.6/dkim/dkimsign.py 2019-11-03 17:09:47.000000000 +0000 @@ -70,8 +70,8 @@ message = sys.stdin.read() try: - d = dkim.DKIM(message,logger=logger, - signature_algorithm=args.signalg) + d = dkim.DKIM(message,logger=logger, signature_algorithm=args.signalg, + linesep=dkim.util.get_linesep(message)) sig = d.sign(args.selector, args.domain, open( args.privatekeyfile, "rb").read(), identity = args.identity, canonicalize=canonicalize, include_headers=include_headers, diff -Nru dkimpy-0.9.1/dkim/dnsplug.py dkimpy-0.9.6/dkim/dnsplug.py --- dkimpy-0.9.1/dkim/dnsplug.py 2018-10-30 01:52:26.000000000 +0000 +++ dkimpy-0.9.6/dkim/dnsplug.py 2019-12-24 15:21:38.000000000 +0000 @@ -44,7 +44,10 @@ response = DNS.DnsRequest(name, qtype='txt').req() if not response.answers: return None - return b''.join(response.answers[0]['data']) + for answer in response.answers: + if answer['typename'].lower() == 'txt': + return b''.join(answer['data']) + return None def get_txt_Milter_dns(name): diff -Nru dkimpy-0.9.1/dkim/tests/data/rfc6376.signed.rsa.msg dkimpy-0.9.6/dkim/tests/data/rfc6376.signed.rsa.msg --- dkimpy-0.9.1/dkim/tests/data/rfc6376.signed.rsa.msg 1970-01-01 00:00:00.000000000 +0000 +++ dkimpy-0.9.6/dkim/tests/data/rfc6376.signed.rsa.msg 2019-12-15 07:33:19.000000000 +0000 @@ -0,0 +1,20 @@ +DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; + d=football.example.com; i=@football.example.com; + q=dns/txt; s=test; t=1527915362; h=from : to : subject : + date : message-id : from : subject : date; + bh=4bLNXImK9drULnmePzZNEBleUanJCX5PIsDIFoH4KTQ=; + b=icKcLSEZYXJ95flvWE8FT6hl5iqd8MC/LEKYH0QjsqYy6MO/4pgVNCZH + l/RAXAuADxE/40Fg7uTlxwwD1hjN2Ple6J//cJfslBdDOq6zTVbne1dqtl + NOat7iamJ1AfRqyG+ja7a2AZsrpUuJ7VA6O+0zRYPqpwMEkEFIzI9i/Xk= +From: Joe SixPack +To: Suzie Q +Subject: Is dinner ready? +Date: Fri, 11 Jul 2003 21:00:37 -0700 (PDT) +Message-ID: <20030712040037.46341.5F8J@football.example.com> + +Hi. + +We lost the game. Are you hungry yet? + +Joe. + diff -Nru dkimpy-0.9.1/dkim/tests/test_arc.py dkimpy-0.9.6/dkim/tests/test_arc.py --- dkimpy-0.9.1/dkim/tests/test_arc.py 2018-11-10 00:56:17.000000000 +0000 +++ dkimpy-0.9.6/dkim/tests/test_arc.py 2019-12-24 15:21:38.000000000 +0000 @@ -74,8 +74,7 @@ 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=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'] - + expected_sig = [b'ARC-Seal: i=1; cv=none; a=rsa-sha256; d=example.com; s=test; t=12345;\r\n b=MBw2+L1/4PuYWJlt1tZlDtbOvyfbyH2t2N6DinFV/BIaB2LqbDKTYjXXk9HuuK1/qEkTd\r\n TxCYScIrtVO7pFbGiSawMuLatVzHNCqTURa1zBTXr2mKW1hgdmrtMMUcMVCYxr1AJpu6IYX\r\n VMIoOAn7tIDdO0VLokK6FnIXTWEAplQ=\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 : date : from : to :\r\n subject : from; bh=wE7NXSkgnx9PGiavN4OZhJztvkqPDlemV3OGuEnLwNo=;\r\n b=a0f6qc3k9eECTSR155A0TQS+LjqPFWfI/brQBA83EUz00SNxj1wmWykvs1hhBVeM0r1kE\r\n Qc6CKbzRYaBNSiFj4q8JBpRIujLz1qLyGmPuAI6ddu/Z/1hQxgpVcp/odmI1UMV2R+d+yQ7\r\n tUp3EQxF/GYNt22rV4rNmDmANZVqJ90=\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) (cv, res, reason) = dkim.arc_verify(b''.join(sig_lines) + self.message, dnsfunc=self.dnsfunc) @@ -84,7 +83,7 @@ def test_fails_h_in_as(self): # ARC 4.1.3, h= not allowed in AS self.maxDiff = None - sig_lines = [b'ARC-Seal: i=1; cv=none; a=rsa-sha256; d=example.com; s=test; t=12345; \r\n h=message-id : date : from : to : subject : from; \r\n b=mIurIuLl0/wAxWhA4DBS1wsUE15IBnmJ7o3sH15hIuesdD4smz1cCLXVhRtxQE\r\n rVtVLv4OgNCgdFsB5zbSOUao2bSSYP6y0BGyCWvr+hU4tai5axIc1Kfwbtv/0Mqg\r\n waiGJPreOAAeZOJ4vPfdaAbSXlN5MI4PHW89U82FSIBKI=\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'] + sig_lines = [b'ARC-Seal: i=1; cv=none; a=rsa-sha256; d=example.com; s=test; t=12345;\r\n h=message-id : date : from : to : subject : from;\r\n b=mIurIuLl0/wAxWhA4DBS1wsUE15IBnmJ7o3sH15hIuesdD4smz1cCLXVhRtxQE\r\n rVtVLv4OgNCgdFsB5zbSOUao2bSSYP6y0BGyCWvr+hU4tai5axIc1Kfwbtv/0Mqg\r\n waiGJPreOAAeZOJ4vPfdaAbSXlN5MI4PHW89U82FSIBKI=\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'] (cv, res, reason) = dkim.arc_verify(b''.join(sig_lines) + self.message, dnsfunc=self.dnsfunc) self.assertEqual(cv, dkim.CV_Fail) diff -Nru dkimpy-0.9.1/dkim/tests/test_dkim.py dkimpy-0.9.6/dkim/tests/test_dkim.py --- dkimpy-0.9.1/dkim/tests/test_dkim.py 2018-10-28 00:48:51.000000000 +0000 +++ dkimpy-0.9.6/dkim/tests/test_dkim.py 2019-12-24 15:21:38.000000000 +0000 @@ -46,6 +46,11 @@ self.assertEqual( b"foo" * 24 + b"\r\n foo", dkim.fold(b"foo" * 25)) + def test_linesep(self): + self.assertEqual( + b"foo" * 24 + b"\n foo", dkim.fold(b"foo" * 25, linesep=b"\n")) + + class TestSignAndVerify(unittest.TestCase): """End-to-end signature and verification tests.""" @@ -54,12 +59,13 @@ self.message = read_test_data("test.message") self.message3 = read_test_data("rfc6376.msg") self.message4 = read_test_data("rfc6376.signed.msg") + self.message5 = read_test_data("rfc6376.signed.rsa.msg") self.key = read_test_data("test.private") self.rfckey = read_test_data("rfc8032_7_1.key") def dnsfunc(self, domain): sample_dns = """\ -k=rsa; \ +k=rsa; s=email;\ p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANmBe10IgY+u7h3enWTukkqtUD5PR52T\ b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ==""" @@ -172,6 +178,24 @@ self.assertTrue(domain in _dns_responses,domain) return _dns_responses[domain] + def dnsfunc6(self, domain, timeout=5): + sample_dns = """\ +k=rsa; \ +p=MFwwDQYJKoZIhvNAQEBBQADSwAwSAJBANmBe10IgY+u7h3enWTukkqtUD5PR52T\ +b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ==""" + + _dns_responses = { + 'test._domainkey.football.example.com.': sample_dns, + 'brisbane._domainkey.football.example.com.': """v=DKIM1; k=ed25519; \ +p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=""" + } + try: + domain = domain.decode('ascii') + except UnicodeDecodeError: + return None + self.assertTrue(domain in _dns_responses,domain) + return _dns_responses[domain] + def test_verifies(self): # A message verifies after being signed. for header_algo in (b"simple", b"relaxed"): @@ -203,6 +227,23 @@ res = d.verify(dnsfunc=self.dnsfunc5) self.assertTrue(res) + def test_catch_bad_key(self): + # Raise correct error for defective public key. + d = dkim.DKIM(self.message5) + res = d.verify(dnsfunc=self.dnsfunc6) + self.assertFalse(res) + + def test_verifies_lflinesep(self): + # A message verifies after being signed. + for header_algo in (b"simple", b"relaxed"): + for body_algo in (b"simple", b"relaxed"): + sig = dkim.sign( + self.message, b"test", b"example.com", self.key, + canonicalize=(header_algo, body_algo), linesep=b"\n") + res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) + self.assertFalse(b'\r\n' in sig) + self.assertTrue(res) + def test_implicit_k(self): # A message verifies after being signed when k= tag is not provided. for header_algo in (b"simple", b"relaxed"): diff -Nru dkimpy-0.9.1/dkim/tests/test_util.py dkimpy-0.9.6/dkim/tests/test_util.py --- dkimpy-0.9.1/dkim/tests/test_util.py 2018-06-16 21:28:10.000000000 +0000 +++ dkimpy-0.9.6/dkim/tests/test_util.py 2019-11-03 17:09:47.000000000 +0000 @@ -22,6 +22,7 @@ DuplicateTag, InvalidTagSpec, parse_tag_value, + get_linesep, ) @@ -75,6 +76,30 @@ self.assertEqual(len(sig),11) +class TestGetLineSep(unittest.TestCase): + """Line seperator probing tests.""" + + def test_default(self): + self.assertEqual( + b'\r\n', + get_linesep(b'abc')) + + def test_withcrlf(self): + self.assertEqual( + b'\r\n', + get_linesep(b'abc\r\n')) + + def test_withlf(self): + self.assertEqual( + b'\n', + get_linesep(b'abc\n')) + + def test_toosmall(self): + self.assertEqual( + b'\r\n', + get_linesep(b'a')) + + def test_suite(): from unittest import TestLoader return TestLoader().loadTestsFromName(__name__) diff -Nru dkimpy-0.9.1/dkim/util.py dkimpy-0.9.6/dkim/util.py --- dkimpy-0.9.1/dkim/util.py 2018-03-11 22:42:04.000000000 +0000 +++ dkimpy-0.9.6/dkim/util.py 2019-11-03 17:09:47.000000000 +0000 @@ -33,6 +33,7 @@ 'InvalidTagSpec', 'InvalidTagValueList', 'parse_tag_value', + 'get_linesep', ] @@ -80,3 +81,8 @@ if not logger.handlers: logger.addHandler(NullHandler()) return logger + +def get_linesep(msg): + if msg[-2:] != b'\r\n' and msg[-1:] == b'\n': + return b'\n' + return b'\r\n' diff -Nru dkimpy-0.9.1/dkimpy.egg-info/PKG-INFO dkimpy-0.9.6/dkimpy.egg-info/PKG-INFO --- dkimpy-0.9.1/dkimpy.egg-info/PKG-INFO 2018-12-09 19:05:45.000000000 +0000 +++ dkimpy-0.9.6/dkimpy.egg-info/PKG-INFO 2019-12-24 15:23:26.000000000 +0000 @@ -1,6 +1,6 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: dkimpy -Version: 0.9.1 +Version: 0.9.6 Summary: DKIM (DomainKeys Identified Mail) Home-page: https://launchpad.net/dkimpy Author: Scott Kitterman @@ -22,3 +22,6 @@ Classifier: Topic :: Communications :: Email :: Filters Classifier: Topic :: Internet :: Name Service (DNS) Classifier: Topic :: Software Development :: Libraries :: Python Modules +Provides-Extra: ARC +Provides-Extra: ed25519 +Provides-Extra: testing diff -Nru dkimpy-0.9.1/dkimpy.egg-info/SOURCES.txt dkimpy-0.9.6/dkimpy.egg-info/SOURCES.txt --- dkimpy-0.9.1/dkimpy.egg-info/SOURCES.txt 2018-12-09 19:05:46.000000000 +0000 +++ dkimpy-0.9.6/dkimpy.egg-info/SOURCES.txt 2019-12-24 15:23:27.000000000 +0000 @@ -1,4 +1,5 @@ ChangeLog +LICENSE MANIFEST.in README setup.py @@ -33,6 +34,7 @@ dkim/tests/data/message.mbox dkim/tests/data/rfc6376.msg dkim/tests/data/rfc6376.signed.msg +dkim/tests/data/rfc6376.signed.rsa.msg dkim/tests/data/rfc8032_7_1.key dkim/tests/data/test.message dkim/tests/data/test.private diff -Nru dkimpy-0.9.1/dkimpy.egg-info/entry_points.txt dkimpy-0.9.6/dkimpy.egg-info/entry_points.txt --- dkimpy-0.9.1/dkimpy.egg-info/entry_points.txt 2018-12-09 19:05:45.000000000 +0000 +++ dkimpy-0.9.6/dkimpy.egg-info/entry_points.txt 2019-12-24 15:23:26.000000000 +0000 @@ -1,7 +1,7 @@ [console_scripts] -dkimsign = dkim.dkimsign:main arcsign = dkim.arcsign:main -dknewkey = dkim.dknewkey:main -dkimverify = dkim.dkimverify:main arcverify = dkim.arcverify:main +dkimsign = dkim.dkimsign:main +dkimverify = dkim.dkimverify:main +dknewkey = dkim.dknewkey:main diff -Nru dkimpy-0.9.1/dkimpy.egg-info/requires.txt dkimpy-0.9.6/dkimpy.egg-info/requires.txt --- dkimpy-0.9.1/dkimpy.egg-info/requires.txt 2018-12-09 19:05:45.000000000 +0000 +++ dkimpy-0.9.6/dkimpy.egg-info/requires.txt 2019-12-24 15:23:26.000000000 +0000 @@ -1,11 +1,11 @@ Py3DNS -[testing] +[ARC] authres -pynacl [ed25519] pynacl -[ARC] -authres \ No newline at end of file +[testing] +authres +pynacl diff -Nru dkimpy-0.9.1/man/arcsign.1 dkimpy-0.9.6/man/arcsign.1 --- dkimpy-0.9.1/man/arcsign.1 2018-03-11 22:42:04.000000000 +0000 +++ dkimpy-0.9.6/man/arcsign.1 2019-11-03 17:09:47.000000000 +0000 @@ -127,31 +127,29 @@ .rm #[ #] #H #V #F C .\" ======================================================================== .\" -.IX Title "arcsign 1" -.TH arcsign 1 "2017-01-23" +.TH arcsign 1 "2019-04-14" .SH "NAME" arcsign \- Script for ARC signing messages on stdin .SH "VERSION" -.IX Header "VERSION" -0\.6\.0 +0\.9\.2 .SH "DESCRIPTION" -.IX Header "DESCRIPTION" arcsign is a filter that reads an RFC822 message on standard input, and writes the same message on standard output with a ARC-Signature lines prepended. .SH "USAGE" -.IX Header "USAGE" The signing options are specified on the command line: -arcsign selector domain privatekeyfile +arcsign selector domain privatekeyfile srv_id + +Note: arcsign will only use authentication results header fields with an +authserv-id that matches srv_id as the input fields for the ARC chain. .SH "AUTHORS" -.IX Header "AUTHORS" This version of \fBarcsign\fR was written by Brandon Long and is derived from \fBdkimsign\fR, written by Greg Hewgill . diff -Nru dkimpy-0.9.1/man/arcverify.1 dkimpy-0.9.6/man/arcverify.1 --- dkimpy-0.9.1/man/arcverify.1 2018-03-11 22:42:04.000000000 +0000 +++ dkimpy-0.9.6/man/arcverify.1 2019-11-03 17:09:47.000000000 +0000 @@ -127,25 +127,21 @@ .rm #[ #] #H #V #F C .\" ======================================================================== .\" -.IX Title "dkimverify 1" .TH dkimverify 1 "2017-01-23" .SH "NAME" dkimverify \- Script for DKIM verifying messages on stdin .SH "VERSION" -.IX Header "VERSION" 0\.6\.0 .SH "DESCRIPTION" -.IX Header "DESCRIPTION" dkimverify reads an RFC822 message on standard input, and returns with exit code 0 if the signature verifies successfully. Otherwise, it returns with exit code 1. .SH "AUTHORS" -.IX Header "AUTHORS" This version of \fBarcverify\fR was written by Brandon Long and is derived from \fBdkimverify\fR, written by Greg Hewgill . diff -Nru dkimpy-0.9.1/man/dkimsign.1 dkimpy-0.9.6/man/dkimsign.1 --- dkimpy-0.9.1/man/dkimsign.1 2018-03-11 22:42:04.000000000 +0000 +++ dkimpy-0.9.6/man/dkimsign.1 2019-11-03 17:09:47.000000000 +0000 @@ -127,24 +127,20 @@ .rm #[ #] #H #V #F C .\" ======================================================================== .\" -.IX Title "dkimsign 1" .TH dkimsign 1 "2018-02-05" .SH "NAME" dkimsign \- Script for DKIM signing messages on stdin .SH "VERSION" -.IX Header "VERSION" 0\.7\.0 .SH "DESCRIPTION" -.IX Header "DESCRIPTION" dkimsign is a filter that reads an RFC822 message on standard input, and writes the same message on standard output with a DKIM-Signature line prepended. .SH "USAGE" -.IX Header "USAGE" usage: dkimsign.py [\-h] [\-\-hcanon {simple,relaxed}] [\-\-bcanon {simple,relaxed}] @@ -168,7 +164,6 @@ \-\-identity IDENTITY Optional value for i= tag. .SH "AUTHORS" -.IX Header "AUTHORS" The original version of \fBdkimsign\fR was written by Greg Hewgill . It has been substantially rewritten by Scott Kitterman . diff -Nru dkimpy-0.9.1/man/dkimverify.1 dkimpy-0.9.6/man/dkimverify.1 --- dkimpy-0.9.1/man/dkimverify.1 2018-03-11 22:42:04.000000000 +0000 +++ dkimpy-0.9.6/man/dkimverify.1 2019-11-03 17:09:47.000000000 +0000 @@ -127,25 +127,21 @@ .rm #[ #] #H #V #F C .\" ======================================================================== .\" -.IX Title "dkimverify 1" .TH dkimverify 1 "2017-01-23" .SH "NAME" dkimverify \- Script for DKIM verifying messages on stdin .SH "VERSION" -.IX Header "VERSION" 0\.6\.0 .SH "DESCRIPTION" -.IX Header "DESCRIPTION" dkimverify reads an RFC822 message on standard input, and returns with exit code 0 if the signature verifies successfully. Otherwise, it returns with exit code 1. .SH "AUTHORS" -.IX Header "AUTHORS" This version of \fBdkimverify\fR was written by Greg Hewgill . .PP This man-page was created by Scott Kitterman and is diff -Nru dkimpy-0.9.1/man/dknewkey.1 dkimpy-0.9.6/man/dknewkey.1 --- dkimpy-0.9.1/man/dknewkey.1 2018-03-28 10:49:43.000000000 +0000 +++ dkimpy-0.9.6/man/dknewkey.1 2019-11-03 17:09:47.000000000 +0000 @@ -127,18 +127,15 @@ .rm #[ #] #H #V #F C .\" ======================================================================== .\" -.IX Title "dknewkey 1" .TH dknewkey 1 "2018-02-05" .SH "NAME" dknewkey \- Generates new DKIM public/private key pairs .SH "VERSION" -.IX Header "VERSION" 0\.8\.0 .SH "DESCRIPTION" -.IX Header "DESCRIPTION" dknewykey generates new DKIM keys. @@ -153,7 +150,6 @@ 8301). .SH "USAGE" -.IX Header "USAGE" dknewkey.py [\-h] [\-\-ktype {rsa,ed25519}] key_name @@ -169,7 +165,6 @@ the executable may vary. .SH "AUTHORS" -.IX Header "AUTHORS" This version of \fBdknewkey\fR was written by Brandon Long . It has been substantially rewritten by Scott Kitterman . .PP diff -Nru dkimpy-0.9.1/setup.cfg dkimpy-0.9.6/setup.cfg --- dkimpy-0.9.1/setup.cfg 2018-12-09 19:05:46.000000000 +0000 +++ dkimpy-0.9.6/setup.cfg 2019-12-24 15:23:27.000000000 +0000 @@ -1,5 +1,4 @@ [egg_info] -tag_date = 0 tag_build = -tag_svn_revision = 0 +tag_date = 0 diff -Nru dkimpy-0.9.1/setup.py dkimpy-0.9.6/setup.py --- dkimpy-0.9.1/setup.py 2018-11-10 00:56:31.000000000 +0000 +++ dkimpy-0.9.6/setup.py 2019-12-24 15:21:38.000000000 +0000 @@ -25,7 +25,7 @@ import os import sys -version = "0.9.1" +version = "0.9.6" kw = {} # Work-around for lack of 'or' requires in setuptools. try: