diff --git a/dkim/__init__.py b/dkim/__init__.py index 223d7cf..3ff305c 100644 --- a/dkim/__init__.py +++ b/dkim/__init__.py @@ -25,10 +25,12 @@ import time import dns.resolver from dkim.crypto import ( + DigestTooLargeError, parse_private_key, parse_public_key, RSASSA_PKCS1_v1_5_sign, RSASSA_PKCS1_v1_5_verify, + UnparsableKeyError, ) from dkim.util import ( InvalidTagValueList, @@ -273,7 +275,11 @@ def sign(message, selector, domain, privkey, identity=None, canonicalize=(Simple raise KeyFormatError(str(e)) if debuglog is not None: print >>debuglog, " ".join("%02x" % ord(x) for x in pkdata) - pk = parse_private_key(pkdata) + try: + pk = parse_private_key(pkdata) + except UnparsableKeyError, e: + raise KeyFormatError(str(e)) + if identity is not None and not identity.endswith(domain): raise ParameterError("identity must end with domain") @@ -321,8 +327,11 @@ def sign(message, selector, domain, privkey, identity=None, canonicalize=(Simple if debuglog is not None: print >>debuglog, "sign digest:", " ".join("%02x" % ord(x) for x in d) - sig2 = RSASSA_PKCS1_v1_5_sign( - d, HASHID_SHA256, pk['privateExponent'], pk['modulus']) + try: + sig2 = RSASSA_PKCS1_v1_5_sign( + d, HASHID_SHA256, pk['privateExponent'], pk['modulus']) + except DigestTooLargeError: + raise ParameterError("digest too large for modulus") sig += base64.b64encode(sig2) return sig + "\r\n" @@ -414,7 +423,12 @@ def verify(message, debuglog=None, dnsfunc=dnstxt): pub = parse_tag_value(s) except InvalidTagValueList: return False - pk = parse_public_key(base64.b64decode(pub['p'])) + try: + pk = parse_public_key(base64.b64decode(pub['p'])) + except UnparsableKeyError, e: + if debuglog is not None: + print >>debuglog, "could not parse public key: %s" % e + return False include_headers = re.split(r"\s*:\s*", sig['h']) h = hasher() @@ -427,5 +441,7 @@ def verify(message, debuglog=None, dnsfunc=dnstxt): try: return RSASSA_PKCS1_v1_5_verify( d, hashid, signature, pk['publicExponent'], pk['modulus']) - except ParameterError: + except DigestTooLargeError: + if debuglog is not None: + print >>debuglog, "digest too large for modulus" return False diff --git a/dkim/crypto.py b/dkim/crypto.py index 4506da4..29d734a 100644 --- a/dkim/crypto.py +++ b/dkim/crypto.py @@ -18,13 +18,16 @@ # Copyright (c) 2011 William Grant __all__ = [ + 'DigestTooLargeError', 'parse_private_key', 'parse_public_key', 'RSASSA_PKCS1_v1_5_sign', 'RSASSA_PKCS1_v1_5_verify', + 'UnparsableKeyError', ] from dkim.asn1 import ( + ASN1FormatError, asn1_build, asn1_parse, BIT_STRING, @@ -68,11 +71,16 @@ ASN1_RSAPrivateKey = [ ] -class DigestTooLarge(Exception): +class DigestTooLargeError(Exception): """The digest is too large to fit within the requested length.""" pass +class UnparsableKeyError(Exception): + """The data could not be parsed as a key.""" + pass + + def parse_public_key(data): """Parse an RSA public key. @@ -80,9 +88,12 @@ def parse_public_key(data): containing an RFC3447 RSAPublicKey. @return: RSA public key """ - 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:]) + try: + # Not sure why the [1:] is necessary to skip a byte. + x = asn1_parse(ASN1_Object, data) + pkd = asn1_parse(ASN1_RSAPublicKey, x[0][1][1:]) + except ASN1FormatError, e: + raise UnparsableKeyError(str(e)) pk = { 'modulus': pkd[0][0], 'publicExponent': pkd[0][1], @@ -96,7 +107,10 @@ def parse_private_key(data): @param data: DER-encoded RFC3447 RSAPrivateKey. @return: RSA private key """ - pka = asn1_parse(ASN1_RSAPrivateKey, data) + try: + pka = asn1_parse(ASN1_RSAPrivateKey, data) + except ASN1FormatError, e: + raise UnparsableKeyError(str(e)) pk = { 'version': pka[0][0], 'modulus': pka[0][1], @@ -128,7 +142,7 @@ def EMSA_PKCS1_v1_5_encode(digest, mlen, hashid): (OCTET_STRING, digest), ])) if len(dinfo)+3 > mlen: - raise DigestTooLarge() + raise DigestTooLargeError() return "\x00\x01"+"\xff"*(mlen-len(dinfo)-3)+"\x00"+dinfo