Skip to content

Commit

Permalink
Validate signing certificate on issue date and on TSA time
Browse files Browse the repository at this point in the history
IB-8296

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma committed Nov 14, 2024
1 parent 82729b4 commit fc0d4a7
Show file tree
Hide file tree
Showing 14 changed files with 162 additions and 147 deletions.
1 change: 1 addition & 0 deletions src/SignatureTST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
5 changes: 2 additions & 3 deletions src/SignatureXAdES_T.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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";
Expand Down
22 changes: 3 additions & 19 deletions src/XMLDocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "crypto/Digest.h"
#include "util/log.h"
#include "util/memory.h"

#include <libxml/parser.h>
#include <libxml/xmlschemas.h>
Expand All @@ -40,23 +41,6 @@ namespace digidoc {

#define VERSION_CHECK(major, minor, patch) (((major)<<16)|((minor)<<8)|(patch))

template<typename> struct unique_xml;
template<class T>
struct unique_xml<void(T *)>
{
using type = std::unique_ptr<T,void(*)(T *)>;
};

template<typename T>
using unique_xml_t = typename unique_xml<T>::type;

template<class T, typename D>
[[nodiscard]]
constexpr std::unique_ptr<T, D> make_unique_ptr(T *p, D d) noexcept
{
return {p, std::forward<D>(d)};
}

static std::vector<unsigned char> from_base64(std::string_view data)
{
static constexpr std::string_view whitespace {" \n\r\f\t\v"};
Expand Down Expand Up @@ -301,7 +285,7 @@ struct XMLNode: public XMLElem<xmlNode>
}
};

struct XMLDocument: public unique_xml_t<decltype(xmlFreeDoc)>, public XMLNode
struct XMLDocument: public unique_free_t<xmlDoc>, 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"};
Expand All @@ -313,7 +297,7 @@ struct XMLDocument: public unique_xml_t<decltype(xmlFreeDoc)>, public XMLNode
using XMLNode::operator bool;

XMLDocument(element_type *ptr = {}, const XMLName &n = {}) noexcept
: std::unique_ptr<element_type, deleter_type>(ptr, xmlFreeDoc)
: unique_free_t<xmlDoc>(ptr, xmlFreeDoc)
, XMLNode{xmlDocGetRootElement(get())}
{
if(d && !n.name.empty() && n.name != name() && !n.ns.empty() && n.ns != ns())
Expand Down
17 changes: 9 additions & 8 deletions src/crypto/Connect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <zlib.h>

#include <algorithm>
#include <sstream>
#include <thread>

#ifdef _WIN32
Expand Down Expand Up @@ -147,13 +148,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<X509Cert>*)data;
return any_of(certs->cbegin(), certs->cend(), [x509](const X509Cert &cert) {
return cert && cert == x509;
}) ? 1 : 0;
}, const_cast<vector<X509Cert>*>(&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)
Expand Down Expand Up @@ -292,7 +293,7 @@ Connect::Result Connect::exec(initializer_list<pair<string_view,string_view>> 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))
Expand Down
9 changes: 5 additions & 4 deletions src/crypto/Digest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <openssl/x509.h>

#include <array>
#include <istream>

using namespace std;
using namespace digidoc;
Expand All @@ -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());
Expand Down Expand Up @@ -68,7 +69,7 @@ vector<unsigned char> Digest::addDigestInfo(vector<unsigned char> digest, string
vector<unsigned char> Digest::digestInfoDigest(const std::vector<unsigned char> &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 {};
Expand All @@ -79,7 +80,7 @@ vector<unsigned char> Digest::digestInfoDigest(const std::vector<unsigned char>
string Digest::digestInfoUri(const std::vector<unsigned char> &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 {};
Expand Down Expand Up @@ -269,7 +270,7 @@ vector<unsigned char> Digest::result() const
return result;
}

vector<unsigned char> Digest::result(const vector<unsigned char> &data)
vector<unsigned char> Digest::result(const vector<unsigned char> &data) const
{
update(data.data(), data.size());
return result();
Expand Down
7 changes: 4 additions & 3 deletions src/crypto/Digest.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@

#include "../Exports.h"

#include <memory>
#include "util/memory.h"

#include <string>
#include <vector>

Expand Down Expand Up @@ -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<unsigned char> result(const std::vector<unsigned char> &data);
std::vector<unsigned char> result(const std::vector<unsigned char> &data) const;
std::vector<unsigned char> result() const;
std::string uri() const;

Expand All @@ -89,7 +90,7 @@ namespace digidoc
static std::string digestInfoUri(const std::vector<unsigned char> &digest);

private:
std::unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX*)> d;
unique_free_t<EVP_MD_CTX> d;
};

}
33 changes: 16 additions & 17 deletions src/crypto/OCSP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand All @@ -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?");

Expand All @@ -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()))
{
Expand All @@ -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.");

Expand All @@ -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
Expand Down Expand Up @@ -187,17 +191,12 @@ 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 OPENSSL_VERSION_NUMBER < 0x30000000L
if(OCSP_basic_verify(basic.get(), nullptr, store.get(), OCSP_NOCHECKS) != 1)
#else
if(OCSP_basic_verify(basic.get(), nullptr, store.get(), OCSP_NOCHECKS | OCSP_PARTIAL_CHAIN) != 1)
#endif
{
unsigned long err = ERR_get_error();
if(ERR_GET_LIB(err) == ERR_LIB_OCSP &&
Expand Down Expand Up @@ -228,7 +227,7 @@ void OCSP::verifyResponse(const X509Cert &cert) const
ASN1_OBJECT *md {};
if(OCSP_id_get0_info(nullptr, &md, nullptr, nullptr, const_cast<OCSP_CERTID*>(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;
}
Expand Down
7 changes: 4 additions & 3 deletions src/crypto/OCSP.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

#pragma once

#include <memory>
#include "util/memory.h"

#include <vector>

using OCSP_RESPONSE = struct ocsp_response_st;
Expand Down Expand Up @@ -50,7 +51,7 @@ namespace digidoc
private:
bool compareResponderCert(const X509Cert &cert) const;

std::shared_ptr<OCSP_RESPONSE> resp;
std::shared_ptr<OCSP_BASICRESP> basic;
unique_free_t<OCSP_RESPONSE> resp;
unique_free_t<OCSP_BASICRESP> basic;
};
}
6 changes: 2 additions & 4 deletions src/crypto/OpenSSLHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@

#include "Exception.h"
#include "util/log.h"

#include <memory>
#include <sstream>
#include "util/memory.h"

#include <openssl/err.h>

Expand All @@ -34,7 +32,7 @@
namespace digidoc
{

#define SCOPE_PTR_FREE(TYPE, DATA, FREE) std::unique_ptr<TYPE,decltype(&FREE)>(static_cast<TYPE*>(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)

Expand Down
Loading

0 comments on commit fc0d4a7

Please sign in to comment.