Revert a bunch of rsafp stuff now that DCRUP isn't going to do rsafp
This commit is contained in:
@@ -1,21 +1,14 @@
|
|||||||
UNRELEASED Version 0.7.0
|
UNRELEASED Version 0.7.0
|
||||||
- Updated ARC implementation to support https://tools.ietf.org/html/
|
- Port dkimsign.py to use argparse; now gives standard usage message and
|
||||||
draft-ietf-dmarc-arc-protocol-08
|
|
||||||
- Authres module now required for ARC
|
|
||||||
- Ported dkimsign.py to use argparse; now gives standard usage message and
|
|
||||||
is more extensible
|
is more extensible
|
||||||
- Added command line options to dkimsign.py to select header and body
|
- Add command line options to dkimsign.py to select header and body
|
||||||
canonicalization algorithmns (LP: #1272724)
|
canonicalization algorithmns (LP: #1272724)
|
||||||
- Added command line option to dkimsign.py to select signing algorithm
|
- Add command line option to dkimsign.py to select signing algorithm
|
||||||
- For dknewkey.py made default to include h=sha256 in the DNS record to
|
- For dknewkey.py make default to include h=sha256 in the DNS record to
|
||||||
exclude usage with sha1. Can be overriden.
|
exclude usage with sha1. Can be overriden.
|
||||||
- Updated dknewkey.py to use argparse. Add --ktype option to specify
|
- Update dknewkey.py to use argparse. Add --ktype option to specify
|
||||||
different key type options in anticipation of the DCRUP WG output.
|
different key type options in anticipation of the DCRUP WG output.
|
||||||
- Added generation of rsafp DNS records per
|
- Add generation of rsafp DNS records per draft-ietf-dcrup-dkim-crypto-02
|
||||||
draft-ietf-dcrup-dkim-crypto-02
|
|
||||||
- Added generation of rsafp DKIM signatures per
|
|
||||||
draft-ietf-dcrup-dkim-crypto-02
|
|
||||||
|
|
||||||
2017-05-30 Version 0.6.2
|
2017-05-30 Version 0.6.2
|
||||||
- Fixed problem with header folding that caused the first line to be
|
- Fixed problem with header folding that caused the first line to be
|
||||||
folded too long (Updated test test_add_body_length since l= tag is no
|
folded too long (Updated test test_add_body_length since l= tag is no
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ REQUIREMENTS
|
|||||||
and python 3.1 - 3.3.
|
and python 3.1 - 3.3.
|
||||||
- dnspython or pydns. dnspython is preferred if both are present.
|
- dnspython or pydns. dnspython is preferred if both are present.
|
||||||
- argparse. Standard library in python2.7 and later.
|
- argparse. Standard library in python2.7 and later.
|
||||||
- authres. Needed only for ARC.
|
|
||||||
|
|
||||||
INSTALLATION
|
INSTALLATION
|
||||||
|
|
||||||
@@ -96,8 +95,6 @@ Received Chain):
|
|||||||
|
|
||||||
https://tools.ietf.org/html/draft-ietf-dmarc-arc-protocol-01
|
https://tools.ietf.org/html/draft-ietf-dmarc-arc-protocol-01
|
||||||
|
|
||||||
Updated to support -08 for 0.7.
|
|
||||||
|
|
||||||
This new functionality is marked experimental because the protocol is still
|
This new functionality is marked experimental because the protocol is still
|
||||||
under development. There are no guarantees about API stability or
|
under development. There are no guarantees about API stability or
|
||||||
compatibility.
|
compatibility.
|
||||||
|
|||||||
+23
-99
@@ -38,13 +38,6 @@ import logging
|
|||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# only needed for arc
|
|
||||||
try:
|
|
||||||
from authres import AuthenticationResultsHeader
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
from dkim.canonicalization import (
|
from dkim.canonicalization import (
|
||||||
CanonicalizationPolicy,
|
CanonicalizationPolicy,
|
||||||
InvalidCanonicalizationPolicyError,
|
InvalidCanonicalizationPolicyError,
|
||||||
@@ -56,7 +49,6 @@ from dkim.crypto import (
|
|||||||
HASH_ALGORITHMS,
|
HASH_ALGORITHMS,
|
||||||
parse_pem_private_key,
|
parse_pem_private_key,
|
||||||
parse_public_key,
|
parse_public_key,
|
||||||
get_rsa_pubkey,
|
|
||||||
RSASSA_PKCS1_v1_5_sign,
|
RSASSA_PKCS1_v1_5_sign,
|
||||||
RSASSA_PKCS1_v1_5_verify,
|
RSASSA_PKCS1_v1_5_verify,
|
||||||
UnparsableKeyError,
|
UnparsableKeyError,
|
||||||
@@ -149,10 +141,6 @@ class ValidationError(DKIMException):
|
|||||||
"""Validation error."""
|
"""Validation error."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class AuthresNotFoundError(DKIMException):
|
|
||||||
""" Authres Package not installed, needed for ARC """
|
|
||||||
pass
|
|
||||||
|
|
||||||
def select_headers(headers, include_headers):
|
def select_headers(headers, include_headers):
|
||||||
"""Select message header fields to be signed/verified.
|
"""Select message header fields to be signed/verified.
|
||||||
|
|
||||||
@@ -373,7 +361,7 @@ class DomainSigner(object):
|
|||||||
#: @param logger: a logger to which debug info will be written (default None)
|
#: @param logger: a logger to which debug info will be written (default None)
|
||||||
#: @param signature_algorithm: the signing algorithm to use when signing
|
#: @param signature_algorithm: the signing algorithm to use when signing
|
||||||
def __init__(self,message=None,logger=None,signature_algorithm=b'rsa-sha256',
|
def __init__(self,message=None,logger=None,signature_algorithm=b'rsa-sha256',
|
||||||
minkey=1024,ktype=b'rsa'):
|
minkey=1024):
|
||||||
self.set_message(message)
|
self.set_message(message)
|
||||||
if logger is None:
|
if logger is None:
|
||||||
logger = get_default_logger()
|
logger = get_default_logger()
|
||||||
@@ -394,8 +382,6 @@ class DomainSigner(object):
|
|||||||
#: Minimum public key size. Shorter keys raise KeyFormatError. The
|
#: Minimum public key size. Shorter keys raise KeyFormatError. The
|
||||||
#: default is 1024
|
#: default is 1024
|
||||||
self.minkey = minkey
|
self.minkey = minkey
|
||||||
#: Key type to use. rsa, rsafp (DCRUP)
|
|
||||||
self.ktype = ktype
|
|
||||||
|
|
||||||
#: Header fields to protect from additions by default.
|
#: Header fields to protect from additions by default.
|
||||||
#:
|
#:
|
||||||
@@ -634,16 +620,12 @@ class DKIM(DomainSigner):
|
|||||||
#: @raise DKIMException: when the message, include_headers, or key are badly
|
#: @raise DKIMException: when the message, include_headers, or key are badly
|
||||||
#: formed.
|
#: formed.
|
||||||
def sign(self, selector, domain, privkey, identity=None,
|
def sign(self, selector, domain, privkey, identity=None,
|
||||||
canonicalize=(b'relaxed',b'simple'), include_headers=None,
|
canonicalize=(b'relaxed',b'simple'), include_headers=None, length=False):
|
||||||
length=False):
|
|
||||||
try:
|
try:
|
||||||
pk = parse_pem_private_key(privkey)
|
pk = parse_pem_private_key(privkey)
|
||||||
except UnparsableKeyError as e:
|
except UnparsableKeyError as e:
|
||||||
raise KeyFormatError(str(e))
|
raise KeyFormatError(str(e))
|
||||||
if self.ktype == b'rsafp':
|
|
||||||
pubkey = get_rsa_pubkey(privkey)
|
|
||||||
else:
|
|
||||||
pubkey = False
|
|
||||||
if identity is not None and not identity.endswith(domain):
|
if identity is not None and not identity.endswith(domain):
|
||||||
raise ParameterError("identity must end with domain")
|
raise ParameterError("identity must end with domain")
|
||||||
|
|
||||||
@@ -671,13 +653,10 @@ class DKIM(DomainSigner):
|
|||||||
h = self.hasher()
|
h = self.hasher()
|
||||||
h.update(body)
|
h.update(body)
|
||||||
bodyhash = base64.b64encode(h.digest())
|
bodyhash = base64.b64encode(h.digest())
|
||||||
if self.ktype == b'rsafp':
|
|
||||||
atag = b'rsafp-sha256'
|
|
||||||
else:
|
|
||||||
atag = self.signature_algorithm
|
|
||||||
sigfields = [x for x in [
|
sigfields = [x for x in [
|
||||||
(b'v', b"1"),
|
(b'v', b"1"),
|
||||||
(b'a', atag),
|
(b'a', self.signature_algorithm),
|
||||||
(b'c', canon_policy.to_c_value()),
|
(b'c', canon_policy.to_c_value()),
|
||||||
(b'd', domain),
|
(b'd', domain),
|
||||||
(b'i', identity or b"@"+domain),
|
(b'i', identity or b"@"+domain),
|
||||||
@@ -686,7 +665,6 @@ class DKIM(DomainSigner):
|
|||||||
(b's', selector),
|
(b's', selector),
|
||||||
(b't', str(int(time.time())).encode('ascii')),
|
(b't', str(int(time.time())).encode('ascii')),
|
||||||
(b'h', b" : ".join(include_headers)),
|
(b'h', b" : ".join(include_headers)),
|
||||||
pubkey and (b'k', pubkey),
|
|
||||||
(b'bh', bodyhash),
|
(b'bh', bodyhash),
|
||||||
# Force b= to fold onto it's own line so that refolding after
|
# Force b= to fold onto it's own line so that refolding after
|
||||||
# adding sig doesn't change whitespace for previous tags.
|
# adding sig doesn't change whitespace for previous tags.
|
||||||
@@ -787,59 +765,27 @@ class ARC(DomainSigner):
|
|||||||
#: @param selector: the DKIM selector value for the signature
|
#: @param selector: the DKIM selector value for the signature
|
||||||
#: @param domain: the DKIM domain 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 privkey: a PKCS#1 private key in base64-encoded text form
|
||||||
#: @param srv_id: an srv_id for identitfying AR headers to sign & extract cv from
|
#: @param auth_results: RFC 7601 Authentication-Results header value for the message
|
||||||
|
#: @param chain_validation_status: CV_Pass, CV_Fail, CV_None
|
||||||
#: @param include_headers: a list of strings indicating which headers
|
#: @param include_headers: a list of strings indicating which headers
|
||||||
#: are to be signed (default rfc4871 recommended headers)
|
#: are to be signed (default rfc4871 recommended headers)
|
||||||
#: @return: list of ARC set header fields
|
#: @return: list of ARC set header fields
|
||||||
#: @raise DKIMException: when the message, include_headers, or key are badly
|
#: @raise DKIMException: when the message, include_headers, or key are badly
|
||||||
#: formed.
|
#: formed.
|
||||||
def sign(self, selector, domain, privkey, srv_id, include_headers=None,
|
def sign(self, selector, domain, privkey, auth_results, chain_validation_status,
|
||||||
timestamp=None, standardize=False):
|
include_headers=None, timestamp=None, standardize=False):
|
||||||
|
|
||||||
# check if authres has been imported
|
|
||||||
try:
|
|
||||||
AuthenticationResultsHeader
|
|
||||||
except:
|
|
||||||
self.logger.debug("authres package not installed")
|
|
||||||
raise AuthresNotFoundError
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pk = parse_pem_private_key(privkey)
|
pk = parse_pem_private_key(privkey)
|
||||||
except UnparsableKeyError as e:
|
except UnparsableKeyError as e:
|
||||||
raise KeyFormatError(str(e))
|
raise KeyFormatError(str(e))
|
||||||
|
|
||||||
# extract, parse, filter & group AR headers
|
|
||||||
ar_headers = [res.strip() for [ar, res] in self.headers if ar == b'Authentication-Results']
|
|
||||||
grouped_headers = [(res, AuthenticationResultsHeader.parse('Authentication-Results: ' + res.decode('utf-8')))
|
|
||||||
for res in ar_headers]
|
|
||||||
auth_headers = [res for res in grouped_headers if res[1].authserv_id == srv_id.decode('utf-8')]
|
|
||||||
|
|
||||||
if len(auth_headers) == 0:
|
|
||||||
self.logger.debug("no AR headers found, chain terminated")
|
|
||||||
return b''
|
|
||||||
|
|
||||||
# consolidate headers
|
|
||||||
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)
|
|
||||||
|
|
||||||
# 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 b''
|
|
||||||
elif len(arc_results) != 1:
|
|
||||||
self.logger.debug("multiple AR arc stamps found, failing chain")
|
|
||||||
chain_validation_status = CV_Fail
|
|
||||||
else:
|
|
||||||
chain_validation_status = arc_results[0].result.lower().encode('utf-8')
|
|
||||||
|
|
||||||
# Setup headers
|
# Setup headers
|
||||||
if include_headers is None:
|
if include_headers is None:
|
||||||
include_headers = self.default_sign_headers()
|
include_headers = self.default_sign_headers()
|
||||||
|
|
||||||
|
if b'arc-authentication-results' not in include_headers:
|
||||||
|
include_headers.append(b'arc-authentication-results')
|
||||||
|
|
||||||
include_headers = tuple([x.lower() for x in include_headers])
|
include_headers = tuple([x.lower() for x in include_headers])
|
||||||
|
|
||||||
# record what verify should extract
|
# record what verify should extract
|
||||||
@@ -865,10 +811,7 @@ class ARC(DomainSigner):
|
|||||||
raise ParameterError("cv=none not allowed on instance %d" % instance)
|
raise ParameterError("cv=none not allowed on instance %d" % instance)
|
||||||
|
|
||||||
new_arc_set = []
|
new_arc_set = []
|
||||||
if chain_validation_status != CV_Fail:
|
|
||||||
arc_headers = [y for x,y in arc_headers_w_instance]
|
arc_headers = [y for x,y in arc_headers_w_instance]
|
||||||
else: # don't include previous sets for a failed/invalid chain
|
|
||||||
arc_headers = []
|
|
||||||
|
|
||||||
# Compute ARC-Authentication-Results
|
# Compute ARC-Authentication-Results
|
||||||
aar_value = ("i=%d; " % instance).encode('utf-8') + auth_results
|
aar_value = ("i=%d; " % instance).encode('utf-8') + auth_results
|
||||||
@@ -926,11 +869,6 @@ class ARC(DomainSigner):
|
|||||||
as_include_headers = [x[0].lower() for x in arc_headers]
|
as_include_headers = [x[0].lower() for x in arc_headers]
|
||||||
as_include_headers.reverse()
|
as_include_headers.reverse()
|
||||||
|
|
||||||
# if our chain is failing or invalid, we only grab the most recent set
|
|
||||||
# reversing the order of the headers accomplishes this
|
|
||||||
if chain_validation_status == CV_Fail:
|
|
||||||
self.headers.reverse()
|
|
||||||
|
|
||||||
res = self.gen_header(as_fields, as_include_headers, canon_policy,
|
res = self.gen_header(as_fields, as_include_headers, canon_policy,
|
||||||
b"ARC-Seal", pk, standardize)
|
b"ARC-Seal", pk, standardize)
|
||||||
|
|
||||||
@@ -949,7 +887,7 @@ class ARC(DomainSigner):
|
|||||||
#: @param dnsfunc: an optional function to lookup TXT resource records
|
#: @param dnsfunc: an optional function to lookup TXT resource records
|
||||||
#: for a DNS domain. The default uses dnspython or pydns.
|
#: for a DNS domain. The default uses dnspython or pydns.
|
||||||
#: @return: True if signature verifies or False otherwise
|
#: @return: True if signature verifies or False otherwise
|
||||||
#: @return: three-tuple of (CV Result (CV_Pass, CV_Fail, CV_None or None, for a chain that has ended), list of
|
#: @return: three-tuple of (CV Result (CV_Pass, CV_Fail or CV_None), list of
|
||||||
#: result dictionaries, result reason)
|
#: result dictionaries, result reason)
|
||||||
#: @raise DKIMException: when the message, signature, or key are badly formed
|
#: @raise DKIMException: when the message, signature, or key are badly formed
|
||||||
def verify(self,dnsfunc=get_txt):
|
def verify(self,dnsfunc=get_txt):
|
||||||
@@ -969,10 +907,10 @@ class ARC(DomainSigner):
|
|||||||
if not result_data[0]['ams-valid']:
|
if not result_data[0]['ams-valid']:
|
||||||
return CV_Fail, result_data, "Most recent ARC-Message-Signature did not validate"
|
return CV_Fail, result_data, "Most recent ARC-Message-Signature did not validate"
|
||||||
for result in result_data:
|
for result in result_data:
|
||||||
if result['cv'] == CV_Fail:
|
if not result['as-valid']:
|
||||||
return None, result_data, "ARC-Seal[%d] reported failure, the chain is terminated" % result['instance']
|
|
||||||
elif not result['as-valid']:
|
|
||||||
return CV_Fail, result_data, "ARC-Seal[%d] did not validate" % result['instance']
|
return CV_Fail, result_data, "ARC-Seal[%d] did not validate" % result['instance']
|
||||||
|
if result['cv'] == CV_Fail:
|
||||||
|
return CV_Fail, result_data, "ARC-Seal[%d] reported failure" % result['instance']
|
||||||
elif (result['instance'] == 1) and (result['cv'] != CV_None):
|
elif (result['instance'] == 1) and (result['cv'] != CV_None):
|
||||||
return CV_Fail, result_data, "ARC-Seal[%d] reported invalid status %s" % (result['instance'], result['cv'])
|
return CV_Fail, result_data, "ARC-Seal[%d] reported invalid status %s" % (result['instance'], result['cv'])
|
||||||
elif (result['instance'] != 1) and (result['cv'] == CV_None):
|
elif (result['instance'] != 1) and (result['cv'] == CV_None):
|
||||||
@@ -1039,18 +977,7 @@ class ARC(DomainSigner):
|
|||||||
raise ParameterError("The Arc-Message-Signature MUST NOT sign ARC-Seal")
|
raise ParameterError("The Arc-Message-Signature MUST NOT sign ARC-Seal")
|
||||||
|
|
||||||
ams_header = (b'ARC-Message-Signature', b' ' + ams_value)
|
ams_header = (b'ARC-Message-Signature', b' ' + ams_value)
|
||||||
|
ams_valid = self.verify_sig(sig, include_headers, ams_header, dnsfunc)
|
||||||
|
|
||||||
# we can't use the AMS provided above, as it's already been canonicalized relaxed
|
|
||||||
# for use in validating the AS. However the AMS is included in the AMS itself,
|
|
||||||
# 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]
|
|
||||||
|
|
||||||
try:
|
|
||||||
ams_valid = self.verify_sig(sig, include_headers, raw_ams_header, dnsfunc)
|
|
||||||
except DKIMException as e:
|
|
||||||
self.logger.error("%s" % e)
|
|
||||||
ams_valid = False
|
|
||||||
|
|
||||||
output['ams-valid'] = ams_valid
|
output['ams-valid'] = ams_valid
|
||||||
self.logger.debug("ams valid: %r" % ams_valid)
|
self.logger.debug("ams valid: %r" % ams_valid)
|
||||||
@@ -1071,11 +998,7 @@ class ARC(DomainSigner):
|
|||||||
as_include_headers = [x[0].lower() for x in arc_headers]
|
as_include_headers = [x[0].lower() for x in arc_headers]
|
||||||
as_include_headers.reverse()
|
as_include_headers.reverse()
|
||||||
as_header = (b'ARC-Seal', b' ' + as_value)
|
as_header = (b'ARC-Seal', b' ' + as_value)
|
||||||
try:
|
|
||||||
as_valid = self.verify_sig(sig, as_include_headers[:-1], as_header, dnsfunc)
|
as_valid = self.verify_sig(sig, as_include_headers[:-1], as_header, dnsfunc)
|
||||||
except DKIMException as e:
|
|
||||||
self.logger.error("%s" % e)
|
|
||||||
as_valid = False
|
|
||||||
|
|
||||||
output['as-valid'] = as_valid
|
output['as-valid'] = as_valid
|
||||||
self.logger.debug("as valid: %r" % as_valid)
|
self.logger.debug("as valid: %r" % as_valid)
|
||||||
@@ -1122,7 +1045,8 @@ dkim_sign = sign
|
|||||||
dkim_verify = verify
|
dkim_verify = verify
|
||||||
|
|
||||||
def arc_sign(message, selector, domain, privkey,
|
def arc_sign(message, selector, domain, privkey,
|
||||||
srv_id, signature_algorithm=b'rsa-sha256',
|
auth_results, chain_validation_status,
|
||||||
|
signature_algorithm=b'rsa-sha256',
|
||||||
include_headers=None, timestamp=None,
|
include_headers=None, timestamp=None,
|
||||||
logger=None, standardize=False):
|
logger=None, standardize=False):
|
||||||
"""Sign an RFC822 message and return the ARC set header lines for the next instance
|
"""Sign an RFC822 message and return the ARC set header lines for the next instance
|
||||||
@@ -1130,19 +1054,19 @@ def arc_sign(message, selector, domain, privkey,
|
|||||||
@param selector: the DKIM selector value for the signature
|
@param selector: the DKIM selector value for the signature
|
||||||
@param domain: the DKIM domain 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 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 auth_results: the RFC 7601 authentication-results header field value for this instance
|
||||||
|
@param chain_validation_status: the validation status of the existing chain on the message (P (pass), F (fail)) or N (none) for no existing chain
|
||||||
@param signature_algorithm: the signing algorithm to use when signing
|
@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 include_headers: a list of strings indicating which headers are to be signed (default all headers not listed as SHOULD NOT sign)
|
||||||
@param logger: a logger to which debug info will be written (default None)
|
@param logger: a logger to which debug info will be written (default None)
|
||||||
@return: A list containing the ARC set of header fields for the next instance
|
@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.
|
@raise DKIMException: when the message, include_headers, or key are badly formed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
a = ARC(message,logger=logger,signature_algorithm=signature_algorithm)
|
a = ARC(message,logger=logger,signature_algorithm=signature_algorithm)
|
||||||
if not include_headers:
|
if not include_headers:
|
||||||
include_headers = a.default_sign_headers()
|
include_headers = a.default_sign_headers()
|
||||||
return a.sign(selector, domain, privkey, srv_id, include_headers=include_headers,
|
return a.sign(selector, domain, privkey, auth_results, chain_validation_status,
|
||||||
timestamp=timestamp, standardize=standardize)
|
include_headers=include_headers, timestamp=timestamp, standardize=standardize)
|
||||||
|
|
||||||
def arc_verify(message, logger=None, dnsfunc=get_txt, minkey=1024):
|
def arc_verify(message, logger=None, dnsfunc=get_txt, minkey=1024):
|
||||||
"""Verify the ARC chain on an RFC822 formatted message.
|
"""Verify the ARC chain on an RFC822 formatted message.
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ __all__ = [
|
|||||||
'parse_pem_private_key',
|
'parse_pem_private_key',
|
||||||
'parse_private_key',
|
'parse_private_key',
|
||||||
'parse_public_key',
|
'parse_public_key',
|
||||||
'get_rsa_pubkey',
|
|
||||||
'RSASSA_PKCS1_v1_5_sign',
|
'RSASSA_PKCS1_v1_5_sign',
|
||||||
'RSASSA_PKCS1_v1_5_verify',
|
'RSASSA_PKCS1_v1_5_verify',
|
||||||
'UnparsableKeyError',
|
'UnparsableKeyError',
|
||||||
@@ -34,7 +33,6 @@ __all__ = [
|
|||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
import re
|
import re
|
||||||
import rsa
|
|
||||||
|
|
||||||
from dkim.asn1 import (
|
from dkim.asn1 import (
|
||||||
ASN1FormatError,
|
ASN1FormatError,
|
||||||
@@ -235,14 +233,6 @@ def rsa_decrypt(message, pk, mlen):
|
|||||||
|
|
||||||
return int2str(m2 + h * pk['prime2'], mlen)
|
return int2str(m2 + h * pk['prime2'], mlen)
|
||||||
|
|
||||||
def get_rsa_pubkey(privkey):
|
|
||||||
"""Extract RSA public key from PEM encoded RSA private key for use with
|
|
||||||
rsafp. Returns base64 encoded public key suitable for use in DKIM key
|
|
||||||
records. Using python-rsa instead of making a stack of custom code.
|
|
||||||
@since: 0.7"""
|
|
||||||
pkobj = rsa.PrivateKey.load_pkcs1(privkey, 'PEM')
|
|
||||||
pubobj = rsa.key.PublicKey(pkobj.n, pkobj.e)
|
|
||||||
return(base64.b64encode(rsa.PublicKey.save_pkcs1(pubobj, 'DER')))
|
|
||||||
|
|
||||||
def rsa_encrypt(message, pk, mlen):
|
def rsa_encrypt(message, pk, mlen):
|
||||||
"""Perform RSA encryption/verification
|
"""Perform RSA encryption/verification
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ def test_suite():
|
|||||||
test_canonicalization,
|
test_canonicalization,
|
||||||
test_crypto,
|
test_crypto,
|
||||||
test_dkim,
|
test_dkim,
|
||||||
test_rsafp,
|
|
||||||
test_util,
|
test_util,
|
||||||
test_arc,
|
test_arc,
|
||||||
test_dnsplug,
|
test_dnsplug,
|
||||||
@@ -37,7 +36,6 @@ def test_suite():
|
|||||||
test_canonicalization,
|
test_canonicalization,
|
||||||
test_crypto,
|
test_crypto,
|
||||||
test_dkim,
|
test_dkim,
|
||||||
test_rsafp,
|
|
||||||
test_util,
|
test_util,
|
||||||
test_arc,
|
test_arc,
|
||||||
test_dnsplug,
|
test_dnsplug,
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
k=rsafp; p=VO1v0Ybphw9AlrLvHB2ly/x6RD/1zJxEhYeWT/v/RtY=
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIIEpAIBAAKCAQEA0EGLyxVqSMvpQ/THu4XH+0sbqQrnGRu36/TBcsJc7p5vGKIY
|
|
||||||
f7lfAiaxceGGVX1NzBoE8SEqmLlNuRtdHSsnItC82m5hGDhTec8ZcGonfnOCPJvd
|
|
||||||
Dou8mDXhtX9knES4PC/Uup3HL84RnsXZ0GWpUtB+fIsHlZryFjsTJtOZVdJSH5H5
|
|
||||||
B0arJCitLVJqPqF5bluIVuXmwXD2Wi0hQkpmSm+SJJQ1efTkpK2UYkJvu8cMF7aA
|
|
||||||
7Lgxzimatqg4oDg9ED5VCHFz82wacjNrnQRnezeOcWF8q76dfCDo5MLUO+CWChFC
|
|
||||||
H897B6kjMmCo3IavLTHB8Dc7q70I8lxn2qAZzwIDAQABAoIBAQCU+R5oExSpqHxg
|
|
||||||
KV0rbqLEx1CtFuiD1Ik32Cj9z72s0FcGlwXRcChfoJA0t7BhvTYGt+IrH5+aCoxn
|
|
||||||
ywRL1k+znwBJfMYmDjzgmN5IxRclZLmYY6K5QgqSE5E3RT8SbjIgk9KoAC+5qb5/
|
|
||||||
BmcupVp/rDefHdE6GtHsRywHDTzV30f8zXb/51w30FkcSQS85v8W4VbyqpGcAgq1
|
|
||||||
ah1gEuSd7HtxQ4HWyvFiMdw+0bBh8gt14Dh3nq8i6tDhlPtykbM4etm16fcpl5cz
|
|
||||||
DOg5srIOKxW4vGOng68v6KQ/gz8/BQMAPbZHgFd5l2grejC9p27hwTYer909AfEO
|
|
||||||
EUcn5oJhAoGBAPVoqvcqA4AL3G46SrYeAnXz/bpaPiBhKat1YgnPQc+5cuGauJIN
|
|
||||||
YtiCv5xza1u8Gc5rRfEn/41mSpDAXcix0d95qpUAToNVRJUA23e28EoKGL8rWaXF
|
|
||||||
lc2IecGNI7HF7XL0V+BF59dAPH4q44mKval6U/wTo0hDAFfBnRy0V03RAoGBANk+
|
|
||||||
Z9G7dYaQpw/nCxVouCeGOX4LECt4m4Y+LIhFPqI8mnTM8S97dnySW3OpafwF6RIk
|
|
||||||
UDMzXSJc6BPOMEk9WVI+1ztKXssjbvVa3fSHZEBMP9d1xFJw4/SRSp3uFjGzLa4u
|
|
||||||
pea8DFRz5jBR2uyGh0+/1E9v+hem7WD6pwJGwrWfAoGAVeiEIO4GN6bvTW7+hG5Q
|
|
||||||
8jWtlrTMls0spyb6YPC62xrSSoO9JPOmrKBorz4AUSax6f7Hhzo3TaqHY9DTg9Qr
|
|
||||||
4g1XV725vmP1FCwup+PUjjamnxVv1oYqgL7L0KO14R+mld1PfeU62bFU+93LtXRq
|
|
||||||
HJAEInRFbqB12EKg21GOVmECgYA6Lox/j0UalQMpLye4xCMN8tTS/QOHoPWGLoCe
|
|
||||||
vmzX5oM3ZOvzW42QL2Jux6Cq7qpNQYx5Kfh3i4pcQ4yLEPMrI8lhB7n7jbHZ5Ewt
|
|
||||||
gVVIIyO2AMRjj/h9N6xUP7+R7/r7+2JTOWnT9HZh2opXbnAu26Fw5PyF+R76Kicw
|
|
||||||
ZOMg4QKBgQDMAJ9nENCiZw7CkYRm+z5UoMeSR3jwEPj1gK3KKhIYWeOw99i2XmBt
|
|
||||||
1aN0UgAeEidleguqeZWxVXJkdVqvxfAPqpPfhGneN5GgEh2gOfFTDrvHhqoMblsw
|
|
||||||
uuNlIixfy4w1ENApV2n7qcBzptMHV2uPDUHCctAZ9+ACg7nkiJEaCw==
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0EGLyxVqSMvpQ/THu4XH+0sbqQrnGRu36/TBcsJc7p5vGKIYf7lfAiaxceGGVX1NzBoE8SEqmLlNuRtdHSsnItC82m5hGDhTec8ZcGonfnOCPJvdDou8mDXhtX9knES4PC/Uup3HL84RnsXZ0GWpUtB+fIsHlZryFjsTJtOZVdJSH5H5B0arJCitLVJqPqF5bluIVuXmwXD2Wi0hQkpmSm+SJJQ1efTkpK2UYkJvu8cMF7aA7Lgxzimatqg4oDg9ED5VCHFz82wacjNrnQRnezeOcWF8q76dfCDo5MLUO+CWChFCH897B6kjMmCo3IavLTHB8Dc7q70I8lxn2qAZzwIDAQAB
|
|
||||||
+1
-5
@@ -47,9 +47,6 @@ parser.add_argument('--bcanon', choices=['simple', 'relaxed'],
|
|||||||
parser.add_argument('--signalg', choices=['rsa-sha256', 'rsa-sha1'],
|
parser.add_argument('--signalg', choices=['rsa-sha256', 'rsa-sha1'],
|
||||||
default='rsa-sha256',
|
default='rsa-sha256',
|
||||||
help='Signature algorithm: default=rsa-sha256')
|
help='Signature algorithm: default=rsa-sha256')
|
||||||
parser.add_argument('--ktype', choices=['rsa', 'rsafp'],
|
|
||||||
default='rsa',
|
|
||||||
help='DKIM key type: Default is rsa')
|
|
||||||
parser.add_argument('--identity', help='Optional value for i= tag.')
|
parser.add_argument('--identity', help='Optional value for i= tag.')
|
||||||
args=parser.parse_args(arguments)
|
args=parser.parse_args(arguments)
|
||||||
include_headers = None
|
include_headers = None
|
||||||
@@ -64,7 +61,6 @@ if sys.version_info[0] >= 3:
|
|||||||
args.hcanon = bytes(args.hcanon, encoding='UTF-8')
|
args.hcanon = bytes(args.hcanon, encoding='UTF-8')
|
||||||
args.bcanon = bytes(args.bcanon, encoding='UTF-8')
|
args.bcanon = bytes(args.bcanon, encoding='UTF-8')
|
||||||
args.signalg = bytes(args.signalg, encoding='UTF-8')
|
args.signalg = bytes(args.signalg, encoding='UTF-8')
|
||||||
args.ktype = bytes(args.ktype, encoding='UTF-8')
|
|
||||||
# Make sys.stdin and stdout binary streams.
|
# Make sys.stdin and stdout binary streams.
|
||||||
sys.stdin = sys.stdin.detach()
|
sys.stdin = sys.stdin.detach()
|
||||||
sys.stdout = sys.stdout.detach()
|
sys.stdout = sys.stdout.detach()
|
||||||
@@ -73,7 +69,7 @@ canonicalize = (args.hcanon, args.bcanon)
|
|||||||
message = sys.stdin.read()
|
message = sys.stdin.read()
|
||||||
try:
|
try:
|
||||||
d = dkim.DKIM(message,logger=logger,
|
d = dkim.DKIM(message,logger=logger,
|
||||||
signature_algorithm=args.signalg, ktype=args.ktype)
|
signature_algorithm=args.signalg)
|
||||||
sig = d.sign(args.selector, args.domain, open(
|
sig = d.sign(args.selector, args.domain, open(
|
||||||
args.privatekeyfile, "rb").read(), identity = args.identity,
|
args.privatekeyfile, "rb").read(), identity = args.identity,
|
||||||
canonicalize=canonicalize, include_headers=include_headers,
|
canonicalize=canonicalize, include_headers=include_headers,
|
||||||
|
|||||||
Reference in New Issue
Block a user