From 6f715a1a017aa0f9a41be16699cbf166d53b6df3 Mon Sep 17 00:00:00 2001 From: William Grant Date: Wed, 9 Mar 2011 23:27:04 +1100 Subject: [PATCH] Extract crypto stuff to dkim.crypto. --- dkim/__init__.py | 88 ++----------------------------------- dkim/crypto.py | 110 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 84 deletions(-) create mode 100644 dkim/crypto.py diff --git a/dkim/__init__.py b/dkim/__init__.py index efc1782..3cc88cc 100644 --- a/dkim/__init__.py +++ b/dkim/__init__.py @@ -5,15 +5,10 @@ import time import dns.resolver -from dkim.asn1 import ( - asn1_build, - asn1_parse, - BIT_STRING, - INTEGER, - SEQUENCE, - OBJECT_IDENTIFIER, - OCTET_STRING, - NULL, +from dkim.crypto import ( + EMSA_PKCS1_v1_5_encode, + parse_private_key, + parse_public_key, ) __all__ = [ @@ -87,22 +82,6 @@ def _remove(s, t): assert i >= 0 return s[:i] + s[i+len(t):] - -def EMSA_PKCS1_v1_5_encode(digest, modlen, hashid): - dinfo = asn1_build( - (SEQUENCE, [ - (SEQUENCE, [ - (OBJECT_IDENTIFIER, hashid), - (NULL, None), - ]), - (OCTET_STRING, digest), - ]), - ) - if len(dinfo)+3 > modlen: - raise ParameterError("Hash too large for modulus") - return "\x00\x01"+"\xff"*(modlen-len(dinfo)-3)+"\x00"+dinfo - - def hash_headers(hasher, canonicalize_headers, headers, include_headers, sigheaders, sig): sign_headers = [] @@ -126,33 +105,6 @@ def hash_headers(hasher, canonicalize_headers, headers, include_headers, hasher.update(x[1]) -def parse_public_key(data): - x = asn1_parse(ASN1_Object, data) - # Not sure why the [1:] is necessary to skip a byte. - pkd = asn1_parse(ASN1_RSAPublicKey, x[0][1][1:]) - pk = { - 'modulus': pkd[0][0], - 'publicExponent': pkd[0][1], - } - return pk - - -def parse_private_key(data): - pka = asn1_parse(ASN1_RSAPrivateKey, data) - pk = { - 'version': pka[0][0], - 'modulus': pka[0][1], - 'publicExponent': pka[0][2], - 'privateExponent': pka[0][3], - 'prime1': pka[0][4], - 'prime2': pka[0][5], - 'exponent1': pka[0][6], - 'exponent2': pka[0][7], - 'coefficient': pka[0][8], - } - return pk - - def validate_signature_fields(sig, debuglog=None): mandatory_fields = ('v', 'a', 'b', 'bh', 'd', 'h', 's') for field in mandatory_fields: @@ -200,38 +152,6 @@ def validate_signature_fields(sig, debuglog=None): return False return True - -ASN1_Object = [ - (SEQUENCE, [ - (SEQUENCE, [ - (OBJECT_IDENTIFIER,), - (NULL,), - ]), - (BIT_STRING,), - ]) -] - -ASN1_RSAPublicKey = [ - (SEQUENCE, [ - (INTEGER,), - (INTEGER,), - ]) -] - -ASN1_RSAPrivateKey = [ - (SEQUENCE, [ - (INTEGER,), - (INTEGER,), - (INTEGER,), - (INTEGER,), - (INTEGER,), - (INTEGER,), - (INTEGER,), - (INTEGER,), - (INTEGER,), - ]) -] - # These values come from RFC 3447, section 9.2 Notes, page 43. HASHID_SHA1 = "\x2b\x0e\x03\x02\x1a" HASHID_SHA256 = "\x60\x86\x48\x01\x65\x03\x04\x02\x01" diff --git a/dkim/crypto.py b/dkim/crypto.py new file mode 100644 index 0000000..217318d --- /dev/null +++ b/dkim/crypto.py @@ -0,0 +1,110 @@ +# 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 +# Copyright (c) 2011 William Grant + +__all__ = [ + 'EMSA_PKCS1_v1_5_encode', + 'parse_private_key', + 'parse_public_key', + ] + +from dkim.asn1 import ( + asn1_build, + asn1_parse, + BIT_STRING, + INTEGER, + SEQUENCE, + OBJECT_IDENTIFIER, + OCTET_STRING, + NULL, + ) + + +ASN1_Object = [ + (SEQUENCE, [ + (SEQUENCE, [ + (OBJECT_IDENTIFIER,), + (NULL,), + ]), + (BIT_STRING,), + ]) +] + +ASN1_RSAPublicKey = [ + (SEQUENCE, [ + (INTEGER,), + (INTEGER,), + ]) +] + +ASN1_RSAPrivateKey = [ + (SEQUENCE, [ + (INTEGER,), + (INTEGER,), + (INTEGER,), + (INTEGER,), + (INTEGER,), + (INTEGER,), + (INTEGER,), + (INTEGER,), + (INTEGER,), + ]) +] + + +def parse_public_key(data): + x = asn1_parse(ASN1_Object, data) + # Not sure why the [1:] is necessary to skip a byte. + pkd = asn1_parse(ASN1_RSAPublicKey, x[0][1][1:]) + pk = { + 'modulus': pkd[0][0], + 'publicExponent': pkd[0][1], + } + return pk + + +def parse_private_key(data): + pka = asn1_parse(ASN1_RSAPrivateKey, data) + pk = { + 'version': pka[0][0], + 'modulus': pka[0][1], + 'publicExponent': pka[0][2], + 'privateExponent': pka[0][3], + 'prime1': pka[0][4], + 'prime2': pka[0][5], + 'exponent1': pka[0][6], + 'exponent2': pka[0][7], + 'coefficient': pka[0][8], + } + return pk + + +def EMSA_PKCS1_v1_5_encode(digest, modlen, hashid): + dinfo = asn1_build( + (SEQUENCE, [ + (SEQUENCE, [ + (OBJECT_IDENTIFIER, hashid), + (NULL, None), + ]), + (OCTET_STRING, digest), + ]), + ) + if len(dinfo)+3 > modlen: + raise Exception("Hash too large for modulus") # XXX: DKIMException + return "\x00\x01"+"\xff"*(modlen-len(dinfo)-3)+"\x00"+dinfo +