Process PKCS #9 counter signature and enhance signature verification

This commit is contained in:
Romain Thomas 2021-01-12 20:44:23 +01:00
parent e4d2da1fd5
commit d29a74996b
28 changed files with 1157 additions and 208 deletions

View File

@ -325,6 +325,9 @@ if(LIEF_FROZEN_ENABLED)
add_dependencies(LIB_LIEF lief_frozen)
endif()
# =======================================
# Leaf
# =======================================
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/LIEF/third-party/boost/leaf/all.hpp
COMMAND
${CMAKE_COMMAND} -E copy_directory ${LEAF_INCLUDE_DIR}/
@ -334,6 +337,9 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/LIEF/third-party/b
target_sources(LIB_LIEF PRIVATE
${CMAKE_CURRENT_BINARY_DIR}/include/LIEF/third-party/boost/leaf/all.hpp)
# =======================================
# utfcpp
# =======================================
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/LIEF/third-party/utfcpp/utf8.h
COMMAND
${CMAKE_COMMAND} -E copy_directory ${UTFCPP_INCLUDE_DIR}/
@ -375,6 +381,7 @@ target_compile_definitions(LIB_LIEF PUBLIC -D_GLIBCXX_USE_CXX11_ABI=1)
# extension.
add_definitions(-DMBEDTLS_MD2_C -DMBEDTLS_MD4_C -DMBEDTLS_PEM_PARSE_C
-DMBEDTLS_X509_CRT_PARSE_C -DMBEDTLS_PEM_WRITE_C
-DMBEDTLS_PKCS1_V15 -DMBEDTLS_PKCS1_V21
-DMBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION)
# ASAN - LSAN - TSAN - USAN

View File

@ -166,15 +166,22 @@ void create<Binary>(py::module& m) {
"algorithm"_a)
.def("verify_signature",
static_cast<Signature::VERIFICATION_FLAGS(Binary::*)() const>(&Binary::verify_signature),
static_cast<Signature::VERIFICATION_FLAGS(Binary::*)(Signature::VERIFICATION_CHECKS) const>(&Binary::verify_signature),
R"delim(
Verify the binary against the embedded signature(s) (if any)
Firstly, it checks that the embedded signatures are correct (c.f. :meth:`lief.PE.Signature.check`)
and then it checks that the authentihash matches :attr:`lief.PE.ContentInfo.digest`
)delim")
One can tweak the verification process with the :class:`lief.PE.Signature.VERIFICATION_CHECKS` flags
.. seealso::
:meth:`lief.PE.Signature.check`
)delim",
"checks"_a = Signature::VERIFICATION_CHECKS::DEFAULT)
.def("verify_signature",
static_cast<Signature::VERIFICATION_FLAGS(Binary::*)(const Signature&) const>(&Binary::verify_signature),
static_cast<Signature::VERIFICATION_FLAGS(Binary::*)(const Signature&, Signature::VERIFICATION_CHECKS) const>(&Binary::verify_signature),
R"delim(
Verify the binary with the Signature object provided in the first parameter
It can be used to verify a detached signature:
@ -184,7 +191,7 @@ void create<Binary>(py::module& m) {
detached = lief.PE.Signature.parse("sig.pkcs7")
binary.verify_signature(detached)
)delim",
"signature"_a)
"signature"_a, "checks"_a = Signature::VERIFICATION_CHECKS::DEFAULT)
.def_property_readonly("authentihash_md5",
[] (const Binary& bin) {

View File

@ -49,9 +49,10 @@ void create<PKCS9CounterSignature>(py::module& m) {
}
)delim")
.def_property_readonly("signers",
&PKCS9CounterSignature::signers,
"Iterator over the " RST_CLASS_REF(lief.PE.SignerInfo) " as described in the RFC")
.def_property_readonly("signer",
&PKCS9CounterSignature::signer,
"Return the " RST_CLASS_REF(lief.PE.SignerInfo) " as described in the RFC #2985",
py::return_value_policy::reference)
.def("__hash__",
[] (const PKCS9CounterSignature& obj) {

View File

@ -51,7 +51,24 @@ void create<Signature>(py::module& m) {
.value("MISSING_PKCS9_MESSAGE_DIGEST", Signature::VERIFICATION_FLAGS::MISSING_PKCS9_MESSAGE_DIGEST)
.value("BAD_DIGEST", Signature::VERIFICATION_FLAGS::BAD_DIGEST)
.value("BAD_SIGNATURE", Signature::VERIFICATION_FLAGS::BAD_SIGNATURE)
.value("NO_SIGNATURE", Signature::VERIFICATION_FLAGS::NO_SIGNATURE);
.value("NO_SIGNATURE", Signature::VERIFICATION_FLAGS::NO_SIGNATURE)
.value("CERT_EXPIRED", Signature::VERIFICATION_FLAGS::CERT_EXPIRED)
.value("CERT_FUTURE", Signature::VERIFICATION_FLAGS::CERT_FUTURE);
LIEF::enum_<Signature::VERIFICATION_CHECKS>(signature, "VERIFICATION_CHECKS", py::arithmetic(),
R"delim(
Flags to tweak the verification process of the signature
See :meth:`lief.PE.Signature.check` and :meth:`lief.PE.Binary.verify_signature`
)delim")
.value("DEFAULT", Signature::VERIFICATION_CHECKS::DEFAULT,
"Default behavior that tries to follow the Microsoft verification process as close as possible")
.value("HASH_ONLY", Signature::VERIFICATION_CHECKS::HASH_ONLY,
"Only check that :meth:`lief.PE.Binary.authentihash` matches :attr:`lief.PE.ContentInfo.digest` regardless of the signature's validity")
.value("LIFETIME_SIGNING", Signature::VERIFICATION_CHECKS::LIFETIME_SIGNING,
"Same semantic as `WTD_LIFETIME_SIGNING_FLAG <https://docs.microsoft.com/en-us/windows/win32/api/wintrust/ns-wintrust-wintrust_data#WTD_LIFETIME_SIGNING_FLAG>`")
.value("SKIP_CERT_TIME", Signature::VERIFICATION_CHECKS::SKIP_CERT_TIME,
"Skip the verification of the certificates time validities so that even though a certificate expired, it returns :attr:`lief.PE.Signature.VERIFICATION_FLAGS.OK`");
signature
.def_static("parse",
@ -78,37 +95,65 @@ void create<Signature>(py::module& m) {
.def_property_readonly("version",
&Signature::version,
"Should be 1")
"Version of the signature. It should be 1")
.def_property_readonly("digest_algorithm",
&Signature::digest_algorithm,
"Return the algorithm (" RST_CLASS_REF(lief.PE.ALGORITHMS) ") \
used to sign the content of " RST_CLASS_REF(lief.PE.ContentInfo) "")
.def_property_readonly("content_info",
&Signature::content_info,
"Return the " RST_CLASS_REF(lief.PE.ContentInfo) "",
py::return_value_policy::reference)
.def_property_readonly("certificates",
&Signature::certificates,
"Return an iterator over " RST_CLASS_REF(lief.PE.x509) " certificates",
py::return_value_policy::reference)
.def_property_readonly("signers",
&Signature::signers,
"Return an iterator over the signers: " RST_CLASS_REF(lief.PE.SignerInfo) "",
py::return_value_policy::reference)
.def("find_crt",
static_cast<const x509*(Signature::*)(const std::vector<uint8_t>&) const>(&Signature::find_crt),
"Find the " RST_CLASS_REF(lief.PE.x509) " certificate according to its serial number",
py::return_value_policy::reference,
"serialno"_a)
.def("find_crt_subject",
static_cast<const x509*(Signature::*)(const std::string&) const>(&Signature::find_crt_subject),
"Find the " RST_CLASS_REF(lief.PE.x509) " certificate according to its subject",
py::return_value_policy::reference,
"subject"_a)
.def("find_crt_subject",
static_cast<const x509*(Signature::*)(const std::string&, const std::vector<uint8_t>&) const>(&Signature::find_crt_subject),
"Find the " RST_CLASS_REF(lief.PE.x509) " certificate according to its subject **AND** its serial number",
py::return_value_policy::reference,
"subject"_a, "serialno"_a)
.def("find_crt_issuer",
static_cast<const x509*(Signature::*)(const std::string&) const>(&Signature::find_crt_issuer),
"Find the " RST_CLASS_REF(lief.PE.x509) " certificate according to its issuer",
py::return_value_policy::reference,
"issuer"_a)
.def("find_crt_issuer",
static_cast<const x509*(Signature::*)(const std::string&, const std::vector<uint8_t>&) const>(&Signature::find_crt_issuer),
"Find the " RST_CLASS_REF(lief.PE.x509) " certificate according to its issuer **AND** its serial number",
py::return_value_policy::reference,
"issuer"_a, "serialno"_a)
.def("check",
&Signature::check,
// Note: This documentation needs to be sync with LIEF::PE::Signature::check
R"delim(
Check the integrity of the signature and return a :class:`lief.PE.Signature.VERIFICATION_FLAGS`
It performs the following verifications:
By default, it performs the following verifications:
1. It must contain only **one** signer info (:attr:`~lief.PE.Signature.signers`)
2. :attr:`lief.PE.Signature.digest_algorithm` must match:
@ -125,8 +170,15 @@ void create<Signature>(py::module& m) {
5. If they are Authenticated attributes, check that a PKCS9_MESSAGE_DIGEST (:class:`lief.PE.PKCS9MessageDigest`) attribute exists
and that its value matches hash of ContentInfo
6. Check the validity of the PKCS #9 counter signature if present
7. If the signature doesn't embed a signing-time in the counter signature, check the certificate
validity. (See :attr:`lief.PE.Signature.VERIFICATION_CHECKS.LIFETIME_SIGNING` and :attr:`lief.pe.Signature.VERIFICATION_CHECKS.SKIP_CERT_TIME`)
)delim")
See: :class:`lief.PE.Signature.VERIFICATION_CHECKS` to tweak the behavior
)delim",
"checks"_a = Signature::VERIFICATION_CHECKS::DEFAULT
)
.def_property_readonly("raw_der",
[] (const Signature& sig) {

View File

@ -102,10 +102,34 @@ void create<SignerInfo>(py::module& m) {
.def("get_attribute",
&SignerInfo::get_attribute,
"Return the authenticated or un-authenticated attribute matching the "
"given " RST_CLASS_REF(lief.PE.SIG_ATTRIBUTE_TYPES) " \n\n"
"It returns **the first** entry that matches the given type. If it can't be "
"found, it returns a nullptr",
R"delim(
Return the authenticated or un-authenticated attribute matching the
given :class:`lief.PE.SIG_ATTRIBUTE_TYPES`
It returns **the first** entry that matches the given type. If it can't be
found, it returns None
)delim",
"type"_a,
py::return_value_policy::reference)
.def("get_auth_attribute",
&SignerInfo::get_auth_attribute,
R"delim(
Return the authenticated attribute matching the
given :class:`lief.PE.SIG_ATTRIBUTE_TYPES`
It returns **the first** entry that matches the given type. If it can't be
found, it returns None
)delim",
"type"_a,
py::return_value_policy::reference)
.def("get_unauth_attribute",
&SignerInfo::get_unauth_attribute,
R"delim(
Return the un-authenticated attribute matching the
given :class:`lief.PE.SIG_ATTRIBUTE_TYPES`
It returns **the first** entry that matches the given type. If it can't be
found, it returns a nullptr
)delim",
"type"_a,
py::return_value_policy::reference)

View File

@ -72,6 +72,17 @@ void create<x509>(py::module& m) {
.value("RSA_ALT", x509::KEY_TYPES::RSA_ALT, "RSA scheme with an alternative implementation for signing and decrypting")
.value("RSASSA_PSS", x509::KEY_TYPES::RSASSA_PSS, "RSA Probabilistic signature scheme");
LIEF::enum_<x509::KEY_USAGE>(cls_x509, "KEY_USAGE", "Key usage as defined in `RFC #5280 - section-4.2.1.3 <https://tools.ietf.org/html/rfc5280#section-4.2.1.3>`_")
.value("DIGITAL_SIGNATURE", x509::KEY_USAGE::DIGITAL_SIGNATURE, "The key is used for digital signature")
.value("NON_REPUDIATION", x509::KEY_USAGE::NON_REPUDIATION, "The key is used for digital signature AND to protects against falsely denying some action")
.value("KEY_ENCIPHERMENT", x509::KEY_USAGE::KEY_ENCIPHERMENT, "The key is used for enciphering private or secret keys")
.value("DATA_ENCIPHERMENT", x509::KEY_USAGE::DATA_ENCIPHERMENT, "The key is used for directly enciphering raw user data without the use of an intermediate symmetric cipher")
.value("KEY_AGREEMENT", x509::KEY_USAGE::KEY_AGREEMENT, "The Key is used for key agreement. (e.g. with Diffie-Hellman)")
.value("KEY_CERT_SIGN", x509::KEY_USAGE::KEY_CERT_SIGN, "The key is used for verifying signatures on public key certificates")
.value("CRL_SIGN", x509::KEY_USAGE::CRL_SIGN, "The key is used for verifying signatures on certificate revocation lists")
.value("ENCIPHER_ONLY", x509::KEY_USAGE::ENCIPHER_ONLY, "In **association with** KEY_AGREEMENT (otherwise the meaning is undefined), the key is only used for enciphering data while performing key agreement")
.value("DECIPHER_ONLY", x509::KEY_USAGE::DECIPHER_ONLY, "In **association with** KEY_AGREEMENT (otherwise the meaning is undefined), the key is only used for deciphering data while performing key agreement");
cls_x509
.def_static("parse",
static_cast<x509::certificates_t(*)(const std::string&)>(&x509::parse),
@ -137,6 +148,27 @@ void create<x509>(py::module& m) {
"Otherwise, return None",
py::return_value_policy::take_ownership)
.def_property_readonly("key_usage",
&x509::key_usage,
"Purpose of the key contained in the certificate (see " RST_CLASS_REF(lief.PE.x509.KEY_USAGE) ")")
.def_property_readonly("ext_key_usage",
&x509::ext_key_usage,
"Indicates one or more purposes for which the certified public key may be used (list of OID)")
.def_property_readonly("certificate_policies",
&x509::certificate_policies,
"Policy information terms as list of OID (see RFC #5280)")
.def_property_readonly("is_ca",
&x509::is_ca)
.def_property_readonly("signature",
[] (const x509& cert) -> py::bytes {
const std::vector<uint8_t>& sig = cert.signature();
return py::bytes(reinterpret_cast<const char*>(sig.data()), sig.size());
}, "The signature of the certificate")
.def("verify",
static_cast<x509::VERIFICATION_FLAGS(x509::*)(const x509&) const>(&x509::verify),
R"delim(

View File

@ -41,7 +41,6 @@ void init_python_module(py::module& m) {
void init_objects(py::module& m) {
CREATE(Parser, m);
CREATE(Binary, m);
CREATE(DosHeader, m);
CREATE(Header, m);
CREATE(OptionalHeader, m);
@ -103,6 +102,7 @@ void init_objects(py::module& m) {
CREATE(LoadConfigurationV6, m);
CREATE(LoadConfigurationV7, m);
CREATE(Binary, m);
CREATE(Builder, m);
}

View File

@ -0,0 +1,302 @@
#!/usr/bin/env python
import lief
from lief.PE import oid_to_string
import argparse
import json
import sys
import string
import argparse
import traceback
import pathlib
try:
from prettyprinter import pprint
except ImportError:
from pprint import pprint
class exceptions_handler(object):
func = None
def __init__(self, exceptions, on_except_callback=None):
self.exceptions = exceptions
self.on_except_callback = on_except_callback
def __call__(self, *args, **kwargs):
if self.func is None:
self.func = args[0]
return self
try:
return self.func(*args, **kwargs)
except self.exceptions as e:
if self.on_except_callback is not None:
self.on_except_callback(e)
else:
print("-" * 60)
print("Exception in {}: {}".format(self.func.__name__, e))
exc_type, exc_value, exc_traceback = sys.exc_info()
traceback.print_tb(exc_traceback)
print("-" * 60)
@exceptions_handler(Exception)
def print_attr(indent: int, auth: lief.PE.Attribute):
if auth.type == lief.PE.SIG_ATTRIBUTE_TYPES.CONTENT_TYPE:
print_content_type(indent, auth)
elif auth.type == lief.PE.SIG_ATTRIBUTE_TYPES.PKCS9_SIGNING_TIME:
print_signing_time(indent, auth)
elif auth.type == lief.PE.SIG_ATTRIBUTE_TYPES.MS_SPC_STATEMENT_TYPE:
print_ms_statement_type(indent, auth)
elif auth.type == lief.PE.SIG_ATTRIBUTE_TYPES.PKCS9_MESSAGE_DIGEST:
print_pkcs_msg_dg(indent, auth)
elif auth.type == lief.PE.SIG_ATTRIBUTE_TYPES.PKCS9_COUNTER_SIGNATURE:
print_pkcs_counter_sig(indent, auth)
elif auth.type == lief.PE.SIG_ATTRIBUTE_TYPES.GENERIC_TYPE:
print_generic_type(indent, auth)
elif auth.type == lief.PE.SIG_ATTRIBUTE_TYPES.SPC_SP_OPUS_INFO:
print_spc_sp_opus_info(indent, auth)
elif auth.type == lief.PE.SIG_ATTRIBUTE_TYPES.MS_SPC_NESTED_SIGN:
print_ms_nested_sig(indent, auth)
elif auth.type == lief.PE.SIG_ATTRIBUTE_TYPES.PKCS9_AT_SEQUENCE_NUMBER:
print_pkcs9_at_seq_number(indent, auth)
else:
print(" " * indent, type(auth), auth)
@exceptions_handler(Exception)
def print_pkcs9_at_seq_number(indent: int, auth: lief.PE.PKCS9AtSequenceNumber):
print("{} PKCS #9 sequence number: {}".format(" " * indent, auth.number))
@exceptions_handler(Exception)
def print_ms_nested_sig(indent: int, auth: lief.PE.MsSpcNestedSignature):
print("{} MS Nested Signature:".format(" " * indent))
print_all(auth.signature, indent + 2)
@exceptions_handler(Exception)
def print_spc_sp_opus_info(indent: int, auth: lief.PE.SpcSpOpusInfo):
if len(auth.program_name) > 0 and len(auth.more_info) > 0:
print("{} Info: {} {}".format(" " * indent, auth.program_name, auth.more_info))
elif len(auth.program_name) > 0 and len(auth.more_info) == 0:
print("{} Info: {}".format(" " * indent, auth.program_name))
elif len(auth.program_name) == 0 and len(auth.more_info) > 0:
print("{} Info: {}".format(" " * indent, auth.more_info))
else:
print("{} Info: <empty>".format(" " * indent))
@exceptions_handler(Exception)
def print_generic_type(indent: int, auth: lief.PE.GenericType):
print("{} Generic Type {} ({})".format(" " * indent, auth.oid, lief.PE.oid_to_string(auth.oid)))
@exceptions_handler(Exception)
def print_content_type(indent: int, auth: lief.PE.ContentType):
print("{} Content Type OID: {} ({})".format(" " * indent, auth.oid, lief.PE.oid_to_string(auth.oid)))
@exceptions_handler(Exception)
def print_signing_time(indent: int, auth: lief.PE.PKCS9SigningTime):
print("{} Signing Time: {}/{:02}/{:02} - {:02}:{:02}:{:02}".format(" " * indent, *auth.time))
@exceptions_handler(Exception)
def print_ms_statement_type(indent: int, auth: lief.PE.MsSpcStatementType):
print("{} MS Statement type OID: {} ({})".format(" " * indent, auth.oid, lief.PE.oid_to_string(auth.oid)))
@exceptions_handler(Exception)
def print_pkcs_msg_dg(indent: int, auth: lief.PE.PKCS9MessageDigest):
print("{} PKCS9 Message Digest: {}".format(" " * indent, auth.digest.hex()))
@exceptions_handler(Exception)
def print_crt(indent: int, crt: lief.PE.x509):
print("{} Version : {:d}".format(" " * indent, crt.version))
print("{} Issuer : {}".format(" " * indent, crt.issuer))
print("{} Subject : {}".format(" " * indent, crt.subject))
print("{} Serial Number : {}".format(" " * indent, crt.serial_number.hex()))
print("{} Signature Algorithm: {}".format(" " * indent, lief.PE.oid_to_string(crt.signature_algorithm)))
print("{} Valid from : {}/{:02d}/{:02d} - {:02d}:{:02d}:{:02d}".format(" " * indent, *crt.valid_from))
print("{} Valid to : {}/{:02d}/{:02d} - {:02d}:{:02d}:{:02d}".format(" " * indent, *crt.valid_to))
if len(crt.key_usage) > 0:
print("{} Key usage : {}".format(" " * indent, " - ".join(str(e).split(".")[-1] for e in crt.key_usage)))
if len(crt.ext_key_usage) > 0:
print("{} Ext key usage : {}".format(" " * indent, " - ".join(lief.PE.oid_to_string(e) for e in crt.ext_key_usage)))
if crt.rsa_info is not None:
rsa_info = crt.rsa_info
print("{} RSA key size : {}".format(" " * indent, rsa_info.key_size))
print("{} ===========================================".format(" " * indent))
@exceptions_handler(Exception)
def print_pkcs_counter_sig(indent: int, auth: lief.PE.PKCS9CounterSignature):
print("{} PKCS9 counter signature".format(" " * indent))
signer = auth.signer
print("{} Version : {:d}".format(" " * indent, signer.version))
print("{} Serial Number : {}".format(" " * indent, signer.serial_number.hex()))
print("{} Issuer : {}".format(" " * indent, signer.issuer))
print("{} Digest Algorithm : {}".format(" " * indent, signer.digest_algorithm))
print("{} Encryption Algorithm: {}".format(" " * indent, signer.encryption_algorithm))
print("{} Encrypted Digest : {} ...".format(" " * indent, signer.encrypted_digest.hex()[:20]))
if len(signer.authenticated_attributes) > 0:
print("{} Authenticated attributes:".format(" " * indent))
for auth in signer.authenticated_attributes:
print_attr(indent + 4, auth)
if len(signer.unauthenticated_attributes) > 0:
print("{} Un-Authenticated attributes:".format(" " * indent))
for auth in signer.unauthenticated_attributes:
print_attr(indent + 4, auth)
@exceptions_handler(Exception)
def print_all(sig: lief.PE.Signature, indent: int = 2):
ci: lief.PE.ContentInfo = sig.content_info
print("{}Signature version : {}".format(" " * indent, sig.version))
print("{}Digest Algorithm : {!s}".format(" " * indent, sig.digest_algorithm))
print("{}Content Info:".format(" " * indent))
print("{} Content Type : {!s} ({})".format(" " * indent, ci.content_type, lief.PE.oid_to_string(ci.content_type)))
print("{} Digest Algorithm: {!s}".format(" " * indent, ci.digest_algorithm))
print("{} Digest : {!s}".format(" " * indent, ci.digest.hex()))
print("{}Certificates".format(" " * indent))
for crt in sig.certificates:
print_crt(indent, crt)
print("{}Signer(s)".format(" " * indent))
for signer in sig.signers:
print("{} Version : {:d}".format(" " * indent, signer.version))
print("{} Serial Number : {}".format(" " * indent, signer.serial_number.hex()))
print("{} Issuer : {}".format(" " * indent, signer.issuer))
print("{} Digest Algorithm : {}".format(" " * indent, signer.digest_algorithm))
print("{} Encryption Algorithm: {}".format(" " * indent, signer.encryption_algorithm))
print("{} Encrypted Digest : {} ...".format(" " * indent, signer.encrypted_digest.hex()[:20]))
if len(signer.authenticated_attributes) > 0:
print("{} Authenticated attributes:".format(" " * indent))
for auth in signer.authenticated_attributes:
print_attr(indent + 4, auth)
if len(signer.unauthenticated_attributes) > 0:
print("{} Un-authenticated attributes:".format(" " * indent))
for auth in signer.unauthenticated_attributes:
print_attr(indent + 4, auth)
@exceptions_handler(Exception)
def show_crts(sig: lief.PE.Signature, args):
for crt in sig.certificates:
print_crt(0, crt)
@exceptions_handler(Exception)
def process_signature(sig: lief.PE.Signature, args):
if args.show_all:
print_all(sig)
if args.show_crt:
show_crts(sig, args)
if args.show_hash:
print("Authentihash: {}".format(sig.content_info.digest.hex()))
def main():
parser = argparse.ArgumentParser()
parser.add_argument("file")
parser.add_argument('-a', '--all',
action='store_true', dest='show_all',
help='Show all information')
parser.add_argument('-c', '--crt',
action='store_true', dest='show_crt',
help='Show embedded x509 certificates')
parser.add_argument('-H', '--hash',
action='store_true', dest='show_hash',
help='Show the autentihash value')
parser.add_argument('-C', '--check',
action='store_true', dest='check_sig',
help='Check the signature')
parser.add_argument('-D', '--allow-expired',
action='store_true', dest='allow_expired',
help='Allow expired certificates')
parser.add_argument('-s', '--save',
dest='ext_file_path',
help='Extract and save the PKCS #7')
# Logging setup
logger_group = parser.add_argument_group('Logger')
verbosity = logger_group.add_mutually_exclusive_group()
verbosity.add_argument('--debug',
dest='main_verbosity',
action='store_const',
const=lief.logging.LOGGING_LEVEL.DEBUG)
verbosity.add_argument('--trace',
dest='main_verbosity',
action='store_const',
const=lief.logging.LOGGING_LEVEL.TRACE)
verbosity.add_argument('--info',
dest='main_verbosity',
action='store_const',
const=lief.logging.LOGGING_LEVEL.INFO)
verbosity.add_argument('--warn',
dest='main_verbosity',
action='store_const',
const=lief.logging.LOGGING_LEVEL.WARNING)
verbosity.add_argument('--err',
dest='main_verbosity',
action='store_const',
const=lief.logging.LOGGING_LEVEL.ERROR)
verbosity.add_argument('--critical',
dest='main_verbosity',
action='store_const',
const=lief.logging.LOGGING_LEVEL.CRITICAL)
parser.set_defaults(main_verbosity=lief.logging.LOGGING_LEVEL.WARNING)
args = parser.parse_args()
lief.logging.set_level(args.main_verbosity)
if lief.PE.is_pe(args.file):
binary = None
try:
binary: lief.PE.Binary = lief.PE.parse(args.file)
if binary is None:
print("Error while parsing {}".format(args.file))
sys.exit(1)
except lief.exception as e:
print(e)
sys.exit(1)
if args.check_sig:
flags = lief.PE.Signature.VERIFICATION_CHECKS.DEFAULT
if args.allow_expired:
flags = lief.PE.Signature.VERIFICATION_CHECKS.SKIP_CERT_TIME
res = binary.verify_signature(flags)
print(res)
if args.show_hash:
print("Binary MD5 authentihash: {}".format(binary.authentihash_md5.hex()))
print("Binary SHA-1 authentihash: {}".format(binary.authentihash_sha1.hex()))
print("Binary SHA-256 authentihash: {}".format(binary.authentihash_sha256.hex()))
for idx, sig in enumerate(binary.signatures):
process_signature(sig, args)
if args.ext_file_path:
path = args.ext_file_path
if idx > 0:
path += str(idx)
if not path.endswith(".p7b"):
path += ".p7b"
outpath = pathlib.Path(path)
outpath.write_bytes(sig.raw_der)
print("Signature saved to {}".format(outpath))
else:
# Try as a regular p7b signature
sig = lief.PE.Signature.parse(args.file)
if sig is None:
print("Fail to parse the signature")
sys.exit(1)
process_signature(sig, args)
if __name__ == "__main__":
main()

View File

@ -1,62 +0,0 @@
#!/usr/bin/env python
import lief
import argparse
import json
import sys
from prettyprinter import pprint
def main():
parser = argparse.ArgumentParser()
parser.add_argument("pe_file")
# Logging setup
logger_group = parser.add_argument_group('Logger')
verbosity = logger_group.add_mutually_exclusive_group()
verbosity.add_argument('--debug',
dest='main_verbosity',
action='store_const',
const=lief.logging.LOGGING_LEVEL.DEBUG)
verbosity.add_argument('--trace',
dest='main_verbosity',
action='store_const',
const=lief.logging.LOGGING_LEVEL.TRACE)
verbosity.add_argument('--info',
dest='main_verbosity',
action='store_const',
const=lief.logging.LOGGING_LEVEL.INFO)
verbosity.add_argument('--warn',
dest='main_verbosity',
action='store_const',
const=lief.logging.LOGGING_LEVEL.WARNING)
verbosity.add_argument('--err',
dest='main_verbosity',
action='store_const',
const=lief.logging.LOGGING_LEVEL.ERROR)
verbosity.add_argument('--critical',
dest='main_verbosity',
action='store_const',
const=lief.logging.LOGGING_LEVEL.CRITICAL)
parser.set_defaults(main_verbosity=lief.logging.LOGGING_LEVEL.WARNING)
args = parser.parse_args()
lief.logging.set_level(args.main_verbosity)
binary = None
try:
binary = lief.PE.parse(args.pe_file)
except lief.exception as e:
print(e)
sys.exit(1)
for sig in binary.signatures:
print(sig)
if __name__ == "__main__":
main()

View File

@ -40,6 +40,14 @@ class VectorStream : public BinaryStream {
}
inline uint8_t* start() {
return this->binary_.data();
}
inline const uint8_t* start() const {
return this->binary_.data();
}
inline uint8_t* end() {
return this->binary_.data() + this->binary_.size();
}

View File

@ -145,9 +145,14 @@ class LIEF_API Binary : public LIEF::Binary {
it_const_signatures signatures(void) const;
//! Verify the binary against the embedded signature(s) (if any)
//! Firstly, it checks that the embedded signatures are correct (c.f. Signature::check)
//! First, it checks that the embedded signatures are correct (c.f. Signature::check)
//! and then it checks that the authentihash matches ContentInfo::digest
Signature::VERIFICATION_FLAGS verify_signature() const;
//!
//! One can tweak the verification process with the Signature::VERIFICATION_CHECKS flags
//!
//! @see LIEF::PE::Signature::check
Signature::VERIFICATION_FLAGS verify_signature(
Signature::VERIFICATION_CHECKS checks = Signature::VERIFICATION_CHECKS::DEFAULT) const;
//! Verify the binary with the Signature object provided in the first parameter
//! It can be used to verify a detached signature:
@ -158,7 +163,8 @@ class LIEF_API Binary : public LIEF::Binary {
//! binary->verify_signature(detached.value());
//! }
//! \endcode
Signature::VERIFICATION_FLAGS verify_signature(const Signature& sig) const;
Signature::VERIFICATION_FLAGS verify_signature(const Signature& sig,
Signature::VERIFICATION_CHECKS checks = Signature::VERIFICATION_CHECKS::DEFAULT) const;
//! Compute the authentihash according to the algorithm provided in the first
//! parameter

View File

@ -45,7 +45,7 @@ class LIEF_API Signature : public Object {
static std::vector<uint8_t> hash(const std::vector<uint8_t>& input, ALGORITHMS algo);
public:
//! Flags returned by verification fonctions
//! Flags returned by verification functions
enum class VERIFICATION_FLAGS {
OK = 0,
INVALID_SIGNER = 1 << 0,
@ -58,6 +58,18 @@ class LIEF_API Signature : public Object {
BAD_DIGEST = 1 << 7,
BAD_SIGNATURE = 1 << 8,
NO_SIGNATURE = 1 << 9,
CERT_EXPIRED = 1 << 10,
CERT_FUTURE = 1 << 11,
};
//! Flags to tweak the verification process of the signature
//!
//! See Signature::check and LIEF::PE::Binary::verify_signature
enum class VERIFICATION_CHECKS {
DEFAULT = 1 << 0, /**< Default behavior that tries to follow the Microsoft verification process as close as possible */
HASH_ONLY = 1 << 1, /**< Only check that Binary::authentihash matches ContentInfo::digest regardless of the signature's validity */
LIFETIME_SIGNING = 1 << 2, /**< Same semantic as [WTD_LIFETIME_SIGNING_FLAG](https://docs.microsoft.com/en-us/windows/win32/api/wintrust/ns-wintrust-wintrust_data#WTD_LIFETIME_SIGNING_FLAG) */
SKIP_CERT_TIME = 1 << 3, /**< Skip the verification of the certificates time validities so that even though a certificate expired, it returns VERIFICATION_FLAGS::OK */
};
Signature(void);
@ -86,10 +98,25 @@ class LIEF_API Signature : public Object {
//! Return the raw original PKCS7 signature
const std::vector<uint8_t>& raw_der(void) const;
virtual void accept(Visitor& visitor) const override;
//! Find x509 certificate according to its serial number
const x509* find_crt(const std::vector<uint8_t>& serialno) const;
//! Find x509 certificate according to its subject
const x509* find_crt_subject(const std::string& subject) const;
//! Find x509 certificate according to its subject **AND** serial number
const x509* find_crt_subject(const std::string& subject, const std::vector<uint8_t>& serialno) const;
//! Find x509 certificate according to its issuer
const x509* find_crt_issuer(const std::string& issuer) const;
//! Find x509 certificate according to its issuer **AND** serial number
const x509* find_crt_issuer(const std::string& issuer, const std::vector<uint8_t>& serialno) const;
//! Check if this signature is valid according to the Authenticode/PKCS #7 verification scheme
//!
//! By default, it performs the following verifications:
//!
//! 1. It must contain only **one** signer info
//! 2. Signature::digest_algorithm must match:
//! * ContentInfo::digest_algorithm
@ -99,16 +126,22 @@ class LIEF_API Signature : public Object {
//! 4. Given the x509 certificate, compare SignerInfo::encrypted_digest against either:
//! * hash of authenticated attributes if present
//! * hash of ContentInfo
//! 5. If they are Authenticated attributes, check that a PKCS9_MESSAGE_DIGEST attribute exists
//! 5. If authenticated attributes are present, check that a PKCS9_MESSAGE_DIGEST attribute exists
//! and that its value matches hash of ContentInfo
VERIFICATION_FLAGS check() const;
//! 6. Check the validity of the PKCS #9 counter signature if present
//! 7. If the signature doesn't embed a signing-time in the counter signature, check the certificate
//! validity. (See LIEF::PE::Signature::VERIFICATION_CHECKS::LIFETIME_SIGNING and LIEF::PE::Signature::VERIFICATION_CHECKS::SKIP_CERT_TIME)
//!
//! See: LIEF::PE::Signature::VERIFICATION_CHECKS to tweak the behavior
VERIFICATION_FLAGS check(VERIFICATION_CHECKS checks = VERIFICATION_CHECKS::DEFAULT) const;
virtual void accept(Visitor& visitor) const override;
virtual ~Signature(void);
LIEF_API friend std::ostream& operator<<(std::ostream& os, const Signature& signature);
private:
uint32_t version_ = 0;
ALGORITHMS digest_algorithm_ = ALGORITHMS::UNKNOWN;
ContentInfo content_info_;
@ -118,9 +151,6 @@ class LIEF_API Signature : public Object {
uint64_t content_info_start_ = 0;
uint64_t content_info_end_ = 0;
uint64_t auth_start_ = 0;
uint64_t auth_end_ = 0;
std::vector<uint8_t> original_raw_signature_;
};
@ -129,6 +159,7 @@ class LIEF_API Signature : public Object {
}
ENABLE_BITMASK_OPERATORS(LIEF::PE::Signature::VERIFICATION_FLAGS);
ENABLE_BITMASK_OPERATORS(LIEF::PE::Signature::VERIFICATION_CHECKS);
#endif

View File

@ -68,7 +68,7 @@ class LIEF_API SignatureParser {
result<ContentInfo> parse_content_info(VectorStream& stream, range_t& range);
result<x509_certificates_t> parse_certificates(VectorStream& stream);
result<signer_infos_t> parse_signer_infos(VectorStream& stream, range_t& auth_attr);
result<signer_infos_t> parse_signer_infos(VectorStream& stream);
result<attributes_t> parse_attributes(VectorStream& stream);
result<std::unique_ptr<Attribute>> parse_content_type(VectorStream& stream);

View File

@ -27,6 +27,7 @@
namespace LIEF {
namespace PE {
class Signature;
class Attribute;
class Parser;
class SignatureParser;
@ -52,6 +53,7 @@ class LIEF_API SignerInfo : public Object {
friend class Parser;
friend class SignatureParser;
friend class Signature;
public:
using encrypted_digest_t = std::vector<uint8_t>;
@ -110,6 +112,18 @@ class LIEF_API SignerInfo : public Object {
//! found, it returns a nullptr.
const Attribute* get_attribute(PE::SIG_ATTRIBUTE_TYPES type) const;
//! Return the authenticated attribute matching the given PE::SIG_ATTRIBUTE_TYPES.
//!
//! It returns **the first** entry that matches the given type. If it can't be
//! found, it returns a nullptr.
const Attribute* get_auth_attribute(PE::SIG_ATTRIBUTE_TYPES type) const;
//! Return the un-authenticated attribute matching the given PE::SIG_ATTRIBUTE_TYPES.
//!
//! It returns **the first** entry that matches the given type. If it can't be
//! found, it returns a nullptr.
const Attribute* get_unauth_attribute(PE::SIG_ATTRIBUTE_TYPES type) const;
//! x509 certificate used by this signer. If it can't be found, it returns a nullptr
inline const x509* cert() const {
return this->cert_.get();
@ -120,6 +134,11 @@ class LIEF_API SignerInfo : public Object {
return this->cert_.get();
}
//! Raw blob that is signed by the signer certificate
const std::vector<uint8_t> raw_auth_data() const {
return this->raw_auth_data_;
}
virtual void accept(Visitor& visitor) const override;
virtual ~SignerInfo(void);
@ -127,7 +146,7 @@ class LIEF_API SignerInfo : public Object {
LIEF_API friend std::ostream& operator<<(std::ostream& os, const SignerInfo& signer_info);
private:
uint32_t version_;
uint32_t version_ = 0;
std::string issuer_;
std::vector<uint8_t> serialno_;
@ -135,11 +154,13 @@ class LIEF_API SignerInfo : public Object {
ALGORITHMS digest_enc_algorithm_ = ALGORITHMS::UNKNOWN;
encrypted_digest_t encrypted_digest_;
std::vector<uint8_t> raw_auth_data_;
std::vector<std::unique_ptr<Attribute>> authenticated_attributes_;
std::vector<std::unique_ptr<Attribute>> unauthenticated_attributes_;
std::unique_ptr<x509> cert_;
};
}

View File

@ -48,15 +48,15 @@ class LIEF_API PKCS9CounterSignature : public Attribute {
public:
PKCS9CounterSignature();
PKCS9CounterSignature(std::vector<SignerInfo> signers);
PKCS9CounterSignature(SignerInfo signer);
PKCS9CounterSignature(const PKCS9CounterSignature&);
PKCS9CounterSignature& operator=(const PKCS9CounterSignature&);
virtual std::unique_ptr<Attribute> clone(void) const override;
//! Iterator over the SignerInfo as described in the RFC
inline it_const_signers_t signers() const {
return this->signers_;
//! SignerInfo as described in the RFC #2985
inline const SignerInfo& signer() const {
return this->signer_;
}
//! Print information about the attribute
@ -67,7 +67,7 @@ class LIEF_API PKCS9CounterSignature : public Attribute {
virtual ~PKCS9CounterSignature();
private:
std::vector<SignerInfo> signers_;
SignerInfo signer_;
};
}

View File

@ -34,6 +34,7 @@ namespace PE {
class Parser;
class SignatureParser;
class Signature;
class RsaInfo;
@ -42,6 +43,7 @@ class LIEF_API x509 : public Object {
friend class Parser;
friend class SignatureParser;
friend class Signature;
public:
//! Tuple (Year, Month, Day, Hour, Minute, Second)
@ -55,6 +57,15 @@ class LIEF_API x509 : public Object {
//! Parse x500 certificate(s) from raw blob
static certificates_t parse(const std::vector<uint8_t>& content);
//! Return True if ``before`` is *before* than ``after``. False otherwise
static bool check_time(const date_t& before, const date_t& after);
//! True if the given time is in the **past** according to the clock's system
static bool time_is_past(const date_t& to);
//! True if the given time is in the future according to the clock's system
static bool time_is_future(const date_t& from);
//! Public key scheme
enum class KEY_TYPES {
NONE = 0, ///< Unknown scheme
@ -93,6 +104,19 @@ class LIEF_API x509 : public Object {
BADCRL_BAD_KEY = 1 << 19, /**< The CRL is signed with an unacceptable key (eg bad curve, RSA too short). */
};
//! Key usage as defined in [RFC #5280 - section-4.2.1.3](https://tools.ietf.org/html/rfc5280#section-4.2.1.3)
enum class KEY_USAGE {
DIGITAL_SIGNATURE = 0, /**< The key is used for digital signature */
NON_REPUDIATION, /**< The key is used for digital signature AND to protects against falsely denying some action */
KEY_ENCIPHERMENT, /**< The key is used for enciphering private or secret keys */
DATA_ENCIPHERMENT, /**< The key is used for directly enciphering raw user data without the use of an intermediate symmetric cipher */
KEY_AGREEMENT, /**< The Key is used for key agreement. (e.g. with Diffie-Hellman) */
KEY_CERT_SIGN, /**< The key is used for verifying signatures on public key certificates */
CRL_SIGN, /**< The key is used for verifying signatures on certificate revocation lists */
ENCIPHER_ONLY, /**< In **association with** KEY_AGREEMENT (otherwise the meaning is undefined), the key is only used for enciphering data while performing key agreement */
DECIPHER_ONLY, /**< In **association with** KEY_AGREEMENT (otherwise the meaning is undefined), the key is only used for deciphering data while performing key agreement */
};
x509(mbedtls_x509_crt* ca);
x509(const x509& other);
x509& operator=(x509 other);
@ -108,10 +132,10 @@ class LIEF_API x509 : public Object {
oid_t signature_algorithm(void) const;
//! Start time of certificate validity
x509::date_t valid_from(void) const;
date_t valid_from(void) const;
//! End time of certificate validity
x509::date_t valid_to(void) const;
date_t valid_to(void) const;
//! Issuer informations
std::string issuer(void) const;
@ -139,6 +163,20 @@ class LIEF_API x509 : public Object {
//! Verify that this certificate **is trusted** by the given CA list
VERIFICATION_FLAGS is_trusted_by(const std::vector<x509>& ca) const;
//! Policy information terms as OID (see RFC #5280)
std::vector<oid_t> certificate_policies() const;
//! Purpose of the key contained in the certificate
std::vector<KEY_USAGE> key_usage() const;
//! Indicates one or more purposes for which the certified public key may be used (OID types)
std::vector<oid_t> ext_key_usage() const;
bool is_ca() const;
//! The signature of the certificate
std::vector<uint8_t> signature() const;
virtual void accept(Visitor& visitor) const override;
virtual ~x509(void);

View File

@ -67,4 +67,11 @@ operator &=(Enum& lhs, Enum rhs)
return lhs;
}
template<typename Enum>
typename std::enable_if<EnableBitMaskOperators<Enum>::bit_mask_enabled, bool>::type
is_true(Enum e)
{
using underlying = typename std::underlying_type<Enum>::type;
return static_cast<underlying>(e) > 0;
}
#endif

View File

@ -253,7 +253,9 @@ result<std::unique_ptr<mbedtls_x509_crt>> VectorStream::asn1_read_cert() {
int ret = mbedtls_x509_crt_parse_der(ca.get(), p, /* buff len */ buff_len);
if (ret != 0) {
LIEF_DEBUG("asn1_read_cert(): {:x}", ret);
std::string strerr(1024, 0);
mbedtls_strerror(ret, const_cast<char*>(strerr.data()), strerr.size());
LIEF_DEBUG("asn1_read_cert(): {}", strerr);
return make_error_code(lief_errors::read_error);
}
if (ca->raw.len <= 0) {

View File

@ -1202,14 +1202,14 @@ std::vector<uint8_t> Binary::authentihash(ALGORITHMS algo) const {
return hash;
}
Signature::VERIFICATION_FLAGS Binary::verify_signature() const {
Signature::VERIFICATION_FLAGS Binary::verify_signature(Signature::VERIFICATION_CHECKS checks) const {
if (not this->has_signatures()) {
return Signature::VERIFICATION_FLAGS::NO_SIGNATURE;
}
for (size_t i = 0; i < this->signatures_.size(); ++i) {
const Signature& sig = this->signatures_[i];
Signature::VERIFICATION_FLAGS flags = this->verify_signature(sig);
Signature::VERIFICATION_FLAGS flags = this->verify_signature(sig, checks);
if (flags != Signature::VERIFICATION_FLAGS::OK) {
LIEF_INFO("Verification failed for signature #{:d} (0b{:b})", i, static_cast<uintptr_t>(flags));
return flags;
@ -1218,11 +1218,13 @@ Signature::VERIFICATION_FLAGS Binary::verify_signature() const {
return Signature::VERIFICATION_FLAGS::OK;
}
Signature::VERIFICATION_FLAGS Binary::verify_signature(const Signature& sig) const {
const Signature::VERIFICATION_FLAGS value = sig.check();
if (value != Signature::VERIFICATION_FLAGS::OK) {
LIEF_INFO("Bad signature (0b{:b})", static_cast<uintptr_t>(value));
return value;
Signature::VERIFICATION_FLAGS Binary::verify_signature(const Signature& sig, Signature::VERIFICATION_CHECKS checks) const {
if (not is_true(checks & Signature::VERIFICATION_CHECKS::HASH_ONLY)) {
const Signature::VERIFICATION_FLAGS value = sig.check(checks);
if (value != Signature::VERIFICATION_FLAGS::OK) {
LIEF_INFO("Bad signature (0b{:b})", static_cast<uintptr_t>(value));
return value;
}
}
// Check that the authentihash matches Content Info's digest

View File

@ -501,8 +501,7 @@ void Hash::visit(const PKCS9AtSequenceNumber& attr) {
}
void Hash::visit(const PKCS9CounterSignature& attr) {
this->visit(*attr.as<Attribute>());
it_const_signers_t signers = attr.signers();
this->process(std::begin(signers), std::end(signers));
this->process(attr.signer());
}
void Hash::visit(const PKCS9MessageDigest& attr) {
this->visit(*attr.as<Attribute>());

View File

@ -864,13 +864,9 @@ void JsonVisitor::visit(const PKCS9AtSequenceNumber& attr) {
void JsonVisitor::visit(const PKCS9CounterSignature& attr) {
this->visit(*attr.as<Attribute>());
std::vector<json> signers;
for (const SignerInfo& signer : attr.signers()) {
JsonVisitor visitor;
visitor(signer);
signers.emplace_back(std::move(visitor.get()));
}
this->node_["signers"] = std::move(signers);
JsonVisitor visitor;
visitor(attr.signer());
this->node_["signer"] = std::move(visitor.get());
}
void JsonVisitor::visit(const PKCS9MessageDigest& attr) {

View File

@ -27,6 +27,8 @@
#include "LIEF/PE/signature/Attribute.hpp"
#include "LIEF/PE/signature/attributes.hpp"
#include <mbedtls/asn1write.h>
#include <mbedtls/sha512.h>
#include <mbedtls/sha256.h>
#include <mbedtls/sha1.h>
@ -36,11 +38,107 @@
#include <mbedtls/md5.h>
#include "mbedtls/x509_crt.h"
#include "mbedtls/x509.h"
namespace LIEF {
namespace PE {
inline std::string time_to_string(const x509::date_t& date) {
return fmt::format("{:d}/{:02d}/{:02d} - {:02d}:{:02d}:{:02d}",
date[0], date[1], date[2],
date[3], date[4], date[5]);
}
Signature::VERIFICATION_FLAGS verify_ts_counter_signature(const SignerInfo& signer,
const PKCS9CounterSignature& cs, Signature::VERIFICATION_CHECKS checks) {
LIEF_DEBUG("PKCS #9 Counter signature found");
Signature::VERIFICATION_FLAGS flags = Signature::VERIFICATION_FLAGS::OK;
const SignerInfo& cs_signer = cs.signer();
if (cs_signer.cert() == nullptr) {
LIEF_WARN("Can't find x509 certificate associated with Counter Signature's signer");
return flags | Signature::VERIFICATION_FLAGS::CERT_NOT_FOUND;
}
const x509& cs_cert = *cs_signer.cert();
const SignerInfo::encrypted_digest_t& cs_enc_digest = cs_signer.encrypted_digest();
std::vector<uint8_t> cs_auth_data = cs_signer.raw_auth_data();
// According to the RFC:
//
// "[...] The Attributes value's tag is SET OF, and the DER encoding of
// the SET OF tag, rather than of the IMPLICIT [0] tag [...]"
cs_auth_data[0] = /* SET OF */ 0x31;
const ALGORITHMS cs_digest_algo = cs_signer.digest_algorithm();
const std::vector<uint8_t>& cs_hash = Signature::hash(cs_auth_data, cs_digest_algo);
LIEF_DEBUG("Signed data digest: {}", hex_dump(cs_hash));
bool check_sig = cs_cert.check_signature(cs_hash, cs_enc_digest, cs_digest_algo);
if (not check_sig) {
LIEF_WARN("Authenticated signature (counter signature) mismatch");
//return flags | VERIFICATION_FLAGS::BAD_SIGNATURE;
}
/* According to Microsoft documentation:
* The Authenticode timestamp SignerInfo structure contains the following authenticated attributes values:
* 1. ContentType (1.2.840.113549.1.9.3) is set to PKCS #7 Data (1.2.840.113549.1.7.1).
* 2. Signing Time (1.2.840.113549.1.9.5) is set to the UTC time of timestamp generation time.
* 3. Message Digest (1.2.840.113549.1.9.4) is set to the hash value of the
* SignerInfo structure's encryptedDigest value. The hash algorithm that
* is used to calculate the hash value is the same as that specified in the
* SignerInfo structures digestAlgorithm value of the timestamp.
*/
// Verify 1.
const auto* content_type_data = reinterpret_cast<const ContentType*>(cs_signer.get_auth_attribute(SIG_ATTRIBUTE_TYPES::CONTENT_TYPE));
if (content_type_data == nullptr) {
LIEF_WARN("Missing ContentType in authenticated attributes in the counter signature's signer");
return flags | Signature::VERIFICATION_FLAGS::INVALID_SIGNER;
}
if (content_type_data->oid() != /* PKCS #7 Data */ "1.2.840.113549.1.7.1") {
LIEF_WARN("Bad OID for ContentType in authenticated attributes in the counter signature's signer ({})",
content_type_data->oid());
return flags | Signature::VERIFICATION_FLAGS::INVALID_SIGNER;
}
// Verify 3.
const auto* message_dg = reinterpret_cast<const PKCS9MessageDigest*>(cs_signer.get_auth_attribute(SIG_ATTRIBUTE_TYPES::PKCS9_MESSAGE_DIGEST));
if (message_dg == nullptr) {
LIEF_WARN("Missing MessageDigest in authenticated attributes in the counter signature's signer");
return flags | Signature::VERIFICATION_FLAGS::INVALID_SIGNER;
}
const std::vector<uint8_t>& dg_value = message_dg->digest();
const std::vector<uint8_t> dg_cs_hash = Signature::hash(signer.encrypted_digest(), cs_digest_algo);
if (dg_value != dg_cs_hash) {
LIEF_WARN("MessageDigest mismatch with Hash(signer ED)");
return flags | Signature::VERIFICATION_FLAGS::INVALID_SIGNER;
}
/*
* Verify that signing's time is valid within the signer's certificate
* validity window.
*/
const auto* signing_time = reinterpret_cast<const PKCS9SigningTime*>(cs_signer.get_auth_attribute(SIG_ATTRIBUTE_TYPES::PKCS9_SIGNING_TIME));
if (signing_time != nullptr and not is_true(checks & Signature::VERIFICATION_CHECKS::SKIP_CERT_TIME)) {
LIEF_DEBUG("PKCS #9 signing time found");
PKCS9SigningTime::time_t time = signing_time->time();
if (not x509::check_time(time, cs_cert.valid_to())) {
LIEF_WARN("Signing time: {} is above the certificate validity: {}",
time_to_string(time), time_to_string(cs_cert.valid_to()));
return flags | Signature::VERIFICATION_FLAGS::CERT_EXPIRED;
}
if (not x509::check_time(cs_cert.valid_from(), time)) {
LIEF_WARN("Signing time: {} is below the certificate validity: {}",
time_to_string(time), time_to_string(cs_cert.valid_to()));
return flags | Signature::VERIFICATION_FLAGS::CERT_FUTURE;
}
}
return flags;
}
Signature::Signature(void) = default;
Signature::Signature(const Signature&) = default;
@ -153,7 +251,7 @@ it_const_signers_t Signature::signers(void) const {
return this->signers_;
}
Signature::VERIFICATION_FLAGS Signature::check() const {
Signature::VERIFICATION_FLAGS Signature::check(VERIFICATION_CHECKS checks) const {
// According to the Authenticode documentation,
// *SignerInfos contains one SignerInfo structure*
const size_t nb_signers = this->signers_.size();
@ -197,6 +295,18 @@ Signature::VERIFICATION_FLAGS Signature::check() const {
const x509& cert = *signer.cert();
const SignerInfo::encrypted_digest_t& enc_digest = signer.encrypted_digest();
/*
* Check the following condition:
* "The signing certificate must contain either the extended key usage (EKU)
* value for code signing, or the entire certificate chain must contain no
* EKUs. The following is the EKU value for code signing"
*/
//TODO(romain)
/*
* Verify certificate validity
*/
if (this->content_info_start_ == 0 or this->content_info_end_ == 0) {
return flags | VERIFICATION_FLAGS::CORRUPTED_CONTENT_INFO;
}
@ -208,19 +318,12 @@ Signature::VERIFICATION_FLAGS Signature::check() const {
const std::vector<uint8_t> content_info_hash = Signature::hash(std::move(raw_content_info), digest_algo);
if (this->auth_start_ == 0 or this->auth_end_ == 0) {
flags |= VERIFICATION_FLAGS::CORRUPTED_AUTH_DATA;
}
// Copy authenticated attributes
it_const_attributes_t auth_attrs = signer.authenticated_attributes();
if (auth_attrs.size() > 0 and
(flags & VERIFICATION_FLAGS::CORRUPTED_AUTH_DATA) != VERIFICATION_FLAGS::CORRUPTED_AUTH_DATA) {
std::vector<uint8_t> auth_data = {
std::begin(this->original_raw_signature_) + this->auth_start_,
std::begin(this->original_raw_signature_) + this->auth_end_
};
if (auth_attrs.size() > 0) {
std::vector<uint8_t> auth_data = signer.raw_auth_data_;
// According to the RFC:
//
// "[...] The Attributes value's tag is SET OF, and the DER encoding of
@ -232,6 +335,7 @@ Signature::VERIFICATION_FLAGS Signature::check() const {
bool check_sig = cert.check_signature(auth_attr_hash, enc_digest, digest_algo);
if (not check_sig) {
LIEF_WARN("Authenticated signature mismatch");
return flags | VERIFICATION_FLAGS::BAD_SIGNATURE;
}
@ -242,6 +346,7 @@ Signature::VERIFICATION_FLAGS Signature::check() const {
});
if (it_pkcs9_digest == std::end(auth_attrs)) {
LIEF_WARN("Can't find the authenticated attribute: 'pkcs9-message-digest'");
return flags | VERIFICATION_FLAGS::MISSING_PKCS9_MESSAGE_DIGEST;
}
@ -250,14 +355,53 @@ Signature::VERIFICATION_FLAGS Signature::check() const {
if (digest_attr.digest() != content_info_hash) {
return flags | VERIFICATION_FLAGS::BAD_DIGEST;
}
return flags;
} else {
/*
* If there is no authenticated attributes, then the encrypted digested should match ENC(content_info_hash)
*/
if (not cert.check_signature(content_info_hash, enc_digest, digest_algo)) {
return flags | VERIFICATION_FLAGS::BAD_SIGNATURE;
}
}
/*
* If there is no authenticated attributes, then encrypted digested should match ENC(content_info_hash)
* CounterSignature Checks
*/
if (not cert.check_signature(content_info_hash, enc_digest, digest_algo)) {
return flags | VERIFICATION_FLAGS::BAD_SIGNATURE;
const auto* counter = reinterpret_cast<const PKCS9CounterSignature*>(signer.get_unauth_attribute(SIG_ATTRIBUTE_TYPES::PKCS9_COUNTER_SIGNATURE));
bool has_ms_counter_sig = false;
for (const Attribute& attr : signer.unauthenticated_attributes()) {
if (attr.type() == SIG_ATTRIBUTE_TYPES::GENERIC_TYPE) {
if (reinterpret_cast<const GenericType&>(attr).oid() == /* Ms-CounterSign */ "1.3.6.1.4.1.311.3.3.1") {
has_ms_counter_sig = true;
break;
}
}
}
bool timeless_signature = false;
if (counter != nullptr) {
VERIFICATION_FLAGS cs_flags = verify_ts_counter_signature(signer, *counter, checks);
if (cs_flags == VERIFICATION_FLAGS::OK) {
timeless_signature = true;
}
} else if (not timeless_signature and has_ms_counter_sig) {
timeless_signature = true;
}
bool should_check_cert_time = not timeless_signature or is_true(checks & VERIFICATION_CHECKS::LIFETIME_SIGNING);
if (is_true(checks & VERIFICATION_CHECKS::SKIP_CERT_TIME)) {
should_check_cert_time = false;
}
if (should_check_cert_time) {
/*
* Verify certificate validities
*/
if (x509::time_is_past(cert.valid_to())) {
return flags | VERIFICATION_FLAGS::CERT_EXPIRED;
}
if (x509::time_is_future(cert.valid_from())) {
return flags | VERIFICATION_FLAGS::CERT_FUTURE;
}
}
return flags;
}
@ -267,6 +411,63 @@ const std::vector<uint8_t>& Signature::raw_der(void) const {
return this->original_raw_signature_;
}
const x509* Signature::find_crt(const std::vector<uint8_t>& serialno) const {
auto it_cert = std::find_if(std::begin(this->certificates_), std::end(this->certificates_),
[&serialno] (const x509& cert) {
return cert.serial_number() == serialno;
});
if (it_cert == std::end(this->certificates_)) {
return nullptr;
}
return &(*it_cert);
}
const x509* Signature::find_crt_subject(const std::string& subject) const {
auto it_cert = std::find_if(std::begin(this->certificates_), std::end(this->certificates_),
[&subject] (const x509& cert) {
return cert.subject() == subject;
});
if (it_cert == std::end(this->certificates_)) {
return nullptr;
}
return &(*it_cert);
}
const x509* Signature::find_crt_subject(const std::string& subject, const std::vector<uint8_t>& serialno) const {
auto it_cert = std::find_if(std::begin(this->certificates_), std::end(this->certificates_),
[&subject, &serialno] (const x509& cert) {
return cert.subject() == subject and cert.serial_number() == serialno;
});
if (it_cert == std::end(this->certificates_)) {
return nullptr;
}
return &(*it_cert);
}
const x509* Signature::find_crt_issuer(const std::string& issuer) const {
auto it_cert = std::find_if(std::begin(this->certificates_), std::end(this->certificates_),
[&issuer] (const x509& cert) {
return cert.issuer() == issuer;
});
if (it_cert == std::end(this->certificates_)) {
return nullptr;
}
return &(*it_cert);
}
const x509* Signature::find_crt_issuer(const std::string& issuer, const std::vector<uint8_t>& serialno) const {
auto it_cert = std::find_if(std::begin(this->certificates_), std::end(this->certificates_),
[&issuer, &serialno] (const x509& cert) {
return cert.issuer() == issuer and cert.serial_number() == serialno;
});
if (it_cert == std::end(this->certificates_)) {
return nullptr;
}
return &(*it_cert);
}
void Signature::accept(Visitor& visitor) const {
visitor.visit(*this);
}
@ -342,13 +543,7 @@ inline void print_attr(it_const_attributes_t& attrs, std::ostream& os) {
case SIG_ATTRIBUTE_TYPES::PKCS9_COUNTER_SIGNATURE:
{
const auto& ct = reinterpret_cast<const PKCS9CounterSignature&>(attr);
it_const_signers_t signers = ct.signers();
if (signers.size() > 1 or signers.size() == 0) {
suffix = std::to_string(signers.size()) + " signers";
break;
}
const SignerInfo& signer = signers[0];
const SignerInfo& signer = ct.signer();
suffix = signer.issuer();
break;
}

View File

@ -279,11 +279,6 @@ result<Signature> SignatureParser::parse_signature() {
} else {
// Makes chain
std::vector<x509> certs = certificates.value();
//mbedtls_x509_crt* next = certs.back().x509_cert_;
//for (size_t i = 1; i < certs.size(); ++i) {
// certs[certs.size() - i - 1].x509_cert_->next = next;
// next = certs[certs.size() - i - 1].x509_cert_->next;
//}
signature.certificates_ = std::move(certs);
}
}
@ -304,17 +299,12 @@ result<Signature> SignatureParser::parse_signature() {
// =========================================================
tag = this->stream_->asn1_read_tag(MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET);
if (tag) {
const uint64_t current_pos = this->stream_->pos();
LIEF_DEBUG("Parse pkcs7-signed-data.signer-infos offset: {:d}", this->stream_->pos());
std::vector<uint8_t> raw_content =
{this->stream_->p(), this->stream_->p() + tag.value()};
VectorStream stream{std::move(raw_content)};
this->stream_->increment_pos(raw_content.size());
range_t auth_attr;
auto signer_info = this->parse_signer_infos(stream, auth_attr);
signature.auth_start_ = current_pos + auth_attr.start;
signature.auth_end_ = current_pos + auth_attr.end;
LIEF_DEBUG("Authenticated attributes: {} -> {}", signature.auth_start_, signature.auth_end_);
auto signer_info = this->parse_signer_infos(stream);
if (not signer_info) {
LIEF_INFO("Fail to parse pkcs7-signed-data.signer-infos");
} else {
@ -324,15 +314,22 @@ result<Signature> SignatureParser::parse_signature() {
// Tied signer info with x509 certificates
for (SignerInfo& signer : signature.signers_) {
auto it_cert = std::find_if(std::begin(signature.certificates_), std::end(signature.certificates_),
[&signer] (const x509& cert) {
return cert.issuer() == signer.issuer() and cert.serial_number() == signer.serial_number();
});
if (it_cert == std::end(signature.certificates_)) {
const x509* crt = signature.find_crt_issuer(signer.issuer(), signer.serial_number());
if (crt != nullptr) {
signer.cert_ = std::unique_ptr<x509>(new x509{*crt});
} else {
LIEF_INFO("Can't find x509 certificate associated with signer '{}'", signer.issuer());
continue;
}
signer.cert_ = std::unique_ptr<x509>(new x509{*it_cert});
const auto* cs = reinterpret_cast<const PKCS9CounterSignature*>(signer.get_attribute(SIG_ATTRIBUTE_TYPES::PKCS9_COUNTER_SIGNATURE));
if (cs != nullptr) {
SignerInfo& cs_signer = const_cast<PKCS9CounterSignature*>(cs)->signer_;
const x509* crt = signature.find_crt_issuer(cs_signer.issuer(), cs_signer.serial_number());
if (crt != nullptr) {
cs_signer.cert_ = std::unique_ptr<x509>(new x509{*crt});
} else {
LIEF_INFO("Can't find x509 certificate associated with signer '{}'", signer.issuer());
}
}
}
return signature;
@ -524,7 +521,7 @@ result<SignatureParser::x509_certificates_t> SignatureParser::parse_certificates
}
result<SignatureParser::signer_infos_t> SignatureParser::parse_signer_infos(VectorStream& stream, range_t& auth_attr) {
result<SignatureParser::signer_infos_t> SignatureParser::parse_signer_infos(VectorStream& stream) {
const uintptr_t end_set = stream.size();
signer_infos_t infos;
@ -621,11 +618,11 @@ result<SignatureParser::signer_infos_t> SignatureParser::parse_signer_infos(Vect
// Authenticated Attributes
// =======================================================
{
auth_attr.start = stream.pos();
const uint64_t auth_attr_start = stream.pos();
tag = stream.asn1_read_tag(/* authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL */
MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED);
if (tag) {
auth_attr.end = stream.pos() + tag.value();
const uint64_t auth_attr_end = stream.pos() + tag.value();
std::vector<uint8_t> raw_authenticated_attributes =
{stream.p(), stream.p() + tag.value()};
VectorStream auth_stream(std::move(raw_authenticated_attributes));
@ -634,10 +631,9 @@ result<SignatureParser::signer_infos_t> SignatureParser::parse_signer_infos(Vect
if (not authenticated_attributes) {
LIEF_INFO("Fail to parse pkcs7-signed-data.signer-infos.authenticated-attributes");
} else {
signer.raw_auth_data_ = {stream.start() + auth_attr_start, stream.start() + auth_attr_end};
signer.authenticated_attributes_ = std::move(authenticated_attributes.value());
}
} else {
auth_attr.start = 0;
}
}
@ -672,7 +668,7 @@ result<SignatureParser::signer_infos_t> SignatureParser::parse_signer_infos(Vect
stream.pos());
return enc_digest.error();
}
LIEF_DEBUG("pkcs7-signed-data.signer-infos.encrypted-digest: {}", hex_dump(enc_digest.value()));
LIEF_DEBUG("pkcs7-signed-data.signer-infos.encrypted-digest: {}", hex_dump(enc_digest.value()).substr(0, 10));
signer.encrypted_digest_ = enc_digest.value();
}
@ -775,7 +771,14 @@ result<SignatureParser::attributes_t> SignatureParser::parse_attributes(VectorSt
if (not res) {
LIEF_INFO("Can't parse pkcs9-counter-sign attribute");
} else {
attributes.emplace_back(new PKCS9CounterSignature(std::move(res.value())));
const std::vector<SignerInfo>& signers = res.value();
if (signers.size() == 0) {
LIEF_INFO("Can't parse signer info associated with the pkcs9-counter-sign");
} else if (signers.size() > 1) {
LIEF_INFO("More than one signer info associated with the pkcs9-counter-sign");
} else {
attributes.emplace_back(new PKCS9CounterSignature(std::move(signers.back())));
}
}
}
@ -916,8 +919,7 @@ result<SignatureParser::signer_infos_t> SignatureParser::parse_pkcs9_counter_sig
// ID pkcs-9-at-counterSignature
// }
LIEF_DEBUG("Parsing pkcs9-CounterSign ({} bytes)", stream.size());
range_t auth_attr;
auto counter_sig = this->parse_signer_infos(stream, auth_attr);
auto counter_sig = this->parse_signer_infos(stream);
if (not counter_sig) {
LIEF_INFO("Fail to parse pkcs9-counter-signature");
return counter_sig.error();
@ -1075,7 +1077,7 @@ result<SignatureParser::time_t> SignatureParser::parse_pkcs9_signing_time(Vector
return tm.error();
}
std::unique_ptr<mbedtls_x509_time> time = std::move(tm.value());
LIEF_INFO("pkcs9-signing-time {}/{}/{}", time->day, time->mon, time->year);
LIEF_DEBUG("pkcs9-signing-time {}/{}/{}", time->day, time->mon, time->year);
return SignatureParser::time_t{time->year, time->mon, time->day, time->hour, time->min, time->sec};
}

View File

@ -43,7 +43,8 @@ SignerInfo::SignerInfo(const SignerInfo& other) :
serialno_{other.serialno_},
digest_algorithm_{other.digest_algorithm_},
digest_enc_algorithm_{other.digest_enc_algorithm_},
encrypted_digest_{other.encrypted_digest_}
encrypted_digest_{other.encrypted_digest_},
raw_auth_data_{other.raw_auth_data_}
{
for (const std::unique_ptr<Attribute>& attr : other.authenticated_attributes_) {
this->authenticated_attributes_.push_back(attr->clone());
@ -70,6 +71,7 @@ void SignerInfo::swap(SignerInfo& other) {
std::swap(this->digest_algorithm_, other.digest_algorithm_);
std::swap(this->digest_enc_algorithm_, other.digest_enc_algorithm_);
std::swap(this->encrypted_digest_, other.encrypted_digest_);
std::swap(this->raw_auth_data_, other.raw_auth_data_);
std::swap(this->authenticated_attributes_, other.authenticated_attributes_);
std::swap(this->unauthenticated_attributes_, other.unauthenticated_attributes_);
std::swap(this->cert_, other.cert_);
@ -110,7 +112,22 @@ it_const_attributes_t SignerInfo::unauthenticated_attributes() const {
const Attribute* SignerInfo::get_attribute(PE::SIG_ATTRIBUTE_TYPES type) const {
// First look for the attribute in the authenticated ones
const Attribute* attr = this->get_auth_attribute(type);
if (attr != nullptr) {
return attr;
}
attr = this->get_unauth_attribute(type);
if (attr != nullptr) {
return attr;
}
// ... not found -> return nullptr
return nullptr;
}
const Attribute* SignerInfo::get_auth_attribute(PE::SIG_ATTRIBUTE_TYPES type) const {
auto it_auth = std::find_if(std::begin(this->authenticated_attributes_), std::end(this->authenticated_attributes_),
[type] (const std::unique_ptr<Attribute>& attr) {
return attr->type() == type;
@ -118,8 +135,10 @@ const Attribute* SignerInfo::get_attribute(PE::SIG_ATTRIBUTE_TYPES type) const {
if (it_auth != std::end(this->authenticated_attributes_)) {
return it_auth->get();
}
return nullptr;
}
// Then in the UN-authenticated ones
const Attribute* SignerInfo::get_unauth_attribute(PE::SIG_ATTRIBUTE_TYPES type) const {
auto it_uauth = std::find_if(std::begin(this->unauthenticated_attributes_), std::end(this->unauthenticated_attributes_),
[type] (const std::unique_ptr<Attribute>& attr) {
return attr->type() == type;
@ -127,11 +146,10 @@ const Attribute* SignerInfo::get_attribute(PE::SIG_ATTRIBUTE_TYPES type) const {
if (it_uauth != std::end(this->unauthenticated_attributes_)) {
return it_uauth->get();
}
// ... not found -> return nullptr
return nullptr;
}
void SignerInfo::accept(Visitor& visitor) const {
visitor.visit(*this);
}

View File

@ -10,27 +10,22 @@ PKCS9CounterSignature::PKCS9CounterSignature() :
PKCS9CounterSignature::PKCS9CounterSignature(const PKCS9CounterSignature&) = default;
PKCS9CounterSignature& PKCS9CounterSignature::operator=(const PKCS9CounterSignature&) = default;
PKCS9CounterSignature::PKCS9CounterSignature(std::vector<SignerInfo> signers) :
PKCS9CounterSignature::PKCS9CounterSignature(SignerInfo signer) :
Attribute(SIG_ATTRIBUTE_TYPES::PKCS9_COUNTER_SIGNATURE),
signers_{std::move(signers)}
signer_{std::move(signer)}
{}
std::unique_ptr<Attribute> PKCS9CounterSignature::clone(void) const {
return std::unique_ptr<Attribute>(new PKCS9CounterSignature{*this});
}
void PKCS9CounterSignature::accept(Visitor& visitor) const {
visitor.visit(*this);
}
std::string PKCS9CounterSignature::print() const {
std::ostringstream oss;
it_const_signers_t signers = this->signers();
oss << std::to_string(signers.size()) << " signer(s): \n";
for (const SignerInfo& signer : signers) {
oss << signer << "\n";
}
oss << this->signer() << "\n";
return oss.str();
}

View File

@ -32,9 +32,50 @@
#include "LIEF/PE/signature/RsaInfo.hpp"
#include "LIEF/PE/EnumToString.hpp"
#include "LIEF/utils.hpp"
namespace {
// Copy this function from mbedtls sinc it is not exported
inline int x509_get_current_time( mbedtls_x509_time *now )
{
struct tm *lt, tm_buf;
mbedtls_time_t tt;
int ret = 0;
tt = mbedtls_time( NULL );
lt = mbedtls_platform_gmtime_r( &tt, &tm_buf );
if( lt == NULL )
ret = -1;
else
{
now->year = lt->tm_year + 1900;
now->mon = lt->tm_mon + 1;
now->day = lt->tm_mday;
now->hour = lt->tm_hour;
now->min = lt->tm_min;
now->sec = lt->tm_sec;
}
return( ret );
}
}
namespace LIEF {
namespace PE {
inline x509::date_t from_mbedtls(const mbedtls_x509_time& time) {
return {
time.year,
time.mon,
time.day,
time.hour,
time.min,
time.sec
};
}
x509::certificates_t x509::parse(const std::string& path) {
std::ifstream cert_fs(path);
@ -77,6 +118,106 @@ x509::certificates_t x509::parse(const std::vector<uint8_t>& content) {
return crts;
}
bool x509::check_time(const date_t& before, const date_t& after) {
// Implementation taken
// from https://github.com/ARMmbed/mbedtls/blob/1c54b5410fd48d6bcada97e30cac417c5c7eea67/library/x509.c#L926-L962
if (before[0] > after[0]) {
LIEF_DEBUG("{} > {}", before[0], after[0]);
return false;
}
if (
before[0] == after[0] and
before[1] > after[1]
)
{
LIEF_DEBUG("{} > {}", before[1], after[1]);
return false;
}
if (
before[0] == after[0] and
before[1] == after[1] and
before[2] > after[2]
)
{
LIEF_DEBUG("{} > {}", before[2], after[2]);
return false;
}
if (
before[0] == after[0] and
before[1] == after[1] and
before[2] == after[2] and
before[3] > after[3]
)
{
LIEF_DEBUG("{} > {}", before[3], after[3]);
return false;
}
if (
before[0] == after[0] and
before[1] == after[1] and
before[2] == after[2] and
before[3] == after[3] and
before[4] > after[4]
)
{
LIEF_DEBUG("{} > {}", before[4], after[4]);
return false;
}
if (
before[0] == after[0] and
before[1] == after[1] and
before[2] == after[2] and
before[3] == after[3] and
before[4] == after[4] and
before[5] > after[5]
)
{
LIEF_DEBUG("{} > {}", before[5], after[5]);
return false;
}
if (
before[0] == after[0] and
before[1] == after[1] and
before[2] == after[2] and
before[3] == after[3] and
before[4] == after[4] and
before[5] == after[5] and
before[6] > after[6]
)
{
LIEF_DEBUG("{} > {}", before[6], after[6]);
return false;
}
return true;
}
bool x509::time_is_past(const date_t& to) {
mbedtls_x509_time now;
if (x509_get_current_time(&now) != 0) {
return true;
}
// check_time(): true if now < to else false
return not check_time(from_mbedtls(now), to);
}
bool x509::time_is_future(const date_t& from) {
mbedtls_x509_time now;
if (x509_get_current_time(&now) != 0) {
return true;
}
return check_time(from_mbedtls(now), from);
}
x509::x509() = default;
x509::x509(mbedtls_x509_crt* ca) :
@ -119,25 +260,11 @@ oid_t x509::signature_algorithm(void) const {
}
x509::date_t x509::valid_from(void) const {
return {{
this->x509_cert_->valid_from.year,
this->x509_cert_->valid_from.mon,
this->x509_cert_->valid_from.day,
this->x509_cert_->valid_from.hour,
this->x509_cert_->valid_from.min,
this->x509_cert_->valid_from.sec
}};
return from_mbedtls(this->x509_cert_->valid_from);
}
x509::date_t x509::valid_to(void) const {
return {{
this->x509_cert_->valid_to.year,
this->x509_cert_->valid_to.mon,
this->x509_cert_->valid_to.day,
this->x509_cert_->valid_to.hour,
this->x509_cert_->valid_to.min,
this->x509_cert_->valid_to.sec
}};
return from_mbedtls(this->x509_cert_->valid_to);
}
@ -206,17 +333,63 @@ bool x509::check_signature(const std::vector<uint8_t>& hash, const std::vector<u
return false;
}
mbedtls_pk_context& ctx = this->x509_cert_->pk;
int ret = mbedtls_pk_verify(&ctx,
/* MD_HASH_ALGO */ it_md->second,
/* Input Hash */ hash.data(), hash.size(),
/* Signature provided */ signature.data(), signature.size());
/* If the verification failed with mbedtls_pk_verify it
* does not necessity means that the signatures don't match.
*
* For RSA public-key scheme, mbedtls encodes the hash with rsa_rsassa_pkcs1_v15_encode() so that it expands
* the hash value with encoded data. On some samples, this encoding failed.
*
* In the approach below, we manually decrypt and unpad the output of the DEC(signature)
* as defined in the RFC #2313
*/
if (ret != 0) {
std::string strerr(1024, 0);
mbedtls_strerror(ret, const_cast<char*>(strerr.data()), strerr.size());
LIEF_INFO("decrypt() failed with error: '{}'", strerr);
return false;
if (mbedtls_pk_get_type(&ctx) == MBEDTLS_PK_RSA) {
auto* ctx_rsa = reinterpret_cast<mbedtls_rsa_context*>(ctx.pk_ctx);
if ((ctx_rsa->len * 8) < 100 or (ctx_rsa->len * 8) > 2048 * 10) {
LIEF_INFO("RSA Key length is not valid ({} bits)", ctx_rsa->len * 8);
return false;
}
std::vector<uint8_t> decrypted(ctx_rsa->len);
int ret_rsa_public = mbedtls_rsa_public(ctx_rsa, signature.data(), decrypted.data());
if (ret_rsa_public != 0) {
std::string strerr(1024, 0);
mbedtls_strerror(ret_rsa_public, const_cast<char*>(strerr.data()), strerr.size());
LIEF_INFO("RSA public key operation failed: '{}'", strerr);
return false;
}
// Check padding header
if (decrypted[0] != 0x00 and decrypted[1] != 0x01 and decrypted[2] != 0xff) {
return false;
}
std::vector<uint8_t> unpadded;
for (size_t i = 2; i < decrypted.size(); ++i) {
if (decrypted[i] == 0) {
unpadded = std::vector<uint8_t>(std::begin(decrypted) + i + 1, std::end(decrypted));
break;
}
if (decrypted[i] != 0xFF) {
return false;
}
}
if (unpadded == hash) {
return true;
}
}
if (ret != 0) {
std::string strerr(1024, 0);
mbedtls_strerror(ret, const_cast<char*>(strerr.data()), strerr.size());
LIEF_INFO("decrypt() failed with error: '{}'", strerr);
return false;
}
return true;
}
return true;
}
@ -228,6 +401,7 @@ x509::VERIFICATION_FLAGS x509::is_trusted_by(const std::vector<x509>& ca) const
ca_list[i].x509_cert_->next = ca_list[i + 1].x509_cert_;
}
VERIFICATION_FLAGS result = VERIFICATION_FLAGS::OK;
uint32_t flags = 0;
mbedtls_x509_crt_profile profile = {
MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_MD5) |
@ -257,17 +431,19 @@ x509::VERIFICATION_FLAGS x509::is_trusted_by(const std::vector<x509>& ca) const
std::string out(1024, 0);
mbedtls_x509_crt_verify_info(const_cast<char*>(out.data()), out.size(), "", flags);
LIEF_WARN("X509 verify failed with: {} (0x{:x})\n{}", strerr, ret, out);
result = VERIFICATION_FLAGS::BADCERT_NOT_TRUSTED;
}
// Clear the chain since ~x509() will delete each object
for (size_t i = 0; i < ca_list.size(); ++i) {
ca_list[i].x509_cert_->next = nullptr;
}
return static_cast<VERIFICATION_FLAGS>(flags);
return result;
}
x509::VERIFICATION_FLAGS x509::verify(const x509& ca) const {
uint32_t flags = 0;
VERIFICATION_FLAGS result = VERIFICATION_FLAGS::OK;
mbedtls_x509_crt_profile profile = {
MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA1) |
MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA224) |
@ -295,8 +471,99 @@ x509::VERIFICATION_FLAGS x509::verify(const x509& ca) const {
std::string out(1024, 0);
mbedtls_x509_crt_verify_info(const_cast<char*>(out.data()), out.size(), "", flags);
LIEF_WARN("X509 verify failed with: {} (0x{:x})\n{}", strerr, ret, out);
result = VERIFICATION_FLAGS::BADCERT_NOT_TRUSTED;
}
return static_cast<VERIFICATION_FLAGS>(flags);
return result;
}
std::vector<oid_t> x509::ext_key_usage() const {
if ((this->x509_cert_->ext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE) == 0) {
return {};
}
mbedtls_asn1_sequence* current = &this->x509_cert_->ext_key_usage;
std::vector<oid_t> oids;
while (current != nullptr) {
char oid_str[256] = {0};
int ret = mbedtls_oid_get_numeric_string(oid_str, sizeof(oid_str), &current->buf);
if (ret != MBEDTLS_ERR_OID_BUF_TOO_SMALL) {
LIEF_DEBUG("OID: {}", oid_str);
oids.push_back(oid_str);
} else {
std::string strerr(1024, 0);
mbedtls_strerror(ret, const_cast<char*>(strerr.data()), strerr.size());
LIEF_WARN("{}", strerr);
}
if (current->next == current) {
break;
}
current = current->next;
}
return oids;
}
std::vector<oid_t> x509::certificate_policies() const {
if ((this->x509_cert_->ext_types & MBEDTLS_OID_X509_EXT_CERTIFICATE_POLICIES) == 0) {
return {};
}
mbedtls_x509_sequence& policies = this->x509_cert_->certificate_policies;
mbedtls_asn1_sequence* current = &policies;
std::vector<oid_t> oids;
while (current != nullptr) {
char oid_str[256] = {0};
int ret = mbedtls_oid_get_numeric_string(oid_str, sizeof(oid_str), &current->buf);
if (ret != MBEDTLS_ERR_OID_BUF_TOO_SMALL) {
oids.push_back(oid_str);
} else {
std::string strerr(1024, 0);
mbedtls_strerror(ret, const_cast<char*>(strerr.data()), strerr.size());
LIEF_WARN("{}", strerr);
}
if (current->next == current) {
break;
}
current = current->next;
}
return oids;
}
bool x509::is_ca() const {
if ((this->x509_cert_->ext_types & MBEDTLS_X509_EXT_BASIC_CONSTRAINTS) == 0) {
return true;
}
return this->x509_cert_->ca_istrue;
}
std::vector<x509::KEY_USAGE> x509::key_usage() const {
static const std::map<uint32_t, KEY_USAGE> MBEDTLS_MAP = {
{MBEDTLS_X509_KU_DIGITAL_SIGNATURE, KEY_USAGE::DIGITAL_SIGNATURE},
{MBEDTLS_X509_KU_NON_REPUDIATION, KEY_USAGE::NON_REPUDIATION},
{MBEDTLS_X509_KU_KEY_ENCIPHERMENT, KEY_USAGE::KEY_ENCIPHERMENT},
{MBEDTLS_X509_KU_DATA_ENCIPHERMENT, KEY_USAGE::DATA_ENCIPHERMENT},
{MBEDTLS_X509_KU_KEY_AGREEMENT, KEY_USAGE::KEY_AGREEMENT},
{MBEDTLS_X509_KU_KEY_CERT_SIGN, KEY_USAGE::KEY_CERT_SIGN},
{MBEDTLS_X509_KU_CRL_SIGN, KEY_USAGE::CRL_SIGN},
{MBEDTLS_X509_KU_ENCIPHER_ONLY, KEY_USAGE::ENCIPHER_ONLY},
{MBEDTLS_X509_KU_DECIPHER_ONLY, KEY_USAGE::DECIPHER_ONLY},
};
if ((this->x509_cert_->ext_types & MBEDTLS_X509_EXT_KEY_USAGE) == 0) {
return {};
}
const uint32_t ku = this->x509_cert_->key_usage;
std::vector<KEY_USAGE> usages;
for (const auto& p : MBEDTLS_MAP) {
if ((ku & p.first) > 0) {
usages.push_back(p.second);
}
}
return usages;
}
std::vector<uint8_t> x509::signature() const {
mbedtls_x509_buf sig = this->x509_cert_->sig;
return {sig.p, sig.p + sig.len};
}
void x509::accept(Visitor& visitor) const {

View File

@ -14,7 +14,7 @@ from unittest import TestCase
import lief
from utils import get_sample
lief.logging.set_level(lief.logging.LOGGING_LEVEL.INFO)
lief.logging.set_level(lief.logging.LOGGING_LEVEL.WARNING)
def from_hex(x):
return bytes.fromhex(x.replace(":", ""))
@ -180,8 +180,7 @@ class TestAuthenticode(TestCase):
sig = lief.PE.Signature.parse(get_sample("pkcs7/cert10.p7b"))
counter_sign = sig.signers[0].get_attribute(lief.PE.SIG_ATTRIBUTE_TYPES.PKCS9_COUNTER_SIGNATURE)
self.assertEqual(len(counter_sign.signers), 1)
signer = counter_sign.signers[0]
signer = counter_sign.signer
self.assertEqual(signer.version, 1)
self.assertEqual(signer.serial_number, from_hex("0e:cf:f4:38:c8:fe:bf:35:6e:04:d8:6a:98:1b:1a:50"))