Skip to content

Commit

Permalink
Implement LTA support
Browse files Browse the repository at this point in the history
IB-7996

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma committed Jul 30, 2024
1 parent 2a9a96c commit 74c01be
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 128 deletions.
1 change: 1 addition & 0 deletions etc/schema/OpenDocument_dsig.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:dsig="urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0">
<xs:import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="xmldsig-core-schema.xsd"/>
<xs:import namespace="http://uri.etsi.org/01903/v1.3.2#" schemaLocation="XAdES01903v132-201601.xsd"/>
<xsd:import namespace="http://uri.etsi.org/01903/v1.4.1#" schemaLocation="XAdES01903v141-201601.xsd"/>
<xs:element name="document-signatures">
<xs:complexType>
<xs:sequence>
Expand Down
21 changes: 13 additions & 8 deletions src/SignatureXAdES_B.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ const map<string_view,SignatureXAdES_B::Policy> SignatureXAdES_B::policylist{
namespace digidoc
{

constexpr XMLName DigestMethod {"DigestMethod", DSIG_NS};
constexpr XMLName DigestValue {"DigestValue", DSIG_NS};
constexpr XMLName X509IssuerName {"X509IssuerName", DSIG_NS};
constexpr XMLName X509SerialNumber {"X509SerialNumber", DSIG_NS};

thread_local ASiContainer *cb_doc {};
thread_local Exception *cb_exception {};

Expand Down Expand Up @@ -523,8 +528,8 @@ vector<unsigned char> SignatureXAdES_B::dataToSign() const
void SignatureXAdES_B::checkCertID(XMLNode certID, const X509Cert &cert)
{
auto issuerSerial = certID/"IssuerSerial";
string_view certIssuerName = issuerSerial/XMLName{"X509IssuerName", DSIG_NS};
string_view certSerialNumber = issuerSerial/XMLName{"X509SerialNumber", DSIG_NS};
string_view certIssuerName = issuerSerial/X509IssuerName;
string_view certSerialNumber = issuerSerial/X509SerialNumber;
if(X509Crypto(cert).compareIssuerToString(certIssuerName) == 0 && cert.serial() == certSerialNumber)
return checkDigest(certID/"CertDigest", cert);
DEBUG("certIssuerName: \"%.*s\"", int(certIssuerName.size()), certIssuerName.data());
Expand All @@ -535,8 +540,8 @@ void SignatureXAdES_B::checkCertID(XMLNode certID, const X509Cert &cert)

void SignatureXAdES_B::checkDigest(XMLNode digest, const vector<unsigned char> &data)
{
auto calcDigest = Digest((digest/XMLName{"DigestMethod", DSIG_NS}).property("Algorithm")).result(data);
vector<unsigned char> digestValue = digest/XMLName{"DigestValue", DSIG_NS};
auto calcDigest = Digest((digest/DigestMethod).property("Algorithm")).result(data);
vector<unsigned char> digestValue = digest/DigestValue;
if(digestValue == calcDigest)
return;
DEBUGMEM("Document cert digest", digestValue.data(), digestValue.size());
Expand Down Expand Up @@ -655,14 +660,14 @@ void SignatureXAdES_B::setSigningCertificate(string_view name, const X509Cert& x

Digest digest;
auto certDigest = cert + "CertDigest";
(certDigest + XMLName{"DigestMethod", DSIG_NS}).setProperty("Algorithm", digest.uri());
certDigest + XMLName{"DigestValue", DSIG_NS} = digest.result(x509);
(certDigest + DigestMethod).setProperty("Algorithm", digest.uri());
certDigest + DigestValue = digest.result(x509);

if(name == "SigningCertificate")
{
auto issuerSerial = cert + "IssuerSerial";
(issuerSerial + XMLName{"X509IssuerName", DSIG_NS}) = x509.issuerName();
issuerSerial + XMLName{"X509SerialNumber", DSIG_NS} = x509.serial();
(issuerSerial + X509IssuerName) = x509.issuerName();
issuerSerial + X509SerialNumber = x509.serial();
}
}

Expand Down
189 changes: 75 additions & 114 deletions src/SignatureXAdES_LTA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,151 +21,120 @@

#include "ASiC_E.h"
#include "Conf.h"
#include "DataFile_p.h"
#include "crypto/Digest.h"
#include "crypto/TS.h"
#include "crypto/X509Cert.h"
#include "util/DateTime.h"
#include "util/File.h"

#include <algorithm>
#include <array>

using namespace digidoc;
using namespace digidoc::util;
using namespace std;

namespace digidoc
{
constexpr XMLName ArchiveTimeStamp {"ArchiveTimeStamp", XADESv141_NS};
}

void SignatureXAdES_LTA::calcArchiveDigest(Digest *digest,
string_view canonicalizationMethod) const
{
#if 0
try {
XSECProvider prov;
auto deleteSig = [&](DSIGSignature *s) { prov.releaseSignature(s); };
DOMNode *node = signatures->element(id());
unique_ptr<DSIGSignature,decltype(deleteSig)> sig(prov.newSignatureFromDOM(node->getOwnerDocument(), node), deleteSig);
unique_ptr<URIResolver> uriresolver = make_unique<URIResolver>(bdoc);
unique_ptr<XSECKeyInfoResolverDefault> keyresolver = make_unique<XSECKeyInfoResolverDefault>();
sig->setURIResolver(uriresolver.get());
sig->setKeyInfoResolver(keyresolver.get());
sig->registerIdAttributeName((const XMLCh*)u"ID");
sig->setIdByAttributeName(true);
sig->load();

safeBuffer m_errStr;
m_errStr.sbXMLChIn((const XMLCh*)u"");

array<XMLByte, 1024> buf{};
DSIGReferenceList *list = sig->getReferenceList();
for(size_t i = 0; i < list->getSize(); ++i)
{
unique_ptr<XSECBinTXFMInputStream> stream(list->item(i)->makeBinInputStream());
for(XMLSize_t size = stream->readBytes(buf.data(), buf.size()); size > 0;
size = stream->readBytes(buf.data(), buf.size()))
digest->update(buf.data(), size);
}
}
catch(const Parsing &e)
{
stringstream s;
s << e;
THROW("Failed to calculate digest: %s", s.str().c_str());
}
catch(const xsd::cxx::xml::invalid_utf16_string & /* ex */) {
THROW("Failed to calculate digest");
}
catch(const XSECException &e)
for(auto ref = signature/"SignedInfo"/"Reference"; ref; ref++)
{
try {
string result = xsd::cxx::xml::transcode<char>(e.getMsg());
THROW("Failed to calculate digest: %s", result.c_str());
} catch(const xsd::cxx::xml::invalid_utf16_string & /* ex */) {
THROW("Failed to calculate digest");
auto uri = ref.property("URI");
if(ref.property("Type") == REF_TYPE)
{
auto sp = qualifyingProperties()/"SignedProperties";
if(uri.front() != '#' || sp.property("Id") != uri.substr(1))
THROW("Invalid SignedProperties ID");
signatures->c14n(digest, canonicalizationMethod, sp);
continue;
}
}
catch(XMLException &e)
{
try {
string result = xsd::cxx::xml::transcode<char>(e.getMessage());
THROW("Failed to calculate digest: %s", result.c_str());
} catch(const xsd::cxx::xml::invalid_utf16_string & /* ex */) {
THROW("Failed to calculate digest");

string uriPath = File::fromUriPath(uri);
if(uriPath.front() == '/')
uriPath.erase(0);

auto files = bdoc->dataFiles();
auto file = find_if(files.cbegin(), files.cend(), [&uriPath](DataFile *file) {
return file->fileName() == uriPath;
});
if(file == files.cend())
THROW("Filed to find reference URI in container");

std::istream *is = static_cast<const DataFilePrivate*>(*file)->m_is.get();
array<unsigned char, 10240> buf{};
is->clear();
is->seekg(0);
while(*is)
{
is->read((char*)buf.data(), streamsize(buf.size()));
if(is->gcount() > 0)
digest->update(buf.data(), size_t(is->gcount()));
}
}
catch(...)
{
THROW("Failed to calculate digest");
}

for(const auto *name: {u"SignedInfo", u"SignatureValue", u"KeyInfo"})
for(const auto *name: {"SignedInfo", "SignatureValue", "KeyInfo"})
{
try {
calcDigestOnNode(digest, DSIG_NS, name, canonicalizationMethod);
} catch(const Exception &) {
DEBUG("Element %s not found", xsd::cxx::xml::transcode<char>(name).data());
}
if(auto elem = signature/name)
signatures->c14n(digest, canonicalizationMethod, elem);
else
DEBUG("Element %s not found", name);
}

auto usp = unsignedSignatureProperties();
for(const auto *name: {
u"SignatureTimeStamp",
u"CounterSignature",
u"CompleteCertificateRefs",
u"CompleteRevocationRefs",
u"AttributeCertificateRefs",
u"AttributeRevocationRefs",
u"CertificateValues",
u"RevocationValues",
u"SigAndRefsTimeStamp",
u"RefsOnlyTimeStamp" })
"SignatureTimeStamp",
"CounterSignature",
"CompleteCertificateRefs",
"CompleteRevocationRefs",
"AttributeCertificateRefs",
"AttributeRevocationRefs",
"CertificateValues",
"RevocationValues",
"SigAndRefsTimeStamp",
"RefsOnlyTimeStamp" })
{
try {
calcDigestOnNode(digest, Signatures::XADES_NAMESPACE, name, canonicalizationMethod);
} catch(const Exception &) {
DEBUG("Element %s not found", xsd::cxx::xml::transcode<char>(name).data());
}
if(auto elem = usp/name)
signatures->c14n(digest, canonicalizationMethod, elem);
else
DEBUG("Element %s not found", name);
}

try {
calcDigestOnNode(digest, Signatures::XADESv141_NAMESPACE, u"TimeStampValidationData", canonicalizationMethod);
} catch(const Exception &) {
if(auto elem = usp/XMLName{"TimeStampValidationData", XADESv141_NS})
signatures->c14n(digest, canonicalizationMethod, elem);
else
DEBUG("Element TimeStampValidationData not found");
}
//ds:Object
#endif
}

void SignatureXAdES_LTA::extendSignatureProfile(const string &profile)
{
SignatureXAdES_LT::extendSignatureProfile(profile);
if(profile != ASiC_E::ASIC_TSA_PROFILE)
return;
#if 0
Digest calc;
calcArchiveDigest(&calc, signature->signedInfo().canonicalizationMethod().algorithm());
auto method = canonicalizationMethod();
calcArchiveDigest(&calc, method);

TS tsa(CONF(TSUrl), calc);
vector<unsigned char> der = tsa;
auto &usp = unsignedSignatureProperties();
auto ts = make_unique<xadesv141::ArchiveTimeStampType>();
ts->id(id() + "-A0");
ts->canonicalizationMethod(signature->signedInfo().canonicalizationMethod());
ts->encapsulatedTimeStamp().push_back(make_unique<EncapsulatedPKIDataType>(
Base64Binary(der.data(), der.size(), der.size(), false)));
usp.archiveTimeStampV141().push_back(std::move(ts));
usp.contentOrder().emplace_back(UnsignedSignaturePropertiesType::archiveTimeStampV141Id,
usp.archiveTimeStampV141().size() - 1);
signatures->reloadDOM();
#endif
auto usp = unsignedSignatureProperties();
auto ts = usp + ArchiveTimeStamp;
ts.setNS(ts.addNS(XADESv141_NS, "xades141"));
ts.setProperty("Id", id() + "-A0");
(ts + CanonicalizationMethod).setProperty("Algorithm", method);
ts + EncapsulatedTimeStamp = tsa;
}

TS SignatureXAdES_LTA::tsaFromBase64() const
{
#if 0
try {
if(unsignedSignatureProperties().archiveTimeStampV141().empty())
return {};
const xadesv141::ArchiveTimeStampType &ts = unsignedSignatureProperties().archiveTimeStampV141().front();
if(ts.encapsulatedTimeStamp().empty())
return {};
const GenericTimeStampType::EncapsulatedTimeStampType &bin =
ts.encapsulatedTimeStamp().front();
return {(const unsigned char*)bin.data(), bin.size()};
return {unsignedSignatureProperties()/ArchiveTimeStamp/EncapsulatedTimeStamp};
} catch(const Exception &) {}
#endif
return {};
}

Expand Down Expand Up @@ -196,24 +165,16 @@ void SignatureXAdES_LTA::validate(const string &policy) const
return;
}

#if 0
try {
if(unsignedSignatureProperties().archiveTimeStampV141().empty())
auto ts = unsignedSignatureProperties()/ArchiveTimeStamp;
if(!ts)
THROW("Missing ArchiveTimeStamp element");

const xadesv141::ArchiveTimeStampType &ts = unsignedSignatureProperties().archiveTimeStampV141().front();
if(ts.encapsulatedTimeStamp().empty())
THROW("Missing EncapsulatedTimeStamp");

verifyTS(ts, exception, [this](Digest *digest, string_view canonicalizationMethod) {
calcArchiveDigest(digest, canonicalizationMethod);
});
} catch(const Exception &e) {
exception.addCause(e);
}
#else
EXCEPTION_ADD(exception, "LTA validation is not supported");
#endif
if(!exception.causes().empty())
throw exception;
}
6 changes: 3 additions & 3 deletions src/SignatureXAdES_T.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ void SignatureXAdES_T::extendSignatureProfile(const std::string &profile)
TS tsa(CONF(TSUrl), calc);
auto ts = usp + "SignatureTimeStamp";
ts.setProperty("Id", id() + Log::format("-T%zu", i));
(ts + XMLName{"CanonicalizationMethod", DSIG_NS}).setProperty("Algorithm", method);
(ts + CanonicalizationMethod).setProperty("Algorithm", method);
ts + "EncapsulatedTimeStamp" = tsa;
}

Expand Down Expand Up @@ -197,15 +197,15 @@ XMLNode SignatureXAdES_T::unsignedSignatureProperties() const
TS SignatureXAdES_T::verifyTS(XMLNode timestamp, digidoc::Exception &exception,
std::function<void (Digest *, std::string_view)> &&calcDigest)
{
auto ets = timestamp/XMLName{"EncapsulatedTimeStamp", XADES_NS};
auto ets = timestamp/EncapsulatedTimeStamp;
if(!ets)
THROW("Missing EncapsulatedTimeStamp");
if(ets + 1)
THROW("More than one EncapsulatedTimeStamp is not supported");

TS ts(ets);
Digest calc(ts.digestMethod());
calcDigest(&calc, (timestamp/XMLName{"CanonicalizationMethod", DSIG_NS}).property("Algorithm"));
calcDigest(&calc, (timestamp/CanonicalizationMethod).property("Algorithm"));
ts.verify(calc.result());


Expand Down
2 changes: 2 additions & 0 deletions src/SignatureXAdES_T.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ namespace digidoc
{

class TS;
constexpr XMLName EncapsulatedTimeStamp {"EncapsulatedTimeStamp", XADES_NS};
constexpr XMLName CanonicalizationMethod {"CanonicalizationMethod", DSIG_NS};

class SignatureXAdES_T: public SignatureXAdES_B
{
Expand Down
10 changes: 7 additions & 3 deletions src/XMLDocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ struct XMLElem
using sv = std::string_view;
using pcxmlChar = const xmlChar *;

static constexpr sv whitespace {" \n\r\f\t\v"};

template<class C, typename P>
constexpr static auto safe(C c, P p) noexcept
{
Expand Down Expand Up @@ -173,6 +171,7 @@ struct XMLElem

constexpr operator sv() const noexcept
{
constexpr sv whitespace {" \n\r\f\t\v"};
auto *text = children(&value_type::children, XML_TEXT_NODE);
auto result = to_string_view(text, &std::decay_t<decltype(*text)>::content);
result.remove_prefix(std::min<size_t>(result.find_first_not_of(whitespace), result.size()));
Expand Down Expand Up @@ -218,6 +217,11 @@ struct XMLNode: public XMLElem<xmlNode>
return xmlSearchNsByHref(nullptr, d, ns.empty() ? nullptr : pcxmlChar(ns.data()));
}

void setNS(xmlNsPtr ns)
{
xmlSetNs(d, ns);
}

constexpr sv property(sv name, sv ns = {}) const noexcept
{
return find(XMLElem<xmlAttr>{children(&value_type::properties, XML_ATTRIBUTE_NODE)}, name, ns);
Expand Down Expand Up @@ -337,7 +341,7 @@ struct XMLDocument: public unique_xml_t<decltype(xmlFreeDoc)>, public XMLNode
{
doc.d = xmlNewNode(nullptr, pcxmlChar(name.data()));
if(!href.empty())
xmlSetNs(doc.d, doc.addNS(href, prefix));
doc.setNS(doc.addNS(href, prefix));
xmlDocSetRootElement(doc.get(), doc.d);
}
return doc;
Expand Down

0 comments on commit 74c01be

Please sign in to comment.