[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#947321: buster-pu: package dkimpy/0.9.1-1



Package: release.debian.org
Severity: normal
Tags: buster
User: release.debian.org@packages.debian.org
Usertags: pu

This proposed update includes fixes for all user reported bugs since the
last version available before buster froze.  These are all issues
reported by actual package users (not all on Debian).  The two most
imoprtant issues are:

 - Updating the ARC (Authenticated Receive Chain) code to match the
   final version of the spec.  RFC 8617 is published not, so it is
   highly unlikely to change again during Buster's lifetime.

 - Ignoring unknown service types: Since 0.9.1 was released, a new DKIM
   service type, TLSRPT, has been defined and is starting to see use.
   RFC 6376 (DKIM RFC) always required unknown service types to be
   ignored, but since there had only ever been one, that check was never
   implemented.  This change will prevent mis-processing of TLSRPT
   service signatures.

Some of the fixes (like the pydns CNAME processing issue, for example)
don't directly affect dkimpy as packaged in Debian, but I would prefer
to use the upstream bugfix release without modification to minimize risk
I make a mistake when extracting changes.

I have the package ready to go.  I've run the autopkgtest in a Buster
chroot and it passes.  All these fixes are in version 1.0.1 in Testing.

Thanks.  Full debian/changelog with upstream changelog entries follows:

  * 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 K
diff -Nru dkimpy-0.9.1/ChangeLog dkimpy-0.9.6/ChangeLog
--- dkimpy-0.9.1/ChangeLog	2018-12-09 14:04:26.000000000 -0500
+++ dkimpy-0.9.6/ChangeLog	2019-12-24 10:22:40.000000000 -0500
@@ -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/debian/changelog dkimpy-0.9.6/debian/changelog
--- dkimpy-0.9.1/debian/changelog	2018-12-09 15:33:28.000000000 -0500
+++ dkimpy-0.9.6/debian/changelog	2019-12-24 10:41:22.000000000 -0500
@@ -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 <scott@kitterman.com>  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 01:20:55.000000000 -0500
+++ dkimpy-0.9.6/debian/gbp.conf	2019-12-24 10:39:42.000000000 -0500
@@ -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 15:28:17.000000000 -0500
+++ dkimpy-0.9.6/debian/watch	2019-12-24 10:32:40.000000000 -0500
@@ -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/arcsign.py dkimpy-0.9.6/dkim/arcsign.py
--- dkimpy-0.9.1/dkim/arcsign.py	2018-10-27 20:48:51.000000000 -0400
+++ dkimpy-0.9.6/dkim/arcsign.py	2019-11-03 12:09:47.000000000 -0500
@@ -24,6 +24,13 @@
 # This has been modified from the original software.
 # Copyright (c) 2016 Google, Inc.
 # Contact: Brandon Long <blong@google.com>
+# This has been modified from the original software.
+# Copyright (c) 2017, 2018, 2019 Scott Kitterman <scott@kitterman.com>
+#
+# This has been modified from the original software.
+# Copyright (c) 2017 Valimail Inc
+# Contact: Gene Shuman <gene@valimail.com>
+
 
 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 19:42:01.000000000 -0400
+++ dkimpy-0.9.6/dkim/canonicalization.py	2019-11-03 12:09:47.000000000 -0500
@@ -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-27 20:48:51.000000000 -0400
+++ dkimpy-0.9.6/dkim/dkimsign.py	2019-11-03 12:09:47.000000000 -0500
@@ -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-29 21:52:26.000000000 -0400
+++ dkimpy-0.9.6/dkim/dnsplug.py	2019-12-24 10:21:38.000000000 -0500
@@ -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/__init__.py dkimpy-0.9.6/dkim/__init__.py
--- dkimpy-0.9.1/dkim/__init__.py	2018-11-09 18:30:35.000000000 -0500
+++ dkimpy-0.9.6/dkim/__init__.py	2019-12-24 10:21:38.000000000 -0500
@@ -24,7 +24,7 @@
 # Contact: Brandon Long <blong@google.com>
 #
 # This has been modified from the original software.
-# Copyright (c) 2016, 2017, 2018 Scott Kitterman <scott@kitterman.com>
+# Copyright (c) 2016, 2017, 2018, 2019 Scott Kitterman <scott@kitterman.com>
 #
 # 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/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	1969-12-31 19:00:00.000000000 -0500
+++ dkimpy-0.9.6/dkim/tests/data/rfc6376.signed.rsa.msg	2019-12-15 02:33:19.000000000 -0500
@@ -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 <joe@football.example.com>
+To: Suzie Q <suzie@shopping.example.net>
+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-09 19:56:17.000000000 -0500
+++ dkimpy-0.9.6/dkim/tests/test_arc.py	2019-12-24 10:21:38.000000000 -0500
@@ -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-27 20:48:51.000000000 -0400
+++ dkimpy-0.9.6/dkim/tests/test_dkim.py	2019-12-24 10:21:38.000000000 -0500
@@ -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 17:28:10.000000000 -0400
+++ dkimpy-0.9.6/dkim/tests/test_util.py	2019-11-03 12:09:47.000000000 -0500
@@ -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 18:42:04.000000000 -0400
+++ dkimpy-0.9.6/dkim/util.py	2019-11-03 12:09:47.000000000 -0500
@@ -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/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 14:05:45.000000000 -0500
+++ dkimpy-0.9.6/dkimpy.egg-info/entry_points.txt	2019-12-24 10:23:26.000000000 -0500
@@ -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/PKG-INFO dkimpy-0.9.6/dkimpy.egg-info/PKG-INFO
--- dkimpy-0.9.1/dkimpy.egg-info/PKG-INFO	2018-12-09 14:05:45.000000000 -0500
+++ dkimpy-0.9.6/dkimpy.egg-info/PKG-INFO	2019-12-24 10:23:26.000000000 -0500
@@ -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/requires.txt dkimpy-0.9.6/dkimpy.egg-info/requires.txt
--- dkimpy-0.9.1/dkimpy.egg-info/requires.txt	2018-12-09 14:05:45.000000000 -0500
+++ dkimpy-0.9.6/dkimpy.egg-info/requires.txt	2019-12-24 10:23:26.000000000 -0500
@@ -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/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 14:05:46.000000000 -0500
+++ dkimpy-0.9.6/dkimpy.egg-info/SOURCES.txt	2019-12-24 10:23:27.000000000 -0500
@@ -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/LICENSE dkimpy-0.9.6/LICENSE
--- dkimpy-0.9.1/LICENSE	1969-12-31 19:00:00.000000000 -0500
+++ dkimpy-0.9.6/LICENSE	2019-11-03 12:09:47.000000000 -0500
@@ -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/man/arcsign.1 dkimpy-0.9.6/man/arcsign.1
--- dkimpy-0.9.1/man/arcsign.1	2018-03-11 18:42:04.000000000 -0400
+++ dkimpy-0.9.6/man/arcsign.1	2019-11-03 12:09:47.000000000 -0500
@@ -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 <blong@google.com>
 and is derived from \fBdkimsign\fR, written by Greg Hewgill
 <greg@hewgill.com>.
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 18:42:04.000000000 -0400
+++ dkimpy-0.9.6/man/arcverify.1	2019-11-03 12:09:47.000000000 -0500
@@ -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 <blong@google.com>
 and is derived from \fBdkimverify\fR, written by Greg Hewgill
 <greg@hewgill.com>.
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 18:42:04.000000000 -0400
+++ dkimpy-0.9.6/man/dkimsign.1	2019-11-03 12:09:47.000000000 -0500
@@ -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
 <greg@hewgill.com>.  It has been substantially rewritten by Scott Kitterman
 <scott@kitterman.com>.
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 18:42:04.000000000 -0400
+++ dkimpy-0.9.6/man/dkimverify.1	2019-11-03 12:09:47.000000000 -0500
@@ -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 <greg@hewgill.com>.
 .PP
 This man-page was created by Scott Kitterman <scott@kitterman.com> 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 06:49:43.000000000 -0400
+++ dkimpy-0.9.6/man/dknewkey.1	2019-11-03 12:09:47.000000000 -0500
@@ -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 <blong@google.com>.
 It has been substantially rewritten by Scott Kitterman <scott@kitterman.com>.
 .PP
diff -Nru dkimpy-0.9.1/MANIFEST.in dkimpy-0.9.6/MANIFEST.in
--- dkimpy-0.9.1/MANIFEST.in	2018-03-25 22:37:59.000000000 -0400
+++ dkimpy-0.9.6/MANIFEST.in	2019-12-24 10:21:38.000000000 -0500
@@ -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 14:05:46.000000000 -0500
+++ dkimpy-0.9.6/PKG-INFO	2019-12-24 10:23:27.000000000 -0500
@@ -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 11:01:24.000000000 -0400
+++ dkimpy-0.9.6/README	2019-12-24 10:21:38.000000000 -0500
@@ -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/setup.cfg dkimpy-0.9.6/setup.cfg
--- dkimpy-0.9.1/setup.cfg	2018-12-09 14:05:46.000000000 -0500
+++ dkimpy-0.9.6/setup.cfg	2019-12-24 10:23:27.000000000 -0500
@@ -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-09 19:56:31.000000000 -0500
+++ dkimpy-0.9.6/setup.py	2019-12-24 10:21:38.000000000 -0500
@@ -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:

Reply to: