DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
#!/usr/bin/env python
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

"""
Helper library for creating a Signed Certificate Timestamp given the
details of a signing key, when to sign, and the certificate data to
sign. Currently only supports precert_entry types. See RFC 6962.
"""

from pyasn1.codec.der import encoder
from struct import pack
import binascii
import calendar
import hashlib

import pykey


class InvalidKeyError(Exception):
    """Helper exception to handle unknown key types."""

    def __init__(self, key):
        self.key = key

    def __str__(self):
        return 'Invalid key: "%s"' % str(self.key)


class SCT(object):
    """SCT represents a Signed Certificate Timestamp."""

    def __init__(self, key, date, tbsCertificate, issuerKey):
        self.key = key
        self.timestamp = calendar.timegm(date.timetuple()) * 1000
        self.tbsCertificate = tbsCertificate
        self.issuerKey = issuerKey

    def signAndEncode(self):
        """Returns a signed and encoded representation of the SCT as a
        string."""
        # The signature is over the following data:
        # sct_version (one 0 byte)
        # signature_type (one 0 byte)
        # timestamp (8 bytes, milliseconds since the epoch)
        # entry_type (two bytes [0, 1] - currently only precert_entry is
        #             supported)
        # signed_entry (bytes of PreCert)
        # extensions (2-byte-length-prefixed, currently empty (so two 0
        #             bytes))
        # A PreCert is:
        # issuer_key_hash (32 bytes of SHA-256 hash of the issuing
        #                  public key, as DER-encoded SPKI)
        # tbs_certificate (3-byte-length-prefixed data)
        timestamp = pack('!Q', self.timestamp)
        hasher = hashlib.sha256()
        hasher.update(encoder.encode(self.issuerKey.asSubjectPublicKeyInfo()))
        issuer_key_hash = hasher.digest()
        len_prefix = pack('!L', len(self.tbsCertificate))[1:]
        data = '\0\0' + timestamp + '\0\1' + issuer_key_hash + len_prefix + \
               self.tbsCertificate + '\0\0'
        if isinstance(self.key, pykey.ECCKey):
            signatureByte = '\3'
        elif isinstance(self.key, pykey.RSAKey):
            signatureByte = '\1'
        else:
            raise InvalidKeyError(self.key)
        # sign returns a hex string like "'<hex bytes>'H", but we want
        # bytes here
        hexSignature = self.key.sign(data, pykey.HASH_SHA256)
        signature = binascii.unhexlify(hexSignature[1:-2])
        # The actual data returned is the following:
        # sct_version (one 0 byte)
        # id (32 bytes of SHA-256 hash of the signing key, as
        #     DER-encoded SPKI)
        # timestamp (8 bytes, milliseconds since the epoch)
        # extensions (2-byte-length-prefixed data, currently
        #             empty)
        # hash (one 4 byte representing sha256)
        # signature (one byte - 1 for RSA and 3 for ECDSA)
        # signature (2-byte-length-prefixed data)
        hasher = hashlib.sha256()
        hasher.update(encoder.encode(self.key.asSubjectPublicKeyInfo()))
        key_id = hasher.digest()
        signature_len_prefix = pack('!H', len(signature))
        return '\0' + key_id + timestamp + '\0\0\4' + signatureByte + \
               signature_len_prefix + signature