From 846827dda4b6d93cd625457b589a689cc6481fba Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Sun, 10 Nov 2024 22:49:35 +0200 Subject: [PATCH] Validate signing certificate on issue date and on TSA time IB-8296 Signed-off-by: Raul Metsma --- src/SignatureTST.cpp | 1 + src/SignatureXAdES_T.cpp | 5 +-- src/XMLDocument.h | 22 ++--------- src/crypto/Connect.cpp | 17 ++++----- src/crypto/Digest.cpp | 9 +++-- src/crypto/Digest.h | 7 ++-- src/crypto/OCSP.cpp | 29 ++++++--------- src/crypto/OCSP.h | 7 ++-- src/crypto/OpenSSLHelpers.h | 6 +-- src/crypto/TS.cpp | 65 +++++++++++++++----------------- src/crypto/TS.h | 10 +++-- src/crypto/X509CertStore.cpp | 72 ++++++++++++++---------------------- src/crypto/X509CertStore.h | 9 +++-- src/util/memory.h | 46 +++++++++++++++++++++++ 14 files changed, 157 insertions(+), 148 deletions(-) create mode 100644 src/util/memory.h diff --git a/src/SignatureTST.cpp b/src/SignatureTST.cpp index 6665da184..52d521221 100644 --- a/src/SignatureTST.cpp +++ b/src/SignatureTST.cpp @@ -21,6 +21,7 @@ #include "ASiC_S.h" #include "DataFile_p.h" +#include "crypto/Digest.h" #include "crypto/TS.h" #include "crypto/X509Cert.h" #include "util/DateTime.h" diff --git a/src/SignatureXAdES_T.cpp b/src/SignatureXAdES_T.cpp index 38ffa5caa..f0c1da962 100644 --- a/src/SignatureXAdES_T.cpp +++ b/src/SignatureXAdES_T.cpp @@ -25,6 +25,7 @@ #include "crypto/OCSP.h" #include "crypto/TS.h" #include "crypto/X509Cert.h" +#include "crypto/X509CertStore.h" #include "util/DateTime.h" #include "util/log.h" @@ -113,9 +114,7 @@ void SignatureXAdES_T::validate(const std::string &policy) const signatures->c14n(digest, canonicalizationMethod, signatureValue()); }); - tm tm = tsa.time(); - time_t validateTime = util::date::mkgmtime(tm); - if(!signingCertificate().isValid(&validateTime)) + if(!X509CertStore::instance()->verify(signingCertificate(), policy == POLv1, tsa.time())) THROW("Signing certificate was not valid on signing time"); auto completeCertRefs = usp/"CompleteCertificateRefs"; diff --git a/src/XMLDocument.h b/src/XMLDocument.h index 2cea1de81..446f0e468 100644 --- a/src/XMLDocument.h +++ b/src/XMLDocument.h @@ -21,6 +21,7 @@ #include "crypto/Digest.h" #include "util/log.h" +#include "util/memory.h" #include #include @@ -40,23 +41,6 @@ namespace digidoc { #define VERSION_CHECK(major, minor, patch) (((major)<<16)|((minor)<<8)|(patch)) -template struct unique_xml; -template -struct unique_xml -{ - using type = std::unique_ptr; -}; - -template -using unique_xml_t = typename unique_xml::type; - -template -[[nodiscard]] -constexpr std::unique_ptr make_unique_ptr(T *p, D d) noexcept -{ - return {p, std::forward(d)}; -} - static std::vector from_base64(std::string_view data) { static constexpr std::string_view whitespace {" \n\r\f\t\v"}; @@ -301,7 +285,7 @@ struct XMLNode: public XMLElem } }; -struct XMLDocument: public unique_xml_t, public XMLNode +struct XMLDocument: public unique_free_t, public XMLNode { static constexpr std::string_view C14D_ID_1_0 {"http://www.w3.org/TR/2001/REC-xml-c14n-20010315"}; static constexpr std::string_view C14D_ID_1_0_COM {"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"}; @@ -313,7 +297,7 @@ struct XMLDocument: public unique_xml_t, public XMLNode using XMLNode::operator bool; XMLDocument(element_type *ptr = {}, const XMLName &n = {}) noexcept - : std::unique_ptr(ptr, xmlFreeDoc) + : unique_free_t(ptr, xmlFreeDoc) , XMLNode{xmlDocGetRootElement(get())} { if(d && !n.name.empty() && n.name != name() && !n.ns.empty() && n.ns != ns()) diff --git a/src/crypto/Connect.cpp b/src/crypto/Connect.cpp index 849426873..9c8d0f13d 100644 --- a/src/crypto/Connect.cpp +++ b/src/crypto/Connect.cpp @@ -29,7 +29,6 @@ #include -#include #include #ifdef _WIN32 @@ -147,13 +146,13 @@ Connect::Connect(const string &_url, string _method, int _timeout, const vector< if(!certs.empty()) { SSL_CTX_set_verify(ssl.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); - SSL_CTX_set_cert_verify_callback(ssl.get(), [](X509_STORE_CTX *store, void *data) -> int { - X509 *x509 = X509_STORE_CTX_get0_cert(store); - auto *certs = (vector*)data; - return any_of(certs->cbegin(), certs->cend(), [x509](const X509Cert &cert) { - return cert && cert == x509; - }) ? 1 : 0; - }, const_cast*>(&certs)); + X509_STORE *store = SSL_CTX_get_cert_store(ssl.get()); + X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST | X509_V_FLAG_PARTIAL_CHAIN); + for(const X509Cert &cert: certs) + { + if(cert.handle()) + X509_STORE_add_cert(store, cert.handle()); + } } BIO *sbio = BIO_new_ssl(ssl.get(), 1); if(!sbio) @@ -292,7 +291,7 @@ Connect::Result Connect::exec(initializer_list> he stringstream stream(r.content); string line; auto to_lower = [](string str) { - std::transform(str.begin(), str.end(), str.begin(), ::tolower); + transform(str.begin(), str.end(), str.begin(), ::tolower); return str; }; while(getline(stream, line)) diff --git a/src/crypto/Digest.cpp b/src/crypto/Digest.cpp index 263d86cc5..bc3a99693 100644 --- a/src/crypto/Digest.cpp +++ b/src/crypto/Digest.cpp @@ -26,6 +26,7 @@ #include #include +#include using namespace std; using namespace digidoc; @@ -37,7 +38,7 @@ using namespace digidoc; * @throws Exception throws exception if the digest calculator initialization failed. */ Digest::Digest(string_view uri) - : d(SCOPE_PTR(EVP_MD_CTX, EVP_MD_CTX_new())) + : d(EVP_MD_CTX_new(), EVP_MD_CTX_free) { if(uri.empty() && Conf::instance()->digestUri() == URI_SHA1) THROW("Unsupported digest method %.*s", int(uri.size()), uri.data()); @@ -68,7 +69,7 @@ vector Digest::addDigestInfo(vector digest, string vector Digest::digestInfoDigest(const std::vector &digest) { const unsigned char *p = digest.data(); - SCOPE(X509_SIG, sig, d2i_X509_SIG(nullptr, &p, long(digest.size()))); + auto sig = make_unique_ptr(d2i_X509_SIG(nullptr, &p, long(digest.size())), X509_SIG_free); if(!sig) return {}; const ASN1_OCTET_STRING *value {}; @@ -79,7 +80,7 @@ vector Digest::digestInfoDigest(const std::vector string Digest::digestInfoUri(const std::vector &digest) { const unsigned char *p = digest.data(); - SCOPE(X509_SIG, sig, d2i_X509_SIG(nullptr, &p, long(digest.size()))); + auto sig = make_unique_ptr(d2i_X509_SIG(nullptr, &p, long(digest.size())), X509_SIG_free); if(!sig) return {}; const X509_ALGOR *algor {}; @@ -269,7 +270,7 @@ vector Digest::result() const return result; } -vector Digest::result(const vector &data) +vector Digest::result(const vector &data) const { update(data.data(), data.size()); return result(); diff --git a/src/crypto/Digest.h b/src/crypto/Digest.h index d9472fc82..c00e3f669 100644 --- a/src/crypto/Digest.h +++ b/src/crypto/Digest.h @@ -21,7 +21,8 @@ #include "../Exports.h" -#include +#include "util/memory.h" + #include #include @@ -73,7 +74,7 @@ namespace digidoc Digest(std::string_view uri = {}); void update(const unsigned char *data, size_t length) const; void update(std::istream &is) const; - std::vector result(const std::vector &data); + std::vector result(const std::vector &data) const; std::vector result() const; std::string uri() const; @@ -89,7 +90,7 @@ namespace digidoc static std::string digestInfoUri(const std::vector &digest); private: - std::unique_ptr d; + unique_free_t d; }; } diff --git a/src/crypto/OCSP.cpp b/src/crypto/OCSP.cpp index bb512ba11..35d9998e9 100644 --- a/src/crypto/OCSP.cpp +++ b/src/crypto/OCSP.cpp @@ -46,6 +46,8 @@ using namespace std; * Initialize OCSP certificate validator. */ OCSP::OCSP(const X509Cert &cert, const X509Cert &issuer) + : resp(nullptr, OCSP_RESPONSE_free) + , basic(nullptr, OCSP_BASICRESP_free) { if(!cert) THROW("Can not check X.509 certificate, certificate is NULL pointer."); @@ -69,7 +71,7 @@ OCSP::OCSP(const X509Cert &cert, const X509Cert &issuer) } OCSP_CERTID *certId = OCSP_cert_to_id(nullptr, cert.handle(), issuer.handle()); - SCOPE(OCSP_REQUEST, req, OCSP_REQUEST_new()); + auto req = make_unique_ptr(OCSP_REQUEST_new(), OCSP_REQUEST_free); if(!req) THROW_OPENSSLEXCEPTION("Failed to create new OCSP request, out of memory?"); @@ -91,7 +93,7 @@ OCSP::OCSP(const X509Cert &cert, const X509Cert &issuer) if(!result) THROW("Failed to send OCSP request"); const unsigned char *p2 = (const unsigned char*)result.content.c_str(); - resp.reset(d2i_OCSP_RESPONSE(nullptr, &p2, long(result.content.size())), OCSP_RESPONSE_free); + resp.reset(d2i_OCSP_RESPONSE(nullptr, &p2, long(result.content.size()))); switch(int respStatus = OCSP_response_status(resp.get())) { @@ -106,7 +108,7 @@ OCSP::OCSP(const X509Cert &cert, const X509Cert &issuer) THROW("OCSP request failed, response status: %s", OCSP_response_status_str(respStatus)); } - basic.reset(OCSP_response_get1_basic(resp.get()), OCSP_BASICRESP_free); + basic.reset(OCSP_response_get1_basic(resp.get())); if(!basic) THROW("Incorrect OCSP response."); @@ -127,12 +129,14 @@ OCSP::OCSP(const X509Cert &cert, const X509Cert &issuer) } OCSP::OCSP(const unsigned char *data, size_t size) + : resp(nullptr, OCSP_RESPONSE_free) + , basic(nullptr, OCSP_BASICRESP_free) { if(size == 0) return; - resp.reset(d2i_OCSP_RESPONSE(nullptr, &data, long(size)), OCSP_RESPONSE_free); + resp.reset(d2i_OCSP_RESPONSE(nullptr, &data, long(size))); if(resp) - basic.reset(OCSP_response_get1_basic(resp.get()), OCSP_BASICRESP_free); + basic.reset(OCSP_response_get1_basic(resp.get())); } bool OCSP::compareResponderCert(const X509Cert &cert) const @@ -187,17 +191,8 @@ void OCSP::verifyResponse(const X509Cert &cert) const THROW("Failed to verify OCSP response."); tm tm = producedAt(); - time_t t = util::date::mkgmtime(tm); - SCOPE(X509_STORE, store, X509CertStore::createStore(X509CertStore::OCSP, &t)); - STACK_OF(X509) *stack = sk_X509_new_null(); - for(const X509Cert &i: X509CertStore::instance()->certs(X509CertStore::OCSP)) - { - if(compareResponderCert(i)) - sk_X509_push(stack, i.handle()); - } - int result = OCSP_basic_verify(basic.get(), stack, store.get(), OCSP_NOCHECKS); - sk_X509_free(stack); - if(result != 1) + auto store = X509CertStore::createStore(X509CertStore::OCSP, tm); + if(OCSP_basic_verify(basic.get(), nullptr, store.get(), OCSP_NOCHECKS | OCSP_PARTIAL_CHAIN) != 1) { unsigned long err = ERR_get_error(); if(ERR_GET_LIB(err) == ERR_LIB_OCSP && @@ -228,7 +223,7 @@ void OCSP::verifyResponse(const X509Cert &cert) const ASN1_OBJECT *md {}; if(OCSP_id_get0_info(nullptr, &md, nullptr, nullptr, const_cast(certID)) == 1) evp_md = EVP_get_digestbyobj(md); - SCOPE(OCSP_CERTID, certId, OCSP_cert_to_id(evp_md, cert.handle(), issuer.handle())); + auto certId = make_unique_ptr(OCSP_cert_to_id(evp_md, cert.handle(), issuer.handle()), OCSP_CERTID_free); if(OCSP_resp_find_status(basic.get(), certId.get(), &status, nullptr, nullptr, nullptr, nullptr) == 1) break; } diff --git a/src/crypto/OCSP.h b/src/crypto/OCSP.h index 6c66fec4a..76cce005b 100644 --- a/src/crypto/OCSP.h +++ b/src/crypto/OCSP.h @@ -19,7 +19,8 @@ #pragma once -#include +#include "util/memory.h" + #include using OCSP_RESPONSE = struct ocsp_response_st; @@ -50,7 +51,7 @@ namespace digidoc private: bool compareResponderCert(const X509Cert &cert) const; - std::shared_ptr resp; - std::shared_ptr basic; + unique_free_t resp; + unique_free_t basic; }; } diff --git a/src/crypto/OpenSSLHelpers.h b/src/crypto/OpenSSLHelpers.h index 1d810d22c..8f51f45ec 100644 --- a/src/crypto/OpenSSLHelpers.h +++ b/src/crypto/OpenSSLHelpers.h @@ -21,9 +21,7 @@ #include "Exception.h" #include "util/log.h" - -#include -#include +#include "util/memory.h" #include @@ -34,7 +32,7 @@ namespace digidoc { -#define SCOPE_PTR_FREE(TYPE, DATA, FREE) std::unique_ptr(static_cast(DATA), FREE) +#define SCOPE_PTR_FREE(TYPE, DATA, FREE) make_unique_ptr(DATA, FREE) #define SCOPE_PTR(TYPE, DATA) SCOPE_PTR_FREE(TYPE, DATA, TYPE##_free) #define SCOPE(TYPE, VAR, DATA) auto VAR = SCOPE_PTR_FREE(TYPE, DATA, TYPE##_free) diff --git a/src/crypto/TS.cpp b/src/crypto/TS.cpp index 47ab67394..60677a7b3 100644 --- a/src/crypto/TS.cpp +++ b/src/crypto/TS.cpp @@ -23,6 +23,7 @@ #include "Container.h" #include "Exception.h" #include "crypto/Connect.h" +#include "crypto/Digest.h" #include "crypto/OpenSSLHelpers.h" #include "crypto/X509CertStore.h" #include "util/DateTime.h" @@ -56,17 +57,17 @@ void *OPENSSL_memdup(const void *data, size_t size) #endif TS::TS(const string &url, const Digest &digest) + : d(nullptr, PKCS7_free) + , cms(nullptr, CMS_ContentInfo_free) { - auto req = SCOPE_PTR(TS_REQ, TS_REQ_new()); + auto req = make_unique_ptr(TS_REQ_new(), TS_REQ_free); TS_REQ_set_version(req.get(), 1); TS_REQ_set_cert_req(req.get(), 1); - auto algo = SCOPE_PTR(X509_ALGOR, X509_ALGOR_new()); - algo->algorithm = OBJ_nid2obj(Digest::toMethod(digest.uri())); - algo->parameter = ASN1_TYPE_new(); - algo->parameter->type = V_ASN1_NULL; + auto algo = make_unique_ptr(X509_ALGOR_new(), X509_ALGOR_free); + X509_ALGOR_set0(algo.get(), OBJ_nid2obj(Digest::toMethod(digest.uri())), V_ASN1_NULL, nullptr); - auto msg_imprint = SCOPE_PTR(TS_MSG_IMPRINT, TS_MSG_IMPRINT_new()); + auto msg_imprint = make_unique_ptr(TS_MSG_IMPRINT_new(), TS_MSG_IMPRINT_free); TS_MSG_IMPRINT_set_algo(msg_imprint.get(), algo.get()); vector digestdata = digest.result(); TS_MSG_IMPRINT_set_msg(msg_imprint.get(), digestdata.data(), int(digestdata.size())); @@ -75,15 +76,14 @@ TS::TS(const string &url, const Digest &digest) #if 0 if(!policy.empty()) { - auto obj = SCOPE_PTR(ASN1_OBJECT, OBJ_txt2obj(policy.c_str(), 0)); + auto obj = make_unique_ptr(OBJ_txt2obj(policy.c_str(), 0), ASN1_OBJECT_free); TS_REQ_set_policy_id(req.get(), obj.get()); } #endif - auto nonce = SCOPE_PTR(ASN1_INTEGER, ASN1_INTEGER_new()); + auto nonce = make_unique_ptr(ASN1_INTEGER_new(), ASN1_INTEGER_free); ASN1_STRING_set(nonce.get(), nullptr, 20); - nonce->data[0] = 0; - while(nonce->data[0] == 0) // Make sure that first byte is not 0x00 + for(nonce->data[0] = 0; nonce->data[0] == 0;) // Make sure that first byte is not 0x00 RAND_bytes(nonce->data, nonce->length); TS_REQ_set_nonce(req.get(), nonce.get()); @@ -109,25 +109,30 @@ TS::TS(const string &url, const Digest &digest) if(!result) THROW("Failed to send Time-stamp request"); - const unsigned char *p2 = (const unsigned char*)result.content.c_str(); - auto resp = SCOPE_PTR(TS_RESP, d2i_TS_RESP(nullptr, &p2, long(result.content.size()))); + const auto *p2 = (const unsigned char*)result.content.c_str(); + auto resp = make_unique_ptr(d2i_TS_RESP(nullptr, &p2, long(result.content.size())), TS_RESP_free); if(!resp) THROW_OPENSSLEXCEPTION("Failed to parse TS response."); - auto ctx = SCOPE_PTR(TS_VERIFY_CTX, TS_REQ_to_TS_VERIFY_CTX(req.get(), nullptr)); + auto ctx = make_unique_ptr(TS_REQ_to_TS_VERIFY_CTX(req.get(), nullptr), TS_VERIFY_CTX_free); TS_VERIFY_CTX_set_flags(ctx.get(), TS_VFY_VERSION|TS_VFY_NONCE); if(TS_RESP_verify_response(ctx.get(), resp.get()) != 1) THROW_OPENSSLEXCEPTION("Failed to verify TS response."); - d.reset(PKCS7_dup(TS_RESP_get_token(resp.get())), PKCS7_free); + d.reset(PKCS7_dup(TS_RESP_get_token(resp.get()))); DEBUG("TSA time %s", util::date::to_string(time()).c_str()); } TS::TS(const unsigned char *data, size_t size) + : d(nullptr, PKCS7_free) + , cms(nullptr, [](CMS_ContentInfo *contentInfo) { + CMS_ContentInfo_free(contentInfo); + ERR_clear_error(); + }) { if(size == 0) return; - d.reset(d2i_PKCS7(nullptr, &data, long(size)), PKCS7_free); + d.reset(d2i_PKCS7(nullptr, &data, long(size))); #ifndef OPENSSL_NO_CMS if(d) return; @@ -139,10 +144,7 @@ TS::TS(const unsigned char *data, size_t size) * * If PKCS7 wrapped TimeStamp parsing fails, try with CMS wrapping */ - cms.reset(d2i_CMS_ContentInfo(nullptr, &data, long(size)), [](CMS_ContentInfo *contentInfo) { - CMS_ContentInfo_free(contentInfo); - ERR_clear_error(); - }); + cms.reset(d2i_CMS_ContentInfo(nullptr, &data, long(size))); if(!cms || OBJ_obj2nid(CMS_get0_eContentType(cms.get())) != NID_id_smime_ct_TSTInfo) cms.reset(); @@ -173,15 +175,15 @@ X509Cert TS::cert() const auto TS::tstInfo() const { if(d) - return SCOPE_PTR(TS_TST_INFO, PKCS7_to_TS_TST_INFO(d.get())); + return make_unique_ptr(PKCS7_to_TS_TST_INFO(d.get()), TS_TST_INFO_free); #ifndef OPENSSL_NO_CMS if(cms) { - auto out = SCOPE_PTR(BIO, CMS_dataInit(cms.get(), nullptr)); - return SCOPE_PTR(TS_TST_INFO, d2i_TS_TST_INFO_bio(out.get(), nullptr)); + auto out = make_unique_ptr(CMS_dataInit(cms.get(), nullptr), BIO_free); + return make_unique_ptr(d2i_TS_TST_INFO_bio(out.get(), nullptr), TS_TST_INFO_free); } #endif - return SCOPE_PTR(TS_TST_INFO, nullptr); + return make_unique_ptr(nullptr, TS_TST_INFO_free); } string TS::digestMethod() const @@ -214,7 +216,7 @@ string TS::serial() const if(!info) return {}; - if(auto bn = SCOPE_PTR_FREE(BIGNUM, ASN1_INTEGER_to_BN(TS_TST_INFO_get_serial(info.get()), nullptr), BN_free)) + if(auto bn = make_unique_ptr(ASN1_INTEGER_to_BN(TS_TST_INFO_get_serial(info.get()), nullptr), BN_free)) { auto openssl_free = [](char *data) { OPENSSL_free(data); }; if(auto str = unique_ptr(BN_bn2dec(bn.get()), openssl_free)) @@ -234,18 +236,11 @@ tm TS::time() const void TS::verify(const vector &digest) { tm tm = time(); - time_t t = util::date::mkgmtime(tm); - auto store = SCOPE_PTR(X509_STORE, X509CertStore::createStore(X509CertStore::TSA, &t)); + auto store = X509CertStore::createStore(X509CertStore::TSA, tm); X509CertStore::instance()->activate(cert()); - auto csc = SCOPE_PTR(X509_STORE_CTX, X509_STORE_CTX_new()); - if (!csc) - THROW_OPENSSLEXCEPTION("Failed to create X509_STORE_CTX"); - if(!X509_STORE_CTX_init(csc.get(), store.get(), nullptr, nullptr)) - THROW_OPENSSLEXCEPTION("Failed to init X509_STORE_CTX"); - if(d) { - auto ctx = SCOPE_PTR(TS_VERIFY_CTX, TS_VERIFY_CTX_new()); + auto ctx = make_unique_ptr(TS_VERIFY_CTX_new(), TS_VERIFY_CTX_free); TS_VERIFY_CTX_set_flags(ctx.get(), TS_VFY_IMPRINT|TS_VFY_VERSION|TS_VFY_SIGNATURE); TS_VERIFY_CTX_set_imprint(ctx.get(), (unsigned char*)OPENSSL_memdup(digest.data(), digest.size()), long(digest.size())); @@ -265,7 +260,7 @@ void TS::verify(const vector &digest) #ifndef OPENSSL_NO_CMS else if(cms) { - auto out = SCOPE_PTR(BIO, BIO_new(BIO_s_mem())); + auto out = make_unique_ptr(BIO_new(BIO_s_mem()), BIO_free); // Override smime_sign purpose bit because it is actually timestamp X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new(); X509_VERIFY_PARAM_set1_name(param, "smime_sign"); @@ -276,7 +271,7 @@ void TS::verify(const vector &digest) if(err != 1) THROW_OPENSSLEXCEPTION("Failed to verify TS response."); - auto info = SCOPE_PTR(TS_TST_INFO, d2i_TS_TST_INFO_bio(out.get(), nullptr)); + auto info = make_unique_ptr(d2i_TS_TST_INFO_bio(out.get(), nullptr), TS_TST_INFO_free); ASN1_OCTET_STRING *msg = TS_MSG_IMPRINT_get_msg(TS_TST_INFO_get_msg_imprint(info.get())); if(digest.size() != size_t(ASN1_STRING_length(msg)) || memcmp(digest.data(), ASN1_STRING_get0_data(msg), digest.size()) != 0) diff --git a/src/crypto/TS.h b/src/crypto/TS.h index 98f1d4e26..665bc567f 100644 --- a/src/crypto/TS.h +++ b/src/crypto/TS.h @@ -19,12 +19,16 @@ #pragma once -#include "Digest.h" +#include "util/memory.h" + +#include +#include using PKCS7 = struct pkcs7_st; using CMS_ContentInfo = struct CMS_ContentInfo_st; namespace digidoc { +class Digest; class X509Cert; class TS @@ -46,8 +50,8 @@ class TS private: auto tstInfo() const; - std::shared_ptr d; - std::shared_ptr cms; + unique_free_t d; + unique_free_t cms; }; } diff --git a/src/crypto/X509CertStore.cpp b/src/crypto/X509CertStore.cpp index 083b49c87..c92b49356 100644 --- a/src/crypto/X509CertStore.cpp +++ b/src/crypto/X509CertStore.cpp @@ -120,7 +120,7 @@ X509Cert X509CertStore::findIssuer(const X509Cert &cert, const Type &type) const X509Cert X509CertStore::issuerFromAIA(const X509Cert &cert) { - SCOPE(AUTHORITY_INFO_ACCESS, aia, X509_get_ext_d2i(cert.handle(), NID_info_access, nullptr, nullptr)); + auto aia = make_unique_ptr(X509_get_ext_d2i(cert.handle(), NID_info_access, nullptr, nullptr), AUTHORITY_INFO_ACCESS_free); if(!aia) return X509Cert(); string url; @@ -137,28 +137,19 @@ X509Cert X509CertStore::issuerFromAIA(const X509Cert &cert) return X509Cert((const unsigned char*)result.content.c_str(), result.content.size()); } -X509_STORE* X509CertStore::createStore(const Type &type, const time_t *t) +unique_free_t X509CertStore::createStore(const Type &type, tm &tm) { - SCOPE(X509_STORE, store, X509_STORE_new()); + auto store = make_unique_ptr(X509_STORE_new(), X509_STORE_free); if (!store) THROW_OPENSSLEXCEPTION("Failed to create X509_STORE_CTX"); - - if(type == CA) - X509_STORE_set_verify_cb(store.get(), [](int ok, X509_STORE_CTX *ctx) -> int { return validate(ok, ctx, CA); }); - else if(type == OCSP) - X509_STORE_set_verify_cb(store.get(), [](int ok, X509_STORE_CTX *ctx) -> int { return validate(ok, ctx, OCSP); }); - else if(type == TSA) - X509_STORE_set_verify_cb(store.get(), [](int ok, X509_STORE_CTX *ctx) -> int { return validate(ok, ctx, TSA); }); - - if(t) - { - X509_VERIFY_PARAM_set_time(X509_STORE_get0_param(store.get()), *t); - X509_STORE_set_flags(store.get(), X509_V_FLAG_USE_CHECK_TIME); - } - return store.release(); + X509_STORE_set_verify_cb(store.get(), X509CertStore::validate); + X509_STORE_set_ex_data(store.get(), 0, const_cast(&type)); + X509_STORE_set_flags(store.get(), X509_V_FLAG_USE_CHECK_TIME | X509_V_FLAG_PARTIAL_CHAIN); + X509_VERIFY_PARAM_set_time(X509_STORE_get0_param(store.get()), util::date::mkgmtime(tm)); + return store; } -int X509CertStore::validate(int ok, X509_STORE_CTX *ctx, const Type &type) +int X509CertStore::validate(int ok, X509_STORE_CTX *ctx) { switch(X509_STORE_CTX_get_error(ctx)) { @@ -172,39 +163,33 @@ int X509CertStore::validate(int ok, X509_STORE_CTX *ctx, const Type &type) default: return ok; } + auto *type = static_cast(X509_STORE_get_ex_data(X509_STORE_CTX_get0_store(ctx), 0)); X509 *x509 = X509_STORE_CTX_get0_cert(ctx); + auto current = util::date::to_string(X509_VERIFY_PARAM_get_time(X509_STORE_CTX_get0_param(ctx))); for(const TSL::Service &s: *instance()->d) { - if(type.find(s.type) == type.cend()) + if(type->find(s.type) == type->cend()) // correct service type continue; if(none_of(s.certs.cbegin(), s.certs.cend(), [&](const X509Cert &issuer){ - if(issuer == x509) + if(issuer == x509) // certificate is listed by service return true; - if(X509_check_issued(issuer.handle(), x509) != X509_V_OK) + if(X509_check_issued(issuer.handle(), x509) != X509_V_OK) // certificate is issued by service return false; - SCOPE(EVP_PKEY, pub, X509_get_pubkey(issuer.handle())); - if(X509_verify(x509, pub.get()) == 1) + auto pub = make_unique_ptr(X509_get_pubkey(issuer.handle()), EVP_PKEY_free); + if(X509_verify(x509, pub.get()) == 1) // certificate is signed by service return true; ERR_clear_error(); return false; - })) + })) // certificate is trusted by service continue; - if(s.validity.empty()) - continue; - X509_STORE_CTX_set_ex_data(ctx, 0, const_cast(&s.validity.begin()->second)); - X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); - if(!(X509_VERIFY_PARAM_get_flags(param) & X509_V_FLAG_USE_CHECK_TIME) || s.validity.empty()) - return 1; - auto current = util::date::to_string(X509_VERIFY_PARAM_get_time(param)); for(auto i = s.validity.crbegin(), end = s.validity.crend(); i != end; ++i) { - if(current >= i->first) - { - if(!i->second.has_value()) - break; - X509_STORE_CTX_set_ex_data(ctx, 0, const_cast(&i->second)); - return 1; - } + if(current < i->first) // Search older status + continue; + if(!i->second.has_value()) // Has revoked + break; + X509_STORE_CTX_set_ex_data(ctx, 0, const_cast(&i->second)); + return 1; } } return ok; @@ -214,14 +199,13 @@ int X509CertStore::validate(int ok, X509_STORE_CTX *ctx, const Type &type) * Check if X509Cert is signed by trusted issuer * @throw Exception if error */ -bool X509CertStore::verify(const X509Cert &cert, bool noqscd) const +bool X509CertStore::verify(const X509Cert &cert, bool noqscd, tm validation_time) const { activate(cert); - tm tm{}; - ASN1_TIME_to_tm(X509_get0_notBefore(cert.handle()), &tm); - time_t time = util::date::mkgmtime(tm); - SCOPE(X509_STORE, store, createStore(X509CertStore::CA, &time)); - SCOPE(X509_STORE_CTX, csc, X509_STORE_CTX_new()); + if(static const tm isSet = {}; memcmp(&isSet, &validation_time, sizeof(isSet)) == 0) + ASN1_TIME_to_tm(X509_get0_notBefore(cert.handle()), &validation_time); + auto store = createStore(X509CertStore::CA, validation_time); + auto csc = make_unique_ptr(X509_STORE_CTX_new(), X509_STORE_CTX_free); if(!X509_STORE_CTX_init(csc.get(), store.get(), cert.handle(), nullptr)) THROW_OPENSSLEXCEPTION("Failed to init X509_STORE_CTX"); if(X509_verify_cert(csc.get()) <= 0) diff --git a/src/crypto/X509CertStore.h b/src/crypto/X509CertStore.h index 8221f5093..61c6e724c 100644 --- a/src/crypto/X509CertStore.h +++ b/src/crypto/X509CertStore.h @@ -21,7 +21,8 @@ #include "../Exports.h" -#include +#include "util/memory.h" + #include #include #include @@ -47,15 +48,15 @@ namespace digidoc std::vector certs(const Type &type) const; X509Cert findIssuer(const X509Cert &cert, const Type &type) const; static X509Cert issuerFromAIA(const X509Cert &cert); - static X509_STORE* createStore(const Type &type, const time_t *t = nullptr); - bool verify(const X509Cert &cert, bool qscd) const; + static unique_free_t createStore(const Type &type, tm &tm); + bool verify(const X509Cert &cert, bool noqscd, tm validation_time = {}) const; private: X509CertStore(); ~X509CertStore(); DISABLE_COPY(X509CertStore); - static int validate(int ok, X509_STORE_CTX *ctx, const Type &type); + static int validate(int ok, X509_STORE_CTX *ctx); class Private; std::unique_ptr d; }; diff --git a/src/util/memory.h b/src/util/memory.h new file mode 100644 index 000000000..39f1a0516 --- /dev/null +++ b/src/util/memory.h @@ -0,0 +1,46 @@ +/* + * libdigidocpp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#pragma once + +#include + +template +using unique_free_t = std::unique_ptr; + +template +[[nodiscard]] +constexpr unique_free_t make_unique_ptr(U *p, void (*d)(T*)) noexcept +{ + return {static_cast(p), d}; +} + +template +[[nodiscard]] +constexpr auto make_unique_ptr(nullptr_t, void (*d)(T*)) noexcept +{ + return make_unique_ptr(nullptr, d); +} + +template +[[nodiscard]] +constexpr std::unique_ptr make_unique_ptr(T *p, D d) noexcept +{ + return {p, std::forward(d)}; +}