Beispiel #1
0
 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")
Beispiel #2
0
 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")
Beispiel #3
0
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
Beispiel #4
0
 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
Beispiel #5
0
  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)
Beispiel #6
0
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')