From 094a37393fd87e3562b4b755a94f5b41134d8bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Milkovi=C4=8D?= Date: Wed, 31 Jan 2024 20:43:55 +0100 Subject: [PATCH] Updated authenticode-parser to the latest version --- .../authenticode-parser/authenticode.h | 8 +- deps/authenticode-parser/src/authenticode.c | 13 +- deps/authenticode-parser/src/certificate.c | 5 +- .../src/countersignature.c | 369 ++++++++++++++++-- deps/authenticode-parser/src/helper.c | 2 +- deps/authenticode-parser/src/helper.h | 2 +- 6 files changed, 347 insertions(+), 52 deletions(-) diff --git a/deps/authenticode-parser/include/authenticode-parser/authenticode.h b/deps/authenticode-parser/include/authenticode-parser/authenticode.h index 4dfe8a2d2..88f96a3cd 100644 --- a/deps/authenticode-parser/include/authenticode-parser/authenticode.h +++ b/deps/authenticode-parser/include/authenticode-parser/authenticode.h @@ -106,8 +106,8 @@ typedef struct { char* key_alg; /* Name of the key algorithm */ char* sig_alg; /* Name of the signature algorithm */ char* sig_alg_oid; /* OID of the signature algorithm */ - time_t not_before; /* NotBefore validity */ - time_t not_after; /* NotAfter validity */ + int64_t not_before; /* NotBefore validity */ + int64_t not_after; /* NotAfter validity */ char* key; /* PEM encoded public key */ Attributes issuer_attrs; /* Parsed X509 Attributes of Issuer */ Attributes subject_attrs; /* Parsed X509 Attributes of Subject */ @@ -120,7 +120,7 @@ typedef struct { typedef struct { int verify_flags; /* COUNTERISGNATURE_VFY_ flag */ - time_t sign_time; /* Signing time of the timestamp countersignature */ + int64_t sign_time; /* Signing time of the timestamp countersignature */ char* digest_alg; /* Name of the digest algorithm used */ ByteArray digest; /* Stored message digest */ CertificateArray* chain; /* Certificate chain of the signer */ @@ -190,7 +190,7 @@ AuthenticodeArray* parse_authenticode(const uint8_t* pe_data, uint64_t pe_len); * @param len * @return AuthenticodeArray* */ -AuthenticodeArray* authenticode_new(const uint8_t* data, long len); +AuthenticodeArray* authenticode_new(const uint8_t* data, int32_t len); /** * @brief Deallocates AuthenticodeArray and all it's allocated members diff --git a/deps/authenticode-parser/src/authenticode.c b/deps/authenticode-parser/src/authenticode.c index 751510246..e8a0d596e 100644 --- a/deps/authenticode-parser/src/authenticode.c +++ b/deps/authenticode-parser/src/authenticode.c @@ -22,6 +22,7 @@ SOFTWARE. #include #include #include +#include #include #include #include @@ -274,7 +275,8 @@ static bool authenticode_verify(PKCS7* p7, PKCS7_SIGNER_INFO* si, X509* signCert } /* Creates all the Authenticode objects so we can parse them with OpenSSL, is not thread-safe, needs - * to be called once before any multi-threading environmentt - https://github.com/openssl/openssl/issues/13524 */ + * to be called once before any multi-threading environmentt - + * https://github.com/openssl/openssl/issues/13524 */ void initialize_authenticode_parser() { OBJ_create("1.3.6.1.4.1.311.2.1.12", "spcSpOpusInfo", "SPC_SP_OPUS_INFO_OBJID"); @@ -285,9 +287,9 @@ void initialize_authenticode_parser() /* Return array of Authenticode signatures stored in the data, there can be multiple * of signatures as Authenticode signatures are often nested through unauth attributes */ -AuthenticodeArray* authenticode_new(const uint8_t* data, long len) +AuthenticodeArray* authenticode_new(const uint8_t* data, int32_t len) { - if (!data || len == 0) + if (!data || len <= 0) return NULL; AuthenticodeArray* result = (AuthenticodeArray*)calloc(1, sizeof(*result)); @@ -318,7 +320,7 @@ AuthenticodeArray* authenticode_new(const uint8_t* data, long len) } /* We expect SignedData type of PKCS7 */ - if (!PKCS7_type_is_signed(p7)) { + if (!PKCS7_type_is_signed(p7) || !p7->d.sign) { auth->verify_flags = AUTHENTICODE_VFY_WRONG_PKCS7_TYPE; goto end; } @@ -567,7 +569,8 @@ AuthenticodeArray* parse_authenticode(const uint8_t* pe_data, uint64_t pe_len) uint32_t dwLength = letoh32(*(uint32_t*)(pe_data + cert_addr)); if (pe_len < cert_addr + dwLength) return NULL; - /* dwLength = offsetof(WIN_CERTIFICATE, bCertificate) + (size of the variable-length binary array contained within bCertificate) */ + /* dwLength = offsetof(WIN_CERTIFICATE, bCertificate) + (size of the variable-length binary + * array contained within bCertificate) */ AuthenticodeArray* auth_array = authenticode_new(pe_data + cert_addr + 0x8, dwLength - 0x8); if (!auth_array) return NULL; diff --git a/deps/authenticode-parser/src/certificate.c b/deps/authenticode-parser/src/certificate.c index e21f87407..cf688e77b 100644 --- a/deps/authenticode-parser/src/certificate.c +++ b/deps/authenticode-parser/src/certificate.c @@ -24,6 +24,7 @@ SOFTWARE. #include #include #include +#include #include #include #include @@ -308,8 +309,8 @@ Certificate* certificate_new(X509* x509) result->version = X509_get_version(x509); result->serial = integer_to_serial(X509_get_serialNumber(x509)); - result->not_after = ASN1_TIME_to_time_t(X509_get0_notAfter(x509)); - result->not_before = ASN1_TIME_to_time_t(X509_get0_notBefore(x509)); + result->not_after = ASN1_TIME_to_int64_t(X509_get0_notAfter(x509)); + result->not_before = ASN1_TIME_to_int64_t(X509_get0_notBefore(x509)); int sig_nid = X509_get_signature_nid(x509); result->sig_alg = strdup(OBJ_nid2ln(sig_nid)); diff --git a/deps/authenticode-parser/src/countersignature.c b/deps/authenticode-parser/src/countersignature.c index d905e239b..1c9ae7e02 100644 --- a/deps/authenticode-parser/src/countersignature.c +++ b/deps/authenticode-parser/src/countersignature.c @@ -21,8 +21,11 @@ SOFTWARE. #include "countersignature.h" +#include +#include #include #include +#include #include #include #include @@ -36,6 +39,73 @@ SOFTWARE. #include "helper.h" #include "structs.h" +struct CountersignatureImplStruct; + +typedef TS_TST_INFO* get_ts_tst_info_func(struct CountersignatureImplStruct*); +typedef STACK_OF(X509) * get_signers_func(struct CountersignatureImplStruct*); +typedef STACK_OF(X509) * get_certs_func(struct CountersignatureImplStruct*); +typedef int +verify_digest_func(struct CountersignatureImplStruct*, uint8_t* digest, size_t digest_size); +typedef BIO* verify_signature_init_func(struct CountersignatureImplStruct*); +typedef int +verify_signature_finish_func(struct CountersignatureImplStruct*, BIO* bio, X509* signer); + +#define IMPL_FUNC_NAME(func, type) ms_countersig_impl_##func##_##type##_ + +#define DECLARE_FUNCS(type) \ + get_ts_tst_info_func IMPL_FUNC_NAME(get_ts_tst_info, type); \ + get_signers_func IMPL_FUNC_NAME(get_signers, type); \ + get_certs_func IMPL_FUNC_NAME(get_certs, type); \ + verify_digest_func IMPL_FUNC_NAME(verify_digest, type); \ + verify_signature_init_func IMPL_FUNC_NAME(verify_signature_init, type); \ + verify_signature_finish_func IMPL_FUNC_NAME(verify_signature_finish, type); + +DECLARE_FUNCS(pkcs7) +DECLARE_FUNCS(cms) + +typedef struct { + get_ts_tst_info_func* get_ts_tst_info; + get_signers_func* get_signers; + get_certs_func* get_certs; + verify_digest_func* verify_digest; + verify_signature_init_func* verify_signature_init; + verify_signature_finish_func* verify_signature_finish; +} CountersignatureImplFuncs; + +#define FUNC_ARRAY_NAME_FOR_IMPL(type) countersig_impl_funcs_##type##_ +#define FUNC_ARRAY_FOR_IMPL(type) \ + static const CountersignatureImplFuncs FUNC_ARRAY_NAME_FOR_IMPL(type) = { \ + &IMPL_FUNC_NAME(get_ts_tst_info, type), \ + &IMPL_FUNC_NAME(get_signers, type), \ + &IMPL_FUNC_NAME(get_certs, type), \ + &IMPL_FUNC_NAME(verify_digest, type), \ + &IMPL_FUNC_NAME(verify_signature_init, type), \ + &IMPL_FUNC_NAME(verify_signature_finish, type), \ + }; + +FUNC_ARRAY_FOR_IMPL(pkcs7) +FUNC_ARRAY_FOR_IMPL(cms) + +typedef enum { + CS_IMPL_PKCS7, + CS_IMPL_CMS, +} CountersignatureImplType; + +typedef struct CountersignatureImplStruct { + CountersignatureImplType type; + const CountersignatureImplFuncs* funcs; + union { + PKCS7* pkcs7; + CMS_ContentInfo* cms; + }; + // this is here to serve as a cache for CMS because the only way to obtain + // certs from CMS is to use CMS_get1_certs which leaves the deallocation + // to the caller but it just complicates things if you need to remember to + // deallocate also certs. This makes it easier if CountersignatureImpl itself + // is an owner of this thing. + STACK_OF(X509) * _certs; +} CountersignatureImpl; + Countersignature* pkcs9_countersig_new( const uint8_t* data, long size, STACK_OF(X509) * certs, ASN1_STRING* enc_digest) { @@ -58,7 +128,7 @@ Countersignature* pkcs9_countersig_new( goto end; } - result->sign_time = ASN1_TIME_to_time_t(sign_time->value.utctime); + result->sign_time = ASN1_TIME_to_int64_t(sign_time->value.utctime); X509* signCert = X509_find_by_issuer_and_serial( certs, si->issuer_and_serial->issuer, si->issuer_and_serial->serial); @@ -133,7 +203,7 @@ Countersignature* pkcs9_countersig_new( /* compare the encrypted digest and calculated digest */ bool isValid = false; - + #if OPENSSL_VERSION_NUMBER >= 0x3000000fL size_t mdLen = EVP_MD_get_size(md); #else @@ -178,22 +248,264 @@ Countersignature* pkcs9_countersig_new( return result; } +TS_TST_INFO* IMPL_FUNC_NAME(get_ts_tst_info, pkcs7)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_PKCS7); + + return PKCS7_to_TS_TST_INFO(impl->pkcs7); +} + +TS_TST_INFO* IMPL_FUNC_NAME(get_ts_tst_info, cms)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_CMS); + + const ASN1_OBJECT* content_type = CMS_get0_eContentType(impl->cms); + if (!content_type || OBJ_obj2nid(content_type) != NID_id_smime_ct_TSTInfo) { + return NULL; + } + + ASN1_OCTET_STRING** content = CMS_get0_content(impl->cms); + if (!content || !*content) { + return NULL; + } + + const uint8_t* data = (*content)->data; + TS_TST_INFO* ts_tst_info = d2i_TS_TST_INFO(NULL, &data, (*content)->length); + if (!ts_tst_info) { + return NULL; + } + + return ts_tst_info; +} + +STACK_OF(X509) * IMPL_FUNC_NAME(get_signers, pkcs7)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_PKCS7); + + return PKCS7_get0_signers(impl->pkcs7, impl->pkcs7->d.sign->cert, 0); +} + +STACK_OF(X509) * IMPL_FUNC_NAME(get_signers, cms)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_CMS); + + STACK_OF(CMS_SignerInfo)* signer_infos = CMS_get0_SignerInfos(impl->cms); + if (!signer_infos) { + return NULL; + } + + // Use our func points to cache the certs and don't create another copy + STACK_OF(X509)* certs = impl->funcs->get_certs(impl); + + int si_count = sk_CMS_SignerInfo_num(signer_infos); + int cert_count = certs ? sk_X509_num(certs) : 0; + STACK_OF(X509)* result = sk_X509_new_null(); + + // PKCS7_get0_signers() lets us specify the certificate array and looks up signer certificate + // there With CMS_ContentInfo, we don't have direct access to signer certificate, just all the + // certificates The only thing we can do is to go through all signer infos and find those which + // match some certificate in all certificates. It essentially simulates what + // PKCS7_get0_signers() does. + for (int i = 0; i < si_count; ++i) { + CMS_SignerInfo* si = sk_CMS_SignerInfo_value(signer_infos, i); + if (!si) { + continue; + } + + if (certs) { + for (int j = 0; j < cert_count; ++j) { + X509* cert = sk_X509_value(certs, j); + if (!cert) { + continue; + } + + if (CMS_SignerInfo_cert_cmp(si, cert) == 0) { + if (!sk_X509_push(result, cert)) { + return NULL; + } + } + } + } + } + + return result; +} + +STACK_OF(X509) * IMPL_FUNC_NAME(get_certs, pkcs7)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_PKCS7); + + return impl->pkcs7->d.sign->cert; +} + +STACK_OF(X509) * IMPL_FUNC_NAME(get_certs, cms)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_CMS); + + if (impl->_certs) { + return impl->_certs; + } + + impl->_certs = CMS_get1_certs(impl->cms); + return impl->_certs; +} + +int IMPL_FUNC_NAME(verify_digest, pkcs7)( + CountersignatureImpl* impl, uint8_t* digest, size_t digest_size) +{ + assert(impl->type == CS_IMPL_PKCS7); + + X509_STORE* store = X509_STORE_new(); + TS_VERIFY_CTX* ctx = TS_VERIFY_CTX_new(); + TS_VERIFY_CTX_init(ctx); + + TS_VERIFY_CTX_set_flags(ctx, TS_VFY_VERSION | TS_VFY_IMPRINT); + TS_VERIFY_CTX_set_store(ctx, store); +#if OPENSSL_VERSION_NUMBER >= 0x3000000fL + TS_VERIFY_CTX_set_certs(ctx, impl->funcs->get_certs(impl)); +#else + TS_VERIFY_CTS_set_certs(ctx, impl->funcs->get_certs(impl)); +#endif + TS_VERIFY_CTX_set_imprint(ctx, digest, digest_size); + + int result = TS_RESP_verify_token(ctx, impl->pkcs7); + + X509_STORE_free(store); + OPENSSL_free(ctx); + + return result; +} + +int IMPL_FUNC_NAME(verify_digest, cms)( + CountersignatureImpl* impl, uint8_t* digest, size_t digest_size) +{ + assert(impl->type == CS_IMPL_CMS); + + // This is essentially just reimplementation of TS_RESP_verify_token() from OpenSSL + TS_TST_INFO* ts_tst_info = impl->funcs->get_ts_tst_info(impl); + if (!ts_tst_info || TS_TST_INFO_get_version(ts_tst_info) != 1) { + if (ts_tst_info) + TS_TST_INFO_free(ts_tst_info); + return 0; + } + + TS_MSG_IMPRINT* ts_imprint = TS_TST_INFO_get_msg_imprint(ts_tst_info); + if (!ts_imprint) { + TS_TST_INFO_free(ts_tst_info); + return 0; + } + + ASN1_OCTET_STRING* ts_imprint_digest = TS_MSG_IMPRINT_get_msg(ts_imprint); + if (!ts_imprint_digest) { + TS_TST_INFO_free(ts_tst_info); + return 0; + } + + if (ts_imprint_digest->length != (int)digest_size || + memcmp(ts_imprint_digest->data, digest, digest_size) != 0) { + TS_TST_INFO_free(ts_tst_info); + return 0; + } + + TS_TST_INFO_free(ts_tst_info); + return 1; +} + +BIO* IMPL_FUNC_NAME(verify_signature_init, pkcs7)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_PKCS7); + + return PKCS7_dataInit(impl->pkcs7, NULL); +} + +BIO* IMPL_FUNC_NAME(verify_signature_init, cms)(CountersignatureImpl* impl) +{ + assert(impl->type == CS_IMPL_CMS); + + return CMS_dataInit(impl->cms, NULL); +} + +int IMPL_FUNC_NAME(verify_signature_finish, pkcs7)( + CountersignatureImpl* impl, BIO* bio, X509* signer) +{ + assert(impl->type == CS_IMPL_PKCS7); + + /* Verify signature with PKCS7_signatureVerify + because TS_RESP_verify_token would try to verify + chain and without trust anchors it always fails */ + PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value(PKCS7_get_signer_info(impl->pkcs7), 0); + return PKCS7_signatureVerify(bio, impl->pkcs7, si, signer); +} + +int IMPL_FUNC_NAME(verify_signature_finish, cms)(CountersignatureImpl* impl, BIO* bio, X509* signer) +{ + assert(impl->type == CS_IMPL_CMS); + + (void)signer; + CMS_SignerInfo* si = sk_CMS_SignerInfo_value(CMS_get0_SignerInfos(impl->cms), 0); + return CMS_SignerInfo_verify_content(si, bio); +} + +CountersignatureImpl* ms_countersig_impl_new(const uint8_t* data, long size) +{ + const uint8_t* d = data; + PKCS7* p7 = d2i_PKCS7(NULL, &d, size); + if (p7 && PKCS7_type_is_signed(p7) && p7->d.sign) { + CountersignatureImpl* result = + (CountersignatureImpl*)calloc(1, sizeof(CountersignatureImpl)); + result->type = CS_IMPL_PKCS7; + result->funcs = &FUNC_ARRAY_NAME_FOR_IMPL(pkcs7); + result->pkcs7 = p7; + return result; + } + + d = data; + CMS_ContentInfo* cms = d2i_CMS_ContentInfo(NULL, &d, size); + if (cms) { + CountersignatureImpl* result = + (CountersignatureImpl*)calloc(1, sizeof(CountersignatureImpl)); + result->type = CS_IMPL_CMS; + result->funcs = &FUNC_ARRAY_NAME_FOR_IMPL(cms); + result->cms = cms; + return result; + } + + return NULL; +} + +void ms_countersig_impl_free(CountersignatureImpl* impl) +{ + switch (impl->type) { + case CS_IMPL_PKCS7: + PKCS7_free(impl->pkcs7); + break; + case CS_IMPL_CMS: + if (impl->_certs) { + sk_X509_pop_free(impl->_certs, X509_free); + } + CMS_ContentInfo_free(impl->cms); + break; + } + + free(impl); +} + Countersignature* ms_countersig_new(const uint8_t* data, long size, ASN1_STRING* enc_digest) { Countersignature* result = (Countersignature*)calloc(1, sizeof(*result)); if (!result) return NULL; - PKCS7* p7 = d2i_PKCS7(NULL, &data, size); - if (!p7) { + CountersignatureImpl* impl = ms_countersig_impl_new(data, size); + if (!impl) { result->verify_flags = COUNTERSIGNATURE_VFY_CANT_PARSE; return result; } - TS_TST_INFO* ts = PKCS7_to_TS_TST_INFO(p7); + TS_TST_INFO* ts = impl->funcs->get_ts_tst_info(impl); if (!ts) { result->verify_flags = COUNTERSIGNATURE_VFY_CANT_PARSE; - PKCS7_free(p7); + ms_countersig_impl_free(impl); return result; } @@ -201,20 +513,21 @@ Countersignature* ms_countersig_new(const uint8_t* data, long size, ASN1_STRING* if (!rawTime) { result->verify_flags = COUNTERSIGNATURE_VFY_TIME_MISSING; TS_TST_INFO_free(ts); - PKCS7_free(p7); + ms_countersig_impl_free(impl); return result; } - result->sign_time = ASN1_TIME_to_time_t(rawTime); + result->sign_time = ASN1_TIME_to_int64_t(rawTime); - STACK_OF(X509)* sigs = PKCS7_get0_signers(p7, p7->d.sign->cert, 0); + STACK_OF(X509)* sigs = impl->funcs->get_signers(impl); X509* signCert = sk_X509_value(sigs, 0); if (!signCert) { result->verify_flags = COUNTERSIGNATURE_VFY_NO_SIGNER_CERT; goto end; } - result->chain = parse_signer_chain(signCert, p7->d.sign->cert); + STACK_OF(X509)* certs = impl->funcs->get_certs(impl); + result->chain = parse_signer_chain(signCert, certs); /* Imprint == digest */ TS_MSG_IMPRINT* imprint = TS_TST_INFO_get_msg_imprint(ts); @@ -259,52 +572,30 @@ Countersignature* ms_countersig_new(const uint8_t* data, long size, ASN1_STRING* goto end; } - TS_VERIFY_CTX* ctx = TS_VERIFY_CTX_new(); - X509_STORE* store = X509_STORE_new(); - TS_VERIFY_CTX_init(ctx); - - TS_VERIFY_CTX_set_flags(ctx, TS_VFY_VERSION | TS_VFY_IMPRINT); - TS_VERIFY_CTX_set_store(ctx, store); -#if OPENSSL_VERSION_NUMBER >= 0x3000000fL - TS_VERIFY_CTX_set_certs(ctx, p7->d.sign->cert); -#else - TS_VERIFY_CTS_set_certs(ctx, p7->d.sign->cert); -#endif - TS_VERIFY_CTX_set_imprint(ctx, calc_digest, mdLen); - - bool isValid = TS_RESP_verify_token(ctx, p7) == 1; - - X509_STORE_free(store); - OPENSSL_free(ctx); - + bool isValid = impl->funcs->verify_digest(impl, calc_digest, mdLen) == 1; if (!isValid) { result->verify_flags = COUNTERSIGNATURE_VFY_INVALID; goto end; } - /* Verify signature with PKCS7_signatureVerify - because TS_RESP_verify_token would try to verify - chain and without trust anchors it always fails */ - BIO* p7bio = PKCS7_dataInit(p7, NULL); + BIO* bio = impl->funcs->verify_signature_init(impl); char buf[4096]; - /* We now have to 'read' from p7bio to calculate digests etc. */ - while (BIO_read(p7bio, buf, sizeof(buf)) > 0) + /* We now have to 'read' from bio to calculate digests etc. */ + while (BIO_read(bio, buf, sizeof(buf)) > 0) continue; - PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value(PKCS7_get_signer_info(p7), 0); - - isValid = PKCS7_signatureVerify(p7bio, p7, si, signCert) == 1; + isValid = impl->funcs->verify_signature_finish(impl, bio, signCert) == 1; - BIO_free_all(p7bio); + BIO_free_all(bio); if (!isValid) result->verify_flags = COUNTERSIGNATURE_VFY_INVALID; end: sk_X509_free(sigs); - PKCS7_free(p7); TS_TST_INFO_free(ts); + ms_countersig_impl_free(impl); return result; } diff --git a/deps/authenticode-parser/src/helper.c b/deps/authenticode-parser/src/helper.c index f65a39179..ecdf7ca84 100644 --- a/deps/authenticode-parser/src/helper.c +++ b/deps/authenticode-parser/src/helper.c @@ -73,7 +73,7 @@ int byte_array_init(ByteArray* arr, const uint8_t* data, int len) return 0; } -time_t ASN1_TIME_to_time_t(const ASN1_TIME* time) +int64_t ASN1_TIME_to_int64_t(const ASN1_TIME* time) { struct tm t = {0}; if (!time) diff --git a/deps/authenticode-parser/src/helper.h b/deps/authenticode-parser/src/helper.h index e435cbc51..dc1261df1 100644 --- a/deps/authenticode-parser/src/helper.h +++ b/deps/authenticode-parser/src/helper.h @@ -59,7 +59,7 @@ int calculate_digest(const EVP_MD* md, const uint8_t* data, size_t len, uint8_t* /* Copies data of length len into already existing arr */ int byte_array_init(ByteArray* arr, const uint8_t* data, int len); /* Converts ASN1_TIME string time into a unix timestamp */ -time_t ASN1_TIME_to_time_t(const ASN1_TIME* time); +int64_t ASN1_TIME_to_int64_t(const ASN1_TIME* time); #ifdef __cplusplus }