From 72f6b8d365644de6a7bab8f5dec58121e81e1f20 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Wed, 16 Nov 2022 09:05:57 +0200 Subject: [PATCH] Implement SigAndRefsTimeStamp check IB-7547 Signed-off-by: Raul Metsma --- src/SignatureXAdES_B.cpp | 27 ++++++------ src/SignatureXAdES_B.h | 2 +- src/SignatureXAdES_LT.cpp | 2 +- src/SignatureXAdES_LTA.cpp | 76 ++++++++++++++++----------------- src/SignatureXAdES_LTA.h | 3 +- src/SignatureXAdES_T.cpp | 84 ++++++++++++++++++++++++------------- src/SignatureXAdES_T.h | 7 +++- src/crypto/OCSP.cpp | 2 +- src/crypto/OCSP.h | 3 +- src/xml/SecureDOMParser.cpp | 4 +- src/xml/SecureDOMParser.h | 2 +- 11 files changed, 118 insertions(+), 94 deletions(-) diff --git a/src/SignatureXAdES_B.cpp b/src/SignatureXAdES_B.cpp index 65012532e..2ed124ac4 100644 --- a/src/SignatureXAdES_B.cpp +++ b/src/SignatureXAdES_B.cpp @@ -229,7 +229,7 @@ SignatureXAdES_B::SignatureXAdES_B(unsigned int id, ASiContainer *bdoc, Signer * } Digest calc(digestMethod); - calcDigestOnNode(&calc, XADES_NAMESPACE, "SignedProperties"); + calcDigestOnNode(&calc, XADES_NAMESPACE, u"SignedProperties"); addReference("#" + nr +"-SignedProperties", calc.uri(), calc.result(), "http://uri.etsi.org/01903#SignedProperties"); } @@ -327,7 +327,7 @@ SignatureXAdES_B::SignatureXAdES_B(istream &sigdata, ASiContainer *bdoc, bool re if(!usp->attributeRevocationRefs().empty()) THROW("AttributeRevocationRefs are not supported"); if(!usp->sigAndRefsTimeStamp().empty()) - THROW("SigAndRefsTimeStamp is not supported"); + WARN("SigAndRefsTimeStamp is not supported"); if(!usp->refsOnlyTimeStamp().empty()) THROW("RefsOnlyTimeStamp is not supported"); if(!usp->attrAuthoritiesCertValues().empty()) @@ -649,7 +649,7 @@ vector SignatureXAdES_B::dataToSign() const { // Calculate SHA digest of the Signature->SignedInfo node. Digest calc(signatureMethod()); - calcDigestOnNode(&calc, URI_ID_DSIG, "SignedInfo"); + calcDigestOnNode(&calc, URI_ID_DSIG, u"SignedInfo"); return calc.result(); } @@ -972,7 +972,7 @@ vector SignatureXAdES_B::getSignatureValue() const * @param tagName signature tag name. */ void SignatureXAdES_B::calcDigestOnNode(Digest* calc, const string& ns, - const string& tagName, const string &id, const string &canonicalizationMethod) const + u16string_view tagName, string_view canonicalizationMethod) const { try { @@ -993,21 +993,18 @@ void SignatureXAdES_B::calcDigestOnNode(Digest* calc, const string& ns, DOMNode *node = nullptr; // Select node, on which the digest is calculated. - if(id.empty()) - { - DOMNodeList* nodeList = doc->getElementsByTagNameNS(xml::string(ns).c_str(), xml::string(tagName).c_str()); - if(nodeList->getLength() == 1) - node = nodeList->item(0); - } - else - node = doc->getElementById(xml::string(id).c_str()); + DOMNodeList* nodeList = doc->getElementsByTagNameNS(xml::string(ns).c_str(), tagName.data()); + if(nodeList->getLength() == 1) + node = nodeList->item(0); // Make sure that exactly one node was found. if(!node) - THROW("Could not find '%s' node which is in '%s' namespace in signature XML.", tagName.c_str(), ns.c_str()); + THROW("Could not find '%s' node which is in '%s' namespace in signature XML.", + xml::transcode(tagName.data()).data(), ns.c_str()); - string algorithmType = canonicalizationMethod.empty() ? signature->signedInfo().canonicalizationMethod().algorithm() : canonicalizationMethod; - SecureDOMParser::calcDigestOnNode(calc, algorithmType, doc.get(), node); + if(canonicalizationMethod.empty()) + canonicalizationMethod = signature->signedInfo().canonicalizationMethod().algorithm(); + SecureDOMParser::calcDigestOnNode(calc, canonicalizationMethod, doc.get(), node); } catch(const Exception& e) { diff --git a/src/SignatureXAdES_B.h b/src/SignatureXAdES_B.h index 80fb0421d..a864b66fd 100644 --- a/src/SignatureXAdES_B.h +++ b/src/SignatureXAdES_B.h @@ -73,7 +73,7 @@ namespace digidoc xades::QualifyingPropertiesType& qualifyingProperties() const; xades::SignedSignaturePropertiesType& getSignedSignatureProperties() const; void calcDigestOnNode(Digest* calc, const std::string& ns, - const std::string& tagName, const std::string &id = {}, const std::string &canonicalizationMethod = {}) const; + std::u16string_view tagName, std::string_view canonicalizationMethod = {}) const; void checkCertID(const xades::CertIDType &certID, const X509Cert &cert) const; void checkDigest(const xades::DigestAlgAndValueType &digest, const std::vector &data) const; diff --git a/src/SignatureXAdES_LT.cpp b/src/SignatureXAdES_LT.cpp index 369c5b9d6..f9266fb95 100644 --- a/src/SignatureXAdES_LT.cpp +++ b/src/SignatureXAdES_LT.cpp @@ -271,7 +271,7 @@ void SignatureXAdES_LT::addOCSPValue(const string &id, const OCSP &ocsp) createUnsignedSignatureProperties(); - vector der = ocsp.toDer(); + vector der = ocsp; OCSPValuesType::EncapsulatedOCSPValueType ocspValueData(Base64Binary(der.data(), der.size())); ocspValueData.id(id); diff --git a/src/SignatureXAdES_LTA.cpp b/src/SignatureXAdES_LTA.cpp index 58bf380c7..ab8e1e87e 100644 --- a/src/SignatureXAdES_LTA.cpp +++ b/src/SignatureXAdES_LTA.cpp @@ -55,7 +55,8 @@ using namespace xercesc; using namespace xml_schema; using namespace std; -void SignatureXAdES_LTA::calcArchiveDigest(Digest *digest) const +void SignatureXAdES_LTA::calcArchiveDigest(Digest *digest, + std::string_view canonicalizationMethod) const { try { stringstream ofs; @@ -80,7 +81,8 @@ void SignatureXAdES_LTA::calcArchiveDigest(Digest *digest) const for(size_t i = 0; i < list->getSize(); ++i) { XSECBinTXFMInputStream *stream = list->item(i)->makeBinInputStream(); - for(XMLSize_t size = stream->readBytes(buf, 1024); size > 0; size = stream->readBytes(buf, 1024)) + for(XMLSize_t size = stream->readBytes(buf, 1024); size > 0; + size = stream->readBytes(buf, 1024)) digest->update(buf, size); delete stream; } @@ -117,36 +119,36 @@ void SignatureXAdES_LTA::calcArchiveDigest(Digest *digest) const THROW("Failed to calculate digest"); } - for(const string &name: {"SignedInfo", "SignatureValue", "KeyInfo"}) + for(auto name: {u"SignedInfo", u"SignatureValue", u"KeyInfo"}) { try { - calcDigestOnNode(digest, URI_ID_DSIG, name); + calcDigestOnNode(digest, URI_ID_DSIG, name, canonicalizationMethod); } catch(const Exception &) { - DEBUG("Element %s not found", name.c_str()); + DEBUG("Element %s not found", xsd::cxx::xml::transcode(name).data()); } } - for(const string &name: { - "SignatureTimeStamp", - "CounterSignature", - "CompleteCertificateRefs", - "CompleteRevocationRefs", - "AttributeCertificateRefs", - "AttributeRevocationRefs", - "CertificateValues", - "RevocationValues", - "SigAndRefsTimeStamp", - "RefsOnlyTimeStamp" }) + for(auto name: { + u"SignatureTimeStamp", + u"CounterSignature", + u"CompleteCertificateRefs", + u"CompleteRevocationRefs", + u"AttributeCertificateRefs", + u"AttributeRevocationRefs", + u"CertificateValues", + u"RevocationValues", + u"SigAndRefsTimeStamp", + u"RefsOnlyTimeStamp" }) { try { - calcDigestOnNode(digest, XADES_NAMESPACE, name); + calcDigestOnNode(digest, XADES_NAMESPACE, name, canonicalizationMethod); } catch(const Exception &) { - DEBUG("Element %s not found", name.c_str()); + DEBUG("Element %s not found", xsd::cxx::xml::transcode(name).data()); } } try { - calcDigestOnNode(digest, XADESv141_NAMESPACE, "TimeStampValidationData"); + calcDigestOnNode(digest, XADESv141_NAMESPACE, u"TimeStampValidationData", canonicalizationMethod); } catch(const Exception &) { DEBUG("Element TimeStampValidationData not found"); } @@ -160,17 +162,19 @@ void SignatureXAdES_LTA::extendSignatureProfile(const std::string &profile) return; Digest calc; - calcArchiveDigest(&calc); + calcArchiveDigest(&calc, signature->signedInfo().canonicalizationMethod().algorithm()); TS tsa(CONF(TSUrl), calc, " Profile: " + profile); vector der = tsa; - xadesv141::ArchiveTimeStampType ts; - ts.id(id() + "-A0"); - ts.encapsulatedTimeStamp().push_back(EncapsulatedPKIDataType(Base64Binary(der.data(), der.size()))); - unsignedSignatureProperties().archiveTimeStampV141().push_back(ts); - unsignedSignatureProperties().contentOrder().push_back( - UnsignedSignaturePropertiesType::ContentOrderType( - UnsignedSignaturePropertiesType::archiveTimeStampV141Id, - unsignedSignatureProperties().archiveTimeStampV141().size() - 1)); + auto &usp = unsignedSignatureProperties(); + auto ts = make_unique(); + ts->id(id() + "-A0"); + ts->canonicalizationMethod(signature->signedInfo().canonicalizationMethod()); + ts->encapsulatedTimeStamp().push_back(make_unique( + Base64Binary(der.data(), der.size(), der.size(), false))); + usp.archiveTimeStampV141().push_back(move(ts)); + usp.contentOrder().push_back(UnsignedSignaturePropertiesType::ContentOrderType( + UnsignedSignaturePropertiesType::archiveTimeStampV141Id, + usp.archiveTimeStampV141().size() - 1)); sigdata_.clear(); } @@ -224,19 +228,9 @@ void SignatureXAdES_LTA::validate(const string &policy) const if(ts.encapsulatedTimeStamp().empty()) THROW("Missing EncapsulatedTimeStamp"); - const GenericTimeStampType::EncapsulatedTimeStampType &bin = ts.encapsulatedTimeStamp().front(); - TS tsa((const unsigned char*)bin.data(), bin.size()); - Digest calc(tsa.digestMethod()); - calcArchiveDigest(&calc); - tsa.verify(calc); - - if(tsa.digestMethod() == URI_SHA1 && - !Exception::hasWarningIgnore(Exception::ReferenceDigestWeak)) - { - Exception e(EXCEPTION_PARAMS("TimeStamp '%s' digest weak", tsa.digestMethod().c_str())); - e.setCode(Exception::ReferenceDigestWeak); - exception.addCause(e); - } + verifyTS(ts, exception, [this](Digest *digest, std::string_view canonicalizationMethod) { + calcArchiveDigest(digest, canonicalizationMethod); + }); } catch(const Exception &e) { exception.addCause(e); } diff --git a/src/SignatureXAdES_LTA.h b/src/SignatureXAdES_LTA.h index e9d5fc26c..9972a39da 100644 --- a/src/SignatureXAdES_LTA.h +++ b/src/SignatureXAdES_LTA.h @@ -37,7 +37,8 @@ class SignatureXAdES_LTA final: public SignatureXAdES_LT private: DISABLE_COPY(SignatureXAdES_LTA); - void calcArchiveDigest(Digest *digest) const; + void calcArchiveDigest(Digest *digest, + std::string_view canonicalizationMethod) const; TS tsaFromBase64() const; }; diff --git a/src/SignatureXAdES_T.cpp b/src/SignatureXAdES_T.cpp index 9f520df9b..bbe452284 100644 --- a/src/SignatureXAdES_T.cpp +++ b/src/SignatureXAdES_T.cpp @@ -77,7 +77,8 @@ void SignatureXAdES_T::extendSignatureProfile(const std::string &profile) createUnsignedSignatureProperties(); Digest calc; - calcDigestOnNode(&calc, URI_ID_DSIG, "SignatureValue"); + calcDigestOnNode(&calc, URI_ID_DSIG, u"SignatureValue", + signature->signedInfo().canonicalizationMethod().algorithm()); TS tsa(CONF(TSUrl), calc, " Profile: " + profile); vector der = tsa; @@ -90,7 +91,7 @@ void SignatureXAdES_T::extendSignatureProfile(const std::string &profile) usp.signatureTimeStamp().push_back(move(ts)); usp.contentOrder().emplace_back(UnsignedSignaturePropertiesType::ContentOrderType( UnsignedSignaturePropertiesType::signatureTimeStampId, - unsignedSignatureProperties().signatureTimeStamp().size() - 1)); + usp.signatureTimeStamp().size() - 1)); sigdata_.clear(); } @@ -125,48 +126,33 @@ void SignatureXAdES_T::validate(const std::string &policy) const } try { + const auto &usp = unsignedSignatureProperties(); const UnsignedSignaturePropertiesType::SignatureTimeStampSequence &tseq = - unsignedSignatureProperties().signatureTimeStamp(); + usp.signatureTimeStamp(); if(tseq.empty()) THROW("Missing SignatureTimeStamp"); if(tseq.size() > 1) THROW("More than one SignatureTimeStamp is not supported"); const UnsignedSignaturePropertiesType::SignatureTimeStampType &ts = tseq.front(); - const GenericTimeStampType::EncapsulatedTimeStampSequence &etseq = - ts.encapsulatedTimeStamp(); - if(etseq.empty()) + if(ts.encapsulatedTimeStamp().empty()) THROW("Missing EncapsulatedTimeStamp"); - if(etseq.size() > 1) + if(ts.encapsulatedTimeStamp().size() > 1) THROW("More than one EncapsulatedTimeStamp is not supported"); - - string canonicalizationMethod; - if(ts.canonicalizationMethod()) - canonicalizationMethod = ts.canonicalizationMethod()->algorithm(); - const GenericTimeStampType::EncapsulatedTimeStampType &bin = etseq.front(); - TS tsa((const unsigned char*)bin.data(), bin.size()); - Digest calc(tsa.digestMethod()); - calcDigestOnNode(&calc, URI_ID_DSIG, "SignatureValue", {}, canonicalizationMethod); - tsa.verify(calc); + TS tsa = verifyTS(ts, exception, [this](Digest *digest, std::string_view canonicalizationMethod) { + calcDigestOnNode(digest, URI_ID_DSIG, u"SignatureValue", canonicalizationMethod); + }); time_t validateTime = util::date::ASN1TimeToTime_t(tsa.time()); if(!signingCertificate().isValid(&validateTime)) THROW("Signing certificate was not valid on signing time"); - if(tsa.digestMethod() == URI_SHA1 && - !Exception::hasWarningIgnore(Exception::ReferenceDigestWeak)) - { - Exception e(EXCEPTION_PARAMS("TimeStamp '%s' digest weak", tsa.digestMethod().c_str())); - e.setCode(Exception::ReferenceDigestWeak); - exception.addCause(e); - } - - const auto &completeCertRefs = unsignedSignatureProperties().completeCertificateRefs(); + const auto &completeCertRefs = usp.completeCertificateRefs(); if(completeCertRefs.size() > 1) THROW("UnsignedSignatureProperties may contain only one CompleteCertificateRefs element"); if(completeCertRefs.size() == 1) { - const auto &certValues = unsignedSignatureProperties().certificateValues(); + const auto &certValues = usp.certificateValues(); if(certValues.size() != 1) THROW("UnsignedSignatureProperties may contain only one CertificateValues element"); const auto &certValue = certValues.front(); @@ -180,7 +166,7 @@ void SignatureXAdES_T::validate(const std::string &policy) const } } - const auto &completeRevRefs = unsignedSignatureProperties().completeRevocationRefs(); + const auto &completeRevRefs = usp.completeRevocationRefs(); if(completeRevRefs.size() > 1) THROW("UnsignedSignatureProperties may contain only one CompleteRevocationRefs element"); if(completeRevRefs.size() == 1) @@ -190,7 +176,7 @@ void SignatureXAdES_T::validate(const std::string &policy) const const auto &ocspRefs = completeRevRefs.front().oCSPRefs(); if(!ocspRefs) THROW("CompleteRevocationRefs is missing OCSPRefs element"); - const auto &revValues = unsignedSignatureProperties().revocationValues(); + const auto &revValues = usp.revocationValues(); if(revValues.size() != 1) THROW("UnsignedSignatureProperties may contain only one RevocationValues element"); const auto &ocspValues = revValues.front().oCSPValues(); @@ -203,9 +189,29 @@ void SignatureXAdES_T::validate(const std::string &policy) const const auto &base64 = ocspValues->encapsulatedOCSPValue().at(i); const auto &ocspRef = ocspRefs->oCSPRef().at(i); OCSP ocsp((const unsigned char*)base64.data(), base64.size()); - checkDigest(ocspRef.digestAlgAndValue().get(), ocsp.toDer()); + checkDigest(ocspRef.digestAlgAndValue().get(), ocsp); } } + + for(const auto &sigAndRefsTS: usp.sigAndRefsTimeStamp()) + { + verifyTS(sigAndRefsTS, exception, [this](Digest *digest, std::string_view canonicalizationMethod) { + calcDigestOnNode(digest, URI_ID_DSIG, u"SignatureValue", canonicalizationMethod); + for(auto name: { + u"SignatureTimeStamp", + u"CompleteCertificateRefs", + u"CompleteRevocationRefs", + u"AttributeCertificateRefs", + u"AttributeRevocationRefs" }) + { + try { + calcDigestOnNode(digest, XADES_NAMESPACE, name, canonicalizationMethod); + } catch(const Exception &) { + DEBUG("Element %s not found", xsd::cxx::xml::transcode(name).data()); + } + } + }); + } } catch(const Exception &e) { exception.addCause(e); } @@ -221,3 +227,23 @@ UnsignedSignaturePropertiesType &SignatureXAdES_T::unsignedSignatureProperties() THROW("UnsignedProperties block 'UnsignedSignatureProperties' is missing."); return qualifyingProperties().unsignedProperties()->unsignedSignatureProperties().get(); } + +TS SignatureXAdES_T::verifyTS(const xades::XAdESTimeStampType ×tamp, digidoc::Exception &exception, + std::function &&calcDigest) const +{ + const GenericTimeStampType::EncapsulatedTimeStampType &bin = timestamp.encapsulatedTimeStamp().front(); + TS tsa((const unsigned char*)bin.data(), bin.size()); + Digest calc(tsa.digestMethod()); + calcDigest(&calc, timestamp.canonicalizationMethod() ? + string_view(timestamp.canonicalizationMethod()->algorithm()) : string_view()); + tsa.verify(calc); + + if(tsa.digestMethod() == URI_SHA1 && + !Exception::hasWarningIgnore(Exception::ReferenceDigestWeak)) + { + Exception e(EXCEPTION_PARAMS("TimeStamp '%s' digest weak", tsa.digestMethod().c_str())); + e.setCode(Exception::ReferenceDigestWeak); + exception.addCause(e); + } + return tsa; +} diff --git a/src/SignatureXAdES_T.h b/src/SignatureXAdES_T.h index 475f55942..6378956ee 100644 --- a/src/SignatureXAdES_T.h +++ b/src/SignatureXAdES_T.h @@ -21,10 +21,12 @@ #include "SignatureXAdES_B.h" +#include + namespace digidoc { -namespace xades { class UnsignedSignaturePropertiesType; } +namespace xades { class UnsignedSignaturePropertiesType; class XAdESTimeStampType; } class TS; class SignatureXAdES_T: public SignatureXAdES_B @@ -44,6 +46,9 @@ class SignatureXAdES_T: public SignatureXAdES_B void createUnsignedSignatureProperties(); xades::UnsignedSignaturePropertiesType& unsignedSignatureProperties() const; + TS verifyTS(const xades::XAdESTimeStampType ×tamp, Exception &exception, + std::function &&calcDigest) const; + private: DISABLE_COPY(SignatureXAdES_T); diff --git a/src/crypto/OCSP.cpp b/src/crypto/OCSP.cpp index 348c93550..507ca4825 100644 --- a/src/crypto/OCSP.cpp +++ b/src/crypto/OCSP.cpp @@ -259,7 +259,7 @@ X509Cert OCSP::responderCert() const return X509Cert(); } -vector OCSP::toDer() const +OCSP::operator std::vector() const { return i2d(resp.get(), i2d_OCSP_RESPONSE); } diff --git a/src/crypto/OCSP.h b/src/crypto/OCSP.h index 992252827..f22a2b949 100644 --- a/src/crypto/OCSP.h +++ b/src/crypto/OCSP.h @@ -46,9 +46,10 @@ namespace digidoc std::vector nonce() const; std::string producedAt() const; X509Cert responderCert() const; - std::vector toDer() const; void verifyResponse(const X509Cert &cert) const; + operator std::vector() const; + private: bool compareResponderCert(const X509Cert &cert) const; OCSP_REQUEST* createRequest(OCSP_CERTID *certId, const std::vector &nonce, bool signRequest); diff --git a/src/xml/SecureDOMParser.cpp b/src/xml/SecureDOMParser.cpp index 6b00cfb05..42c06e024 100644 --- a/src/xml/SecureDOMParser.cpp +++ b/src/xml/SecureDOMParser.cpp @@ -86,7 +86,7 @@ SecureDOMParser::SecureDOMParser(const string &schema_location, bool dont_valida } void SecureDOMParser::calcDigestOnNode(Digest *calc, - const string &algorithmType, DOMDocument *doc, DOMNode *node) + string_view algorithmType, DOMDocument *doc, DOMNode *node) { XSECC14n20010315 c14n(doc, node); c14n.setCommentsProcessing(false); @@ -110,7 +110,7 @@ void SecureDOMParser::calcDigestOnNode(Digest *calc, c14n.setInclusive11(); c14n.setCommentsProcessing(true); } else { - THROW("Unsupported canonicalization method '%s'", algorithmType.c_str()); + THROW("Unsupported canonicalization method '%s'", algorithmType.data()); } unsigned char buffer[1024]; diff --git a/src/xml/SecureDOMParser.h b/src/xml/SecureDOMParser.h index 197a5910a..6d697cfca 100644 --- a/src/xml/SecureDOMParser.h +++ b/src/xml/SecureDOMParser.h @@ -33,7 +33,7 @@ class SecureDOMParser: public xercesc::DOMLSParserImpl SecureDOMParser(const std::string &schema_location = {}); SecureDOMParser(const std::string &schema_location, bool dont_validate); - static void calcDigestOnNode(Digest *calc, const std::string &algorithmType, + static void calcDigestOnNode(Digest *calc, std::string_view algorithmType, xercesc::DOMDocument *doc, xercesc::DOMNode *node); void doctypeDecl(const xercesc::DTDElementDecl& root,