async def get_old_timestamp(signature, timestamp_url=None):
    """Retrieve an old style timestamp countersignature.

    Args:
        signature (str): the signature to get a counter signature for. This is
                         usally the encryptedDigest of our file's signerInfo section.
        timestamp_url (str): what service to use to fetch the timestamp countersignature from.
                             defaults to 'http://timestamp.digicert.com'.

    Returns:
        SignedData object

    """
    req = OldTimeStampReq()
    req["type"] = univ.ObjectIdentifier("1.3.6.1.4.1.311.3.2.1")
    req["blob"]["signature"] = signature
    req["blob"]["type"] = univ.ObjectIdentifier("1.2.840.113549.1.7.1")

    encoded_req = der_encode(req)
    b64_req = base64.b64encode(encoded_req)

    url = timestamp_url or "http://timestamp.digicert.com"

    async with aiohttp.request(
        "POST", url, data=b64_req, headers={"Content-Type": "application/octet-stream"}
    ) as resp:
        # Uncomment below to capture a real response
        # open('old-ts.dat', 'wb').write(resp.content)
        ci, _ = der_decode(base64.b64decode(await resp.read()), ContentInfo())
        ts, _ = der_decode(ci["content"], SignedData())
        return ts
Exemple #2
0
def verify_pefile_rfc3161_timestamp(f, pe):
    """Verifies that the timestamp in this PE file is valid."""
    certificates = pe.certificates
    if not certificates:
        return True, "No certificates present"

    messages = []

    x509_certs_by_serial = get_x509_certificates(pe)

    passed = True
    for pe_cert in certificates:
        content_info, _ = der_decode(pe_cert.data, ContentInfo())
        signed_data, _ = der_decode(content_info["content"], SignedData())

        for info in signed_data["signerInfos"]:
            # Check if there are any timestamps in unauthenticatedAttributes
            for a in info["unauthenticatedAttributes"]:
                if a["type"] == id_timestampSignature:  # RFC3161 timestamps
                    # Calculate the hash of our signature
                    # TODO: don't hardcode the digest algorithm
                    digest_algo = DIGEST_NAME_BY_OID[info["digestAlgorithm"]
                                                     ["algorithm"]]
                    signature_digest = hashlib.new(
                        digest_algo,
                        info["encryptedDigest"].asOctets()).digest()
                    counter_sig = der_decode(a["values"][0], ContentInfo())[0]
                    counter_sig = der_decode(counter_sig["content"],
                                             CMSSignedData())[0]

                    tst_info = der_decode(
                        counter_sig["encapContentInfo"]["eContent"],
                        TSTInfo())[0]
                    message_digest = tst_info["messageImprint"][
                        "hashedMessage"].asOctets()
                    if message_digest != signature_digest:
                        passed = False
                        messages.append(
                            f"counter signature is over the wrong data (hash: {hexlify(signature_digest)})"
                        )
                    t_passed, message = verify_signed_data(
                        counter_sig, x509_certs_by_serial)
                    passed = passed and t_passed
                    messages.append(message)

    return passed, "\n".join(messages)
Exemple #3
0
async def make_authenticode_signeddata(
    cert,
    signer,
    authenticode_digest,
    digest_algo,
    timestamp=None,
    opus_info=None,
    opus_url=None,
):
    """Creates a SignedData object containing the signature for a PE file.

    Arguments:
        cert (X509):        public certificate used for signing
        signer (function):  signing function
        authenticode_digest (bytes): Authenticode digest of PE file to sign
                                     NB. This is not simply the hash of the file!
        digest_algo (str): digest algorithm to use. e.g. 'sha256'
        timestamp (UTCTime): optional. timestamp to include in the signature.
                             If not provided, the current time is used.
        opus_info (string):  Additional information to include in the signature
        opus_url (string):   URL to include in the signature
    Returns:
        A ContentInfo ASN1 object

    """
    if not timestamp:
        timestamp = useful.UTCTime.fromDateTime(datetime.now())

    asn_digest_algo = ASN_DIGEST_ALGO_MAP[digest_algo]

    spc = make_spc(digest_algo, authenticode_digest)

    encoded_spc = der_encode(spc)

    pkcs7_cert = x509_to_pkcs7(cert)

    signer_info = make_signer_info(pkcs7_cert, digest_algo, timestamp,
                                   calc_spc_digest(encoded_spc, digest_algo))

    signer_digest = calc_signerinfo_digest(signer_info, digest_algo)
    signer_info["encryptedDigest"] = await signer(signer_digest, digest_algo)

    sig = SignedData()
    sig["version"] = 1
    sig["digestAlgorithms"][0] = asn_digest_algo
    sig["certificates"][0]["certificate"] = pkcs7_cert
    sig["contentInfo"]["contentType"] = id_spcIndirectDataContext
    sig["contentInfo"]["content"] = encoded_spc
    sig["signerInfos"][0] = signer_info

    ci = ContentInfo()
    ci["contentType"] = id_signedData
    ci["content"] = sig

    return ci
Exemple #4
0
def get_signatures_from_certificates(certificates):
    """Retrieve the signatures from a list of certificates."""
    retval = []
    for cert in certificates:
        ci, _ = der_decode(cert["data"], ContentInfo())
        signed_data, _ = der_decode(ci["content"], SignedData())
        spc, _ = der_decode(signed_data["contentInfo"]["content"],
                            SpcIndirectDataContent())
        signed_data["contentInfo"]["content"] = spc
        retval.append(signed_data)
    return retval
Exemple #5
0
def get_x509_certificates(pe):
    """Returns a mapping of (issuer, serial) to x509 certificates."""
    certificates = pe.certificates
    x509_certs_by_serial = {}
    for pe_cert in certificates:
        content_info, _ = der_decode(pe_cert.data, ContentInfo())
        signed_data, _ = der_decode(content_info["content"], SignedData())
        for cert in signed_data["certificates"]:
            cert = der_encode(cert["certificate"])
            x509_cert = x509.load_der_x509_certificate(cert, default_backend())
            x509_certs_by_serial[x509_cert.issuer,
                                 x509_cert.serial_number] = x509_cert

    return x509_certs_by_serial
Exemple #6
0
async def resign(old_sig, certs, signer):
    """Resigns an old signature with a new certificate.

    Replaces the encrypted signature digest in the given signature with new one
    generated with the given signer function.

    Args:
        old_sig (SignedData): the original signature as a SignedData object
        certs (list of x509 certificates): certificates to attach to the new signature
        signer (function): function to call to generate the new encrypted
                           digest. The function is passed two arguments: (signer_digest,
                           digest_algo)

    Returns:
        ContentInfo object with the new signature embedded

    """
    new_sig = SignedData()
    new_sig["version"] = old_sig["version"]
    new_sig["contentInfo"] = old_sig["contentInfo"]

    new_sig["digestAlgorithms"] = old_sig["digestAlgorithms"]

    for i, cert in enumerate(certs):
        pkcs7_cert = x509_to_pkcs7(cert)
        new_sig["certificates"][i]["certificate"] = pkcs7_cert

    new_si = copy_signer_info(old_sig["signerInfos"][0],
                              new_sig["certificates"][0]["certificate"])
    if new_si["digestAlgorithm"]["algorithm"] == id_sha1:
        digest_algo = "sha1"
    elif new_si["digestAlgorithm"]["algorithm"] == id_sha256:
        digest_algo = "sha256"
    signer_digest = calc_signerinfo_digest(new_si, digest_algo)
    log.debug("Digest to sign is: %s", hexlify(signer_digest))
    new_si["encryptedDigest"] = await signer(signer_digest, digest_algo)
    new_sig["signerInfos"][0] = new_si

    ci = ContentInfo()
    ci["contentType"] = id_signedData
    ci["content"] = new_sig
    sig = der_encode(ci)

    return sig
Exemple #7
0
def verify_pefile_signature(f, pe):
    """Verifies that the signature in this PE file is valid."""
    # TODO: Check that the message being signed refers to something.
    # e.g. the authenticatedAttributes' digest is our hash
    certificates = pe.certificates
    if not certificates:
        return True, "No certificates present"

    messages = []

    x509_certs_by_serial = get_x509_certificates(pe)

    passed = True
    for pe_cert in certificates:
        content_info, _ = der_decode(pe_cert.data, ContentInfo())
        signed_data, _ = der_decode(content_info["content"], SignedData())

        spc = der_decode(signed_data["contentInfo"]["content"],
                         SpcIndirectDataContent())[0]
        digest_algo_oid = spc["messageDigest"]["digestAlgorithm"]["algorithm"]
        digest_algo = DIGEST_NAME_BY_OID[digest_algo_oid]
        a_digest = calc_authenticode_digest(f, digest_algo)
        e_spc = der_encode(make_spc(digest_algo, a_digest))
        spc_digest = calc_spc_digest(e_spc, digest_algo)

        for info in signed_data["signerInfos"]:
            # Check that the signature is on the right hash
            info_digest = der_decode(
                get_attribute(info["authenticatedAttributes"],
                              id_messageDigest)[0])[0].asOctets()
            if info_digest != spc_digest:
                passed = False
                messages.append(
                    f"Wrong digest: {hexlify(info_digest)} != {hexlify(spc_digest)}"
                )
                continue
            t_passed, message = verify_signer_info(info, x509_certs_by_serial)
            passed = passed and t_passed
            messages.append(message)

    return passed, "\n".join(messages)
Exemple #8
0
def verify_pefile_old_timestamp(f, pe):
    """Verifies that the timestamp in this PE file is valid."""
    certificates = pe.certificates
    if not certificates:
        return True, "No certificates present"

    messages = []

    x509_certs_by_serial = get_x509_certificates(pe)

    passed = True
    for pe_cert in certificates:
        content_info, _ = der_decode(pe_cert.data, ContentInfo())
        signed_data, _ = der_decode(content_info["content"], SignedData())

        for info in signed_data["signerInfos"]:
            # Check if there are any timestamps in unauthenticatedAttributes
            for a in info["unauthenticatedAttributes"]:
                if a["type"] == id_counterSignature:  # Old timestamps
                    # Calculate the hash of our signature
                    # These timestamps are always sha1
                    signature_digest = hashlib.new(
                        "sha1", info["encryptedDigest"].asOctets()).digest()
                    counter_sig = der_decode(a["values"][0], SignerInfo())[0]
                    counter_sig_digest = der_decode(
                        get_attribute(counter_sig["authenticatedAttributes"],
                                      id_messageDigest)[0])[0].asOctets()
                    # Check that the counter signature is of the right data
                    if counter_sig_digest != signature_digest:
                        passed = False
                        messages.append(
                            f"counter signature is over the wrong data (hash: {hexlify(signature_digest)})"
                        )
                    # Check that the timestamp signature itself is valid
                    t_passed, message = verify_signer_info(
                        counter_sig, x509_certs_by_serial)
                    passed = passed and t_passed
                    messages.append(message)

    return passed, "\n".join(messages)
Exemple #9
0
def verify_pefile_digest(f, pe):
    """Verifies that the authenticode digest in this PE file is valid."""
    certificates = pe.certificates
    if not certificates:
        return True, "No certificates present"

    for pe_cert in certificates:
        content_info, _ = der_decode(pe_cert.data, ContentInfo())
        signed_data, _ = der_decode(content_info["content"], SignedData())

        spc = der_decode(signed_data["contentInfo"]["content"],
                         SpcIndirectDataContent())[0]
        file_digest = spc["messageDigest"]["digest"].asOctets()
        digest_algo_oid = spc["messageDigest"]["digestAlgorithm"]["algorithm"]
        a_digest = calc_authenticode_digest(
            f, DIGEST_NAME_BY_OID[digest_algo_oid])

        if file_digest != a_digest:
            return (
                False,
                f"authenticode digests don't match: {hexlify(file_digest)} != {hexlify(a_digest)}",
            )

    return True, "authenticode digests match"
Exemple #10
0
def get_signeddata(s):
    """Gets the SignedData from an encoded ContentInfo object."""
    ci = der_decode(s, ContentInfo())[0]
    sd = der_decode(ci["content"], SignedData())[0]
    return sd