mirror of
https://github.com/QuasarApp/LIEF.git
synced 2025-04-26 20:34:32 +00:00
Process PKCS #9 counter signature and enhance signature verification
This commit is contained in:
parent
e4d2da1fd5
commit
d29a74996b
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
302
examples/python/authenticode/authenticode_reader.py
Normal file
302
examples/python/authenticode/authenticode_reader.py
Normal 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()
|
@ -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()
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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>());
|
||||
|
@ -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) {
|
||||
|
@ -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 structure’s 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;
|
||||
}
|
||||
|
@ -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};
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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), ¤t->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), ¤t->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 {
|
||||
|
@ -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"))
|
||||
|
Loading…
x
Reference in New Issue
Block a user