def test_parse_public_key(self): data = read_test_data("test.txt") key = parse_public_key(base64.b64decode(parse_tag_value(data)[b"p"])) self.assertEqual(key["modulus"], TEST_KEY_MODULUS) self.assertEqual(key["publicExponent"], TEST_KEY_PUBLIC_EXPONENT) try: data = read_test_data("test_bad.txt") key = parse_public_key(base64.b64decode(parse_tag_value(data)[b"p"])) except UnparsableKeyError: return self.fail("failed to reject invalid public key")
def test_parse_public_key(self): data = read_test_data('test.txt') key = parse_public_key(base64.b64decode(parse_tag_value(data)[b'p'])) self.assertEqual(key['modulus'], TEST_KEY_MODULUS) self.assertEqual(key['publicExponent'], TEST_KEY_PUBLIC_EXPONENT) try: data = read_test_data('test_bad.txt') key = parse_public_key( base64.b64decode(parse_tag_value(data)[b'p'])) except UnparsableKeyError: return self.fail("failed to reject invalid public key")
def load_pk_from_dns(name, dnsfunc=get_txt): s = dnsfunc(name) if not s: raise KeyFormatError("missing public key: %s" % name) try: if type(s) is str: s = s.encode('ascii') pub = parse_tag_value(s) except InvalidTagValueList as e: raise KeyFormatError(e) try: pk = parse_public_key(base64.b64decode(pub[b'p'])) keysize = bitsize(pk['modulus']) except KeyError: raise KeyFormatError("incomplete public key: %s" % s) except (TypeError, UnparsableKeyError) as e: raise KeyFormatError("could not parse public key (%s): %s" % (pub[b'p'], e)) return pk, keysize
def load_pk(name, s=None): """ Load the corresponding public key from DNS records :param name: Domain name :param s: TXT record from DNS :return: tuple (pk, keysize, ktag) WHERE pk: public key value keysize: size of public key ktag: key type (RSA, etc.) """ if not s: raise KeyFormatError("missing public key: %s" % name) try: if type(s) is str: s = s.encode("ascii") pub = dkim.util.parse_tag_value(s) except InvalidTagValueList as e: raise KeyFormatError(e) try: if pub[b"k"] == b"ed25519": pk = nacl.signing.VerifyKey( pub[b"p"], encoder=nacl.encoding.Base64Encoder) keysize = 256 ktag = b"ed25519" except KeyError: pub[b"k"] = b"rsa" if pub[b"k"] == b"rsa": try: pk = crypto.parse_public_key(base64.b64decode(pub[b"p"])) keysize = dkim.bitsize(pk["modulus"]) except KeyError: raise KeyFormatError(f"incomplete public key: {s}") except (TypeError, UnparsableKeyError) as e: raise KeyFormatError( f"could not parse public key ({pub[b'p']}): {e}") ktag = b"rsa" return pk, keysize, ktag
def verify(self,idx=0,dnsfunc=get_txt): sigheaders = [(x,y) for x,y in self.headers if x.lower() == b"dkim-signature"] if len(sigheaders) <= idx: return False # By default, we validate the first DKIM-Signature line found. try: sig = parse_tag_value(sigheaders[idx][1]) self.signature_fields = sig except InvalidTagValueList as e: raise MessageFormatError(e) logger = self.logger logger.debug("sig: %r" % sig) validate_signature_fields(sig) self.domain = sig[b'd'] self.selector = sig[b's'] try: canon_policy = CanonicalizationPolicy.from_c_value(sig.get(b'c')) except InvalidCanonicalizationPolicyError as e: raise MessageFormatError("invalid c= value: %s" % e.args[0]) headers = canon_policy.canonicalize_headers(self.headers) body = canon_policy.canonicalize_body(self.body) try: hasher = HASH_ALGORITHMS[sig[b'a']] except KeyError as e: raise MessageFormatError("unknown signature algorithm: %s" % e.args[0]) if b'l' in sig: body = body[:int(sig[b'l'])] h = hasher() h.update(body) bodyhash = h.digest() logger.debug("bh: %s" % base64.b64encode(bodyhash)) try: bh = base64.b64decode(re.sub(br"\s+", b"", sig[b'bh'])) except TypeError as e: raise MessageFormatError(str(e)) if bodyhash != bh: raise ValidationError( "body hash mismatch (got %s, expected %s)" % (base64.b64encode(bodyhash), sig[b'bh'])) name = sig[b's'] + b"._domainkey." + sig[b'd'] + b"." s = dnsfunc(name) if not s: raise KeyFormatError("missing public key: %s"%name) try: if type(s) is str: s = s.encode('ascii') pub = parse_tag_value(s) except InvalidTagValueList as e: raise KeyFormatError(e) try: pk = parse_public_key(base64.b64decode(pub[b'p'])) self.keysize = bitsize(pk['modulus']) except KeyError: raise KeyFormatError("incomplete public key: %s" % s) except (TypeError,UnparsableKeyError) as e: raise KeyFormatError("could not parse public key (%s): %s" % (pub[b'p'],e)) include_headers = [x.lower() for x in re.split(br"\s*:\s*", sig[b'h'])] self.include_headers = tuple(include_headers) # address bug#644046 by including any additional From header # fields when verifying. Since there should be only one From header, # this shouldn't break any legitimate messages. This could be # generalized to check for extras of other singleton headers. if b'from' in include_headers: include_headers.append(b'from') h = hasher() self.signed_headers = hash_headers( h, canon_policy, headers, include_headers, sigheaders[idx], sig) try: signature = base64.b64decode(re.sub(br"\s+", b"", sig[b'b'])) res = RSASSA_PKCS1_v1_5_verify(h, signature, pk) if res and self.keysize < self.minkey: raise KeyFormatError("public key too small: %d" % self.keysize) return res except (TypeError,DigestTooLargeError) as e: raise KeyFormatError("digest too large for modulus: %s"%e)
def sig_gen_multi(public_as, private_as, public_ams, private_ams, body, amsh, arsh, fold=False, verbose=False, as_tmp = None, ams_tmp = None): # body hasher = HASH_ALGORITHMS[b'rsa-sha256'] h = hasher() h.update(body) bh = base64.b64encode(h.digest()) print("ams bh= ") print(bh) #amsh hasher = HASH_ALGORITHMS[b'rsa-sha256'] h = hasher() h = HashThrough(hasher()) h.update(b"\r\n".join([x + b":" + y for (x,y) in amsh(bh)])) if verbose: print("\nsign ams hashed: %r" % h.hashed()) pk = parse_pem_private_key(private_ams) sig2 = RSASSA_PKCS1_v1_5_sign(h, pk) msb = base64.b64encode(bytes(sig2)) if fold: msb = msb[:70] + b" " + msb[70:142] + b" " + msb[142:214]# + b" " + msb[214:286] + b" " + msb[286:] print("ams b= ") print(msb) pk_ams = parse_public_key(base64.b64decode(public_ams)) signature = base64.b64decode(msb) ams_valid = RSASSA_PKCS1_v1_5_verify(h, signature, pk_ams) print("ams sig valid: %r" % ams_valid) hasher = HASH_ALGORITHMS[b'rsa-sha256'] h = hasher() h = HashThrough(hasher()) h.update(b"\r\n".join([x + b":" + y for (x,y) in arsh(msb, bh)])) if verbose: print("\nsign ars hashed: %r" % h.hashed()) pk = parse_pem_private_key(private_as) sig2 = RSASSA_PKCS1_v1_5_sign(h, pk) sb = base64.b64encode(bytes(sig2)) print("arsh b=") print(sb) pk_as = parse_public_key(base64.b64decode(public_as)) signature = base64.b64decode(sb) ams_valid = RSASSA_PKCS1_v1_5_verify(h, signature, pk_as) print("arsh sig valid: %r" % ams_valid) spc = fold and b"" or b" " accum = '' if as_tmp: sb = sb[:70] + b"\n " + spc + sb[70:142] + b"\n " + spc + sb[142:214]# + b"\n " + sb[214:286] + b"\n " + msb[286:] res = as_tmp.replace(b'%b', sb) accum = res print(res.decode('utf-8')) if ams_tmp: msb = msb.replace(b' ', b'') msb = msb[:70] + b"\n " + spc + msb[70:142] + b"\n " + spc + msb[142:214]# + b"\n " + msb[214:286] + b"\n " + msb[286:] res = ams_tmp.replace(b'%bh', bh) res = res.replace(b'%b', msb) accum += b"\n" + res print(res.decode('utf-8')) os.system(b'echo "' + accum + b'" | pbcopy')