Exemple #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")
Exemple #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")
Exemple #3
0
   def test_trailing_whitespace(self):
       hval = b'''v=1; a=rsa-sha256; d=facebookmail.com; s=s1024-2011-q2; c=relaxed/simple;
         q=dns/txt; [email protected]; t=1308078492;
         h=From:Subject:Date:To:MIME-Version:Content-Type;
         bh=+qPyCOiDQkusTPstCoGjimgDgeZbUaJWIr1mdE6RFxk=;
         b=EUmDmdnAsNtjSEHGHNTa8PXgGaEUtOVezagmninX5Bs/Q26R9r3AMgawyUSKkbHp
         /bQZU6QPZfdvmLMPdIWCQPo8SP+gsz4dpox2efO61DlvgYaxBRhwFedAW9LjYhQc
         3KzW0yB9JHwiDCw1EioVkv+OMHhAYzoIypA0bQyi2bc=;
 '''
       sig = parse_tag_value(hval)
       self.assertEquals(sig[b't'], b'1308078492')
       self.assertEquals(len(sig), 11)
Exemple #4
0
   def test_trailing_whitespace(self):
     hval = b'''v=1; a=rsa-sha256; d=facebookmail.com; s=s1024-2011-q2; c=relaxed/simple;
         q=dns/txt; [email protected]; t=1308078492;
         h=From:Subject:Date:To:MIME-Version:Content-Type;
         bh=+qPyCOiDQkusTPstCoGjimgDgeZbUaJWIr1mdE6RFxk=;
         b=EUmDmdnAsNtjSEHGHNTa8PXgGaEUtOVezagmninX5Bs/Q26R9r3AMgawyUSKkbHp
         /bQZU6QPZfdvmLMPdIWCQPo8SP+gsz4dpox2efO61DlvgYaxBRhwFedAW9LjYhQc
         3KzW0yB9JHwiDCw1EioVkv+OMHhAYzoIypA0bQyi2bc=;
 '''
     sig = parse_tag_value(hval)
     self.assertEquals(sig[b't'],b'1308078492')
     self.assertEquals(len(sig),11)
Exemple #5
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
Exemple #6
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)

        self.logger.debug("sig: %r" % sig)

        validate_signature_fields(sig)
        self.domain = sig[b'd']
        self.selector = sig[b's']

        include_headers = [x.lower() for x in re.split(br"\s*:\s*", sig[b'h'])]
        self.include_headers = tuple(include_headers)

        return self.verify_sig(sig, include_headers, sigheaders[idx], dnsfunc)
Exemple #7
0
 def test_multiple(self):
     self.assertEqual({
         b'foo': b'bar',
         b'baz': b'foo'
     }, parse_tag_value(b'foo=bar;baz=foo'))
Exemple #8
0
    def verify_instance(self,
                        arc_headers_w_instance,
                        instance,
                        dnsfunc=get_txt):
        if (instance == 0) or (len(arc_headers_w_instance) == 0):
            raise ParameterError("request to verify instance %d not present" %
                                 (instance))

        aar_value = None
        ams_value = None
        as_value = None
        arc_headers = []
        output = {'instance': instance}

        for i, arc_header in arc_headers_w_instance:
            if i > instance: continue
            arc_headers.append(arc_header)
            if i == instance:
                if arc_header[0].lower() == b"arc-authentication-results":
                    if aar_value is not None:
                        raise MessageFormatError(
                            "Duplicate ARC-Authentication-Results for instance %d"
                            % instance)
                    aar_value = arc_header[1]
                elif arc_header[0].lower() == b"arc-message-signature":
                    if ams_value is not None:
                        raise MessageFormatError(
                            "Duplicate ARC-Message-Signature for instance %d" %
                            instance)
                    ams_value = arc_header[1]
                elif arc_header[0].lower() == b"arc-seal":
                    if as_value is not None:
                        raise MessageFormatError(
                            "Duplicate ARC-Seal for instance %d" % instance)
                    as_value = arc_header[1]

        if (aar_value is None) or (ams_value is None) or (as_value is None):
            raise MessageFormatError("Incomplete ARC set for instance %d" %
                                     instance)

        output['aar-value'] = aar_value

        # Validate Arc-Message-Signature
        try:
            sig = parse_tag_value(ams_value)
        except InvalidTagValueList as e:
            raise MessageFormatError(e)

        self.logger.debug("ams sig[%d]: %r" % (instance, sig))

        validate_signature_fields(
            sig, [b'i', b'a', b'b', b'c', b'bh', b'd', b'h', b's'], True)
        output['ams-domain'] = sig[b'd']
        output['ams-selector'] = sig[b's']

        include_headers = [x.lower() for x in re.split(br"\s*:\s*", sig[b'h'])]
        if b'arc-seal' in include_headers:
            raise ParameterError(
                "The Arc-Message-Signature MUST NOT sign ARC-Seal")

        ams_header = (b'ARC-Message-Signature', b' ' + ams_value)
        ams_valid = self.verify_sig(sig, include_headers, ams_header, dnsfunc)

        output['ams-valid'] = ams_valid
        self.logger.debug("ams valid: %r" % ams_valid)

        # Validate Arc-Seal
        try:
            sig = parse_tag_value(as_value)
        except InvalidTagValueList as e:
            raise MessageFormatError(e)

        self.logger.debug("as sig[%d]: %r" % (instance, sig))

        validate_signature_fields(sig,
                                  [b'i', b'a', b'b', b'cv', b'd', b's', b't'],
                                  True)
        output['as-domain'] = sig[b'd']
        output['as-selector'] = sig[b's']
        output['cv'] = sig[b'cv']

        as_include_headers = [x[0].lower() for x in arc_headers]
        as_include_headers.reverse()
        as_header = (b'ARC-Seal', b' ' + as_value)
        as_valid = self.verify_sig(sig, as_include_headers[:-1], as_header,
                                   dnsfunc)

        output['as-valid'] = as_valid
        self.logger.debug("as valid: %r" % as_valid)
        return output
Exemple #9
0
 def test_trailing_separator_ignored(self):
     self.assertEqual(
         {b'foo': b'bar'},
         parse_tag_value(b'foo=bar;'))
Exemple #10
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)
Exemple #11
0
 def test_multiple(self):
     self.assertEqual(
         {b'foo': b'bar', b'baz': b'foo'},
         parse_tag_value(b'foo=bar;baz=foo'))
Exemple #12
0
 def test_whitespace_is_stripped(self):
     self.assertEqual({
         b'foo': b'bar',
         b'baz': b'f oo=bar'
     }, parse_tag_value(b'   foo  \t= bar;\tbaz=  f oo=bar  '))
Exemple #13
0
 def test_value_with_equals(self):
     self.assertEqual(
         {b'foo': b'bar', b'baz': b'foo=bar'},
         parse_tag_value(b'foo=bar;baz=foo=bar'))
Exemple #14
0
 def test_value_with_equals(self):
     self.assertEqual({
         b'foo': b'bar',
         b'baz': b'foo=bar'
     }, parse_tag_value(b'foo=bar;baz=foo=bar'))
Exemple #15
0
 def test_single(self):
     self.assertEqual(
         {b'foo': b'bar'},
         parse_tag_value(b'foo=bar'))
Exemple #16
0
 def test_trailing_separator_ignored(self):
     self.assertEqual({b'foo': b'bar'}, parse_tag_value(b'foo=bar;'))
Exemple #17
0
 def test_single(self):
     self.assertEqual({b'foo': b'bar'}, parse_tag_value(b'foo=bar'))
Exemple #18
0
 def test_whitespace_is_stripped(self):
     self.assertEqual(
         {b'foo': b'bar', b'baz': b'f oo=bar'},
         parse_tag_value(b'   foo  \t= bar;\tbaz=  f oo=bar  '))