示例#1
0
    def sign(self,
             selector,
             domain,
             privkey,
             auth_results,
             chain_validation_status,
             include_headers=None,
             timestamp=None,
             standardize=False):
        try:
            pk = parse_pem_private_key(privkey)
        except UnparsableKeyError as e:
            raise KeyFormatError(str(e))

        # Setup headers
        if include_headers is None:
            include_headers = self.default_sign_headers()

        if b'arc-authentication-results' not in include_headers:
            include_headers.append(b'arc-authentication-results')

        include_headers = tuple([x.lower() for x in include_headers])

        # record what verify should extract
        self.include_headers = include_headers

        # rfc4871 says FROM is required
        if b'from' not in include_headers:
            raise ParameterError("The From header field MUST be signed")

        # raise exception for any SHOULD_NOT headers, call can modify
        # SHOULD_NOT if really needed.
        for x in set(include_headers).intersection(self.should_not_sign):
            raise ParameterError("The %s header field SHOULD NOT be signed" %
                                 x)

        max_instance, arc_headers_w_instance = self.sorted_arc_headers()
        instance = 1
        if len(arc_headers_w_instance) != 0:
            instance = max_instance + 1

        if instance == 1 and chain_validation_status != CV_None:
            raise ParameterError(
                "No existing chain found on message, cv should be none")
        elif instance != 1 and chain_validation_status == CV_None:
            raise ParameterError("cv=none not allowed on instance %d" %
                                 instance)

        new_arc_set = []
        arc_headers = [y for x, y in arc_headers_w_instance]

        # Compute ARC-Authentication-Results
        aar_value = ("i=%d; " % instance).encode('utf-8') + auth_results
        if aar_value[-1] != b'\n': aar_value += b'\r\n'

        new_arc_set.append(b"ARC-Authentication-Results: " + aar_value)
        self.headers.insert(0, (b"arc-authentication-results", aar_value))
        arc_headers.insert(0, (b"ARC-Authentication-Results", aar_value))

        # Compute bh=
        canon_policy = CanonicalizationPolicy.from_c_value(b'relaxed/relaxed')

        self.hasher = HASH_ALGORITHMS[self.signature_algorithm]
        h = HashThrough(self.hasher())
        h.update(canon_policy.canonicalize_body(self.body))
        self.logger.debug("sign ams body hashed: %r" % h.hashed())
        bodyhash = base64.b64encode(h.digest())

        # Compute ARC-Message-Signature
        timestamp = str(timestamp or int(time.time())).encode('ascii')
        ams_fields = [
            x for x in [
                (b'i', str(instance).encode('ascii')),
                (b'a', self.signature_algorithm),
                (b'c', b'relaxed/relaxed'),
                (b'd', domain),
                (b's', selector),
                (b't', timestamp),
                (b'h', b" : ".join(include_headers)),
                (b'bh', bodyhash),
                # Force b= to fold onto it's own line so that refolding after
                # adding sig doesn't change whitespace for previous tags.
                (b'b', b'0' * 60),
            ] if x
        ]

        res = self.gen_header(ams_fields, include_headers, canon_policy,
                              b"ARC-Message-Signature", pk, standardize)

        new_arc_set.append(b"ARC-Message-Signature: " + res)
        self.headers.insert(0, (b"ARC-Message-Signature", res))
        arc_headers.insert(0, (b"ARC-Message-Signature", res))

        # Compute ARC-Seal
        as_fields = [
            x for x in [
                (b'i', str(instance).encode('ascii')),
                (b'cv', chain_validation_status),
                (b'a', self.signature_algorithm),
                (b'd', domain),
                (b's', selector),
                (b't', timestamp),
                # Force b= to fold onto it's own line so that refolding after
                # adding sig doesn't change whitespace for previous tags.
                (b'b', b'0' * 60),
            ] if x
        ]

        as_include_headers = [x[0].lower() for x in arc_headers]
        as_include_headers.reverse()

        res = self.gen_header(as_fields, as_include_headers, canon_policy,
                              b"ARC-Seal", pk, standardize)

        new_arc_set.append(b"ARC-Seal: " + res)
        self.headers.insert(0, (b"ARC-Seal", res))
        arc_headers.insert(0, (b"ARC-Seal", res))

        new_arc_set.reverse()

        return new_arc_set
示例#2
0
 def setUp(self):
     self.key = parse_pem_private_key(read_test_data("test.private"))
     self.hash = hashlib.sha1(self.test_digest)
示例#3
0
    def sign(self,
             selector,
             domain,
             privkey,
             identity=None,
             canonicalize=(b'relaxed', b'simple'),
             include_headers=None,
             length=False):
        try:
            pk = parse_pem_private_key(privkey)
        except UnparsableKeyError as e:
            raise KeyFormatError(str(e))

        if identity is not None and not identity.endswith(domain):
            raise ParameterError("identity must end with domain")

        canon_policy = CanonicalizationPolicy.from_c_value(
            b'/'.join(canonicalize))

        if include_headers is None:
            include_headers = self.default_sign_headers()

        include_headers = tuple([x.lower() for x in include_headers])
        # record what verify should extract
        self.include_headers = include_headers

        # rfc4871 says FROM is required
        if b'from' not in include_headers:
            raise ParameterError("The From header field MUST be signed")

        # raise exception for any SHOULD_NOT headers, call can modify
        # SHOULD_NOT if really needed.
        for x in set(include_headers).intersection(self.should_not_sign):
            raise ParameterError("The %s header field SHOULD NOT be signed" %
                                 x)

        body = canon_policy.canonicalize_body(self.body)

        self.hasher = HASH_ALGORITHMS[self.signature_algorithm]
        h = self.hasher()
        h.update(body)
        bodyhash = base64.b64encode(h.digest())

        sigfields = [
            x for x in [
                (b'v', b"1"),
                (b'a', self.signature_algorithm),
                (b'c', canon_policy.to_c_value()),
                (b'd', domain),
                (b'i', identity or b"@" + domain),
                length and (b'l', str(len(body)).encode('ascii')),
                (b'q', b"dns/txt"),
                (b's', selector),
                (b't', str(int(time.time())).encode('ascii')),
                (b'h', b" : ".join(include_headers)),
                (b'bh', bodyhash),
                # Force b= to fold onto it's own line so that refolding after
                # adding sig doesn't change whitespace for previous tags.
                (b'b', b'0' * 60),
            ] if x
        ]

        res = self.gen_header(sigfields, include_headers, canon_policy,
                              b"DKIM-Signature", pk)

        self.domain = domain
        self.selector = selector
        self.signature_fields = dict(sigfields)
        return b'DKIM-Signature: ' + res
示例#4
0
 def test_parse_pem_private_key(self):
     key = parse_pem_private_key(read_test_data("test.private"))
     self.assertEqual(key, TEST_PK)
示例#5
0
  def sign(self, selector, domain, privkey, identity=None,
        canonicalize=(b'relaxed',b'simple'), include_headers=None, length=False):
    try:
        pk = parse_pem_private_key(privkey)
    except UnparsableKeyError as e:
        raise KeyFormatError(str(e))

    if identity is not None and not identity.endswith(domain):
        raise ParameterError("identity must end with domain")

    canon_policy = CanonicalizationPolicy.from_c_value(
        b'/'.join(canonicalize))
    headers = canon_policy.canonicalize_headers(self.headers)

    if include_headers is None:
        include_headers = self.default_sign_headers()

    # rfc4871 says FROM is required
    if b'from' not in ( x.lower() for x in include_headers ):
        raise ParameterError("The From header field MUST be signed")

    # raise exception for any SHOULD_NOT headers, call can modify 
    # SHOULD_NOT if really needed.
    for x in include_headers:
        if x.lower() in self.should_not_sign:
            raise ParameterError("The %s header field SHOULD NOT be signed"%x)

    body = canon_policy.canonicalize_body(self.body)

    hasher = HASH_ALGORITHMS[self.signature_algorithm]
    h = hasher()
    h.update(body)
    bodyhash = base64.b64encode(h.digest())

    sigfields = [x for x in [
        (b'v', b"1"),
        (b'a', self.signature_algorithm),
        (b'c', canon_policy.to_c_value()),
        (b'd', domain),
        (b'i', identity or b"@"+domain),
        length and (b'l', len(body)),
        (b'q', b"dns/txt"),
        (b's', selector),
        (b't', str(int(time.time())).encode('ascii')),
        (b'h', b" : ".join(include_headers)),
        (b'bh', bodyhash),
        # Force b= to fold onto it's own line so that refolding after
        # adding sig doesn't change whitespace for previous tags.
        (b'b', b'0'*60), 
    ] if x]
    include_headers = [x.lower() for x in include_headers]
    # record what verify should extract
    self.include_headers = tuple(include_headers)

    sig_value = fold(b"; ".join(b"=".join(x) for x in sigfields))
    sig_value = RE_BTAG.sub(b'\\1',sig_value)
    dkim_header = (b'DKIM-Signature', b' ' + sig_value)
    h = hasher()
    sig = dict(sigfields)
    self.signed_headers = hash_headers(
        h, canon_policy, headers, include_headers, dkim_header,sig)
    self.logger.debug("sign headers: %r" % self.signed_headers)

    try:
        sig2 = RSASSA_PKCS1_v1_5_sign(h, pk)
    except DigestTooLargeError:
        raise ParameterError("digest too large for modulus")
    # Folding b= is explicity allowed, but yahoo and live.com are broken
    #sig_value += base64.b64encode(bytes(sig2))
    # Instead of leaving unfolded (which lets an MTA fold it later and still
    # breaks yahoo and live.com), we change the default signing mode to
    # relaxed/simple (for broken receivers), and fold now.
    sig_value = fold(sig_value + base64.b64encode(bytes(sig2)))

    self.domain = domain
    self.selector = selector
    self.signature_fields = sig
    return b'DKIM-Signature: ' + sig_value + b"\r\n"
示例#6
0
 def test_parse_pem_private_key(self):
     key = parse_pem_private_key(read_test_data('test.private'))
     self.assertEqual(key, TEST_PK)
示例#7
0
 def setUp(self):
     self.key = parse_pem_private_key(read_test_data('test.private'))
     self.hash = hashlib.sha1(self.test_digest)
示例#8
0
def sig_gen(public, private, 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)
    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 = parse_public_key(base64.b64decode(public))
    #signature = base64.b64decode(b)
    #ams_valid = RSASSA_PKCS1_v1_5_verify(h, signature, pk)
    #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)
    sig2 = RSASSA_PKCS1_v1_5_sign(h, pk)
    sb = base64.b64encode(bytes(sig2))
    print("arsh b=")
    print(sb)

    #signature = base64.b64decode(b)
    #ams_valid = RSASSA_PKCS1_v1_5_verify(h, signature, pk)
    #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')