Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate signing certificate on issue date and on TSA time #644

Merged
merged 1 commit into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/Signer.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
23 changes: 3 additions & 20 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 @@ -32,30 +33,12 @@

#include <openssl/evp.h>

#include <memory>
#include <istream>

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 @@ -300,7 +283,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 @@ -312,7 +295,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
5 changes: 3 additions & 2 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 @@ -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;
};

}
45 changes: 26 additions & 19 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, const std::string &userAgent)
: 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 @@ -56,7 +58,7 @@ OCSP::OCSP(const X509Cert &cert, const X509Cert &issuer, const std::string &user
if(url.empty())
{
STACK_OF(OPENSSL_STRING) *urls = X509_get1_ocsp(cert.handle());
if(sk_OPENSSL_STRING_num(urls) > 0)
if(urls && sk_OPENSSL_STRING_num(urls) > 0)
url = sk_OPENSSL_STRING_value(urls, 0);
X509_email_free(urls);
}
Expand Down Expand Up @@ -90,8 +92,8 @@ OCSP::OCSP(const X509Cert &cert, const X509Cert &issuer, const std::string &user
THROW("OCSP service responded - Forbidden");
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);
const auto *p2 = (const unsigned char*)result.content.c_str();
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, const std::string &user
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, const std::string &user
}

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,19 @@ 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();
// Some OCSP-s do not have certificates in response and stack is used for finding certificate for this
auto stack = make_unique_ptr(sk_X509_new_null(), [](auto *sk) { sk_X509_free(sk); });
for(const X509Cert &i: X509CertStore::instance()->certs(X509CertStore::OCSP))
{
if(compareResponderCert(i))
sk_X509_push(stack, i.handle());
sk_X509_push(stack.get(), 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(), stack.get(), store.get(), OCSP_NOCHECKS) != 1)
#else
if(OCSP_basic_verify(basic.get(), stack.get(), 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 @@ -259,17 +265,18 @@ void OCSP::verifyResponse(const X509Cert &cert) const
*/
vector<unsigned char> OCSP::nonce() const
{
vector<unsigned char> nonce;
if(!basic)
return {};
return nonce;
int resp_idx = OCSP_BASICRESP_get_ext_by_NID(basic.get(), NID_id_pkix_OCSP_Nonce, -1);
if(resp_idx < 0)
return {};
return nonce;
X509_EXTENSION *ext = OCSP_BASICRESP_get_ext(basic.get(), resp_idx);
if(!ext)
return {};
return nonce;

ASN1_OCTET_STRING *value = X509_EXTENSION_get_data(ext);
vector<unsigned char> nonce(value->data, value->data + value->length);
nonce.assign(value->data, std::next(value->data, value->length));
//OpenSSL OCSP created messages NID_id_pkix_OCSP_Nonce field is DER encoded twice, not a problem with java impl
//XXX: UglyHackTM check if nonceAsn1 contains ASN1_OCTET_STRING
//XXX: if first 2 bytes seem to be beginning of DER ASN1_OCTET_STRING then remove them
Expand All @@ -281,9 +288,9 @@ vector<unsigned char> OCSP::nonce() const

tm OCSP::producedAt() const
{
if(!basic)
return {};
tm tm {};
if(!basic)
return tm;
ASN1_TIME_to_tm(OCSP_resp_get0_produced_at(basic.get()), &tm);
return tm;
}
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 <string>
#include <vector>

Expand Down Expand Up @@ -51,7 +52,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;
};
}
15 changes: 7 additions & 8 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,22 +32,23 @@
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)

template<class T, typename Func>
[[nodiscard]]
inline std::vector<unsigned char> i2d(T *obj, Func func)
{
std::vector<unsigned char> result;
if(!obj)
return {};
return result;
int size = func(obj, nullptr);
if(size <= 0)
return {};
std::vector<unsigned char> result(size_t(size), 0);
return result;
result.resize(size_t(size), 0);
if(unsigned char *p = result.data(); func(obj, &p) != size)
return {};
result.clear();
return result;
}

Expand Down
Loading
Loading