From c8210363fba25707c73459a2a19208fa4fb6921c Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Tue, 10 Sep 2024 11:36:55 +0300 Subject: [PATCH] Validate ASiC-S manifest containers IB-8180 Signed-off-by: Raul Metsma --- src/ASiC_S.cpp | 50 +++++++++++++++++++++++++++++++++++--- src/ASiC_S.h | 7 ++++++ src/SignatureTST.cpp | 32 ++++++++++++++++++++++-- src/SignatureTST.h | 5 +++- src/SignatureXAdES_LTA.cpp | 3 +-- src/SignatureXAdES_T.cpp | 3 +-- src/crypto/TS.cpp | 4 +-- src/crypto/TS.h | 2 +- 8 files changed, 93 insertions(+), 13 deletions(-) diff --git a/src/ASiC_S.cpp b/src/ASiC_S.cpp index 3485b0e71..cf5624c95 100644 --- a/src/ASiC_S.cpp +++ b/src/ASiC_S.cpp @@ -19,6 +19,7 @@ #include "ASiC_S.h" +#include "Conf.h" #include "SignatureTST.h" #include "SignatureXAdES_LTA.h" #include "crypto/Signer.h" @@ -32,6 +33,18 @@ using namespace digidoc; using namespace digidoc::util; using namespace std; +struct ASiC_S::Data { + std::string name, mime, data; + + Digest digest(Digest digest = {}) const + { + digest.update((const unsigned char*)data.data(), data.size()); + return digest; + } +}; + + + /** * Initialize ASiCS container. */ @@ -58,7 +71,9 @@ ASiC_S::ASiC_S(const string &path) { if(!signatures().empty()) THROW("Can not add signature to ASiC-S container which already contains a signature."); - addSignature(make_unique(z.stringStream(file).str(), this)); + string tst = z.stringStream(file).str(); + addSignature(make_unique(tst, this)); + metadata.push_back({file, "application/vnd.etsi.timestamp-token", std::move(tst)}); } else if(file == "META-INF/signatures.xml") { @@ -70,7 +85,27 @@ ASiC_S::ASiC_S(const string &path) addSignature(make_unique(signatures, s, this)); } else if(file == "META-INF/ASiCArchiveManifest.xml") - THROW("ASiCArchiveManifest are not supported."); + { + function add = [this, &add, &z](const string &file, string_view mime) { + stringstream xml = z.stringStream(file); + XMLDocument doc = XMLDocument::openStream(xml, {"ASiCManifest", ASIC_NS}); + doc.validateSchema(util::File::path(Conf::instance()->xsdPath(), "en_31916201v010101.xsd")); + + for(auto ref = doc/"DataObjectReference"; ref; ref++) + { + if(ref["Rootfile"] == "true") + add(util::File::fromUriPath(ref["URI"]), ref["MimeType"]); + } + + auto ref = doc/"SigReference"; + string uri = util::File::fromUriPath(ref["URI"]); + string tst = z.stringStream(uri).str(); + addSignature(make_unique(file, ::move(doc), tst, this)); + metadata.push_back({file, string(mime), xml.str()}); + metadata.push_back({uri, string(ref["MimeType"]), std::move(tst)}); + }; + add(file, "text/xml"); + } else if(starts_with(file, "META-INF/")) continue; else if(const auto directory = File::directory(file); @@ -110,6 +145,14 @@ void ASiC_S::addAdESSignature(istream & /*signature*/) THROW("Not implemented."); } +Digest ASiC_S::fileDigest(const string &file, string_view method) const +{ + if(auto i = find_if(metadata.cbegin(), metadata.cend(), [&file](const auto &d) { return d.name == file; }); + i != metadata.cend()) + return i->digest(method); + THROW("File not found %s.", file.c_str()); +} + unique_ptr ASiC_S::openInternal(const string &path, ContainerOpenCB * /*cb*/) { if (!isContainerSimpleFormat(path)) @@ -131,7 +174,8 @@ void ASiC_S::save(const ZipSerialize &s) auto *tst = dynamic_cast(list.front()); if(tst->profile() != ASIC_TST_PROFILE) THROW("ASiC-S container supports only TimeStampToken signing."); - s.addFile("META-INF/timestamp.tst", zproperty("META-INF/timestamp.tst"))(tst->save()); + for(const auto &[name, mime, data]: metadata) + s.addFile(name, zproperty(name))(data); } Signature *ASiC_S::sign(Signer *signer) diff --git a/src/ASiC_S.h b/src/ASiC_S.h index 2f8512f22..71d5bda54 100644 --- a/src/ASiC_S.h +++ b/src/ASiC_S.h @@ -23,6 +23,8 @@ namespace digidoc { + class Digest; + /** * Implements the ASiC-S specification of the timestamped digital document container. * Container contains a single datafile object and one time assertion file. @@ -37,6 +39,8 @@ namespace digidoc Signature* prepareSignature(Signer *signer) override; Signature* sign(Signer* signer) override; + Digest fileDigest(const std::string &file, std::string_view method = {}) const; + static std::unique_ptr createInternal(const std::string &path); static std::unique_ptr openInternal(const std::string &path, ContainerOpenCB *cb); @@ -49,5 +53,8 @@ namespace digidoc void save(const ZipSerialize &s) final; static bool isContainerSimpleFormat(const std::string &path); + + struct Data; + std::vector metadata; }; } diff --git a/src/SignatureTST.cpp b/src/SignatureTST.cpp index 9d4b62b76..abbad0c63 100644 --- a/src/SignatureTST.cpp +++ b/src/SignatureTST.cpp @@ -20,28 +20,40 @@ #include "SignatureTST.h" #include "ASiC_S.h" -#include "Conf.h" #include "DataFile_p.h" #include "crypto/TS.h" #include "crypto/X509Cert.h" #include "util/DateTime.h" +#include "util/File.h" #include "util/log.h" using namespace digidoc; using namespace std; +constexpr std::string_view DSIG_NS {"http://www.w3.org/2000/09/xmldsig#"}; +constexpr XMLName DigestMethod {"DigestMethod", DSIG_NS}; +constexpr XMLName DigestValue {"DigestValue", DSIG_NS}; + SignatureTST::SignatureTST(const string &data, ASiC_S *asicSDoc) : asicSDoc(asicSDoc) , timestampToken(make_unique((const unsigned char*)data.data(), data.size())) {} + +SignatureTST::SignatureTST(string current, XMLDocument &&xml, const string &data, ASiC_S *asicSDoc) + : SignatureTST(data, asicSDoc) +{ + file = std::move(current); + doc = std::move(xml); +} + SignatureTST::SignatureTST(ASiC_S *asicSDoc) : asicSDoc(asicSDoc) { auto *dataFile = dynamic_cast(asicSDoc->dataFiles().front()); Digest digest; dataFile->digest(digest); - timestampToken = make_unique(CONF(TSUrl), digest); + timestampToken = make_unique(digest); } SignatureTST::~SignatureTST() = default; @@ -102,6 +114,20 @@ void SignatureTST::validate() const e.setCode(Exception::ReferenceDigestWeak); exception.addCause(e); } + if(doc) + { + DataFile *file = asicSDoc->dataFiles().front(); + for(auto ref = doc/"DataObjectReference"; ref; ref++) + { + string_view method = (ref/DigestMethod)["Algorithm"]; + auto uri = util::File::fromUriPath(ref["URI"]); + vector digest = file->fileName() == uri ? + dynamic_cast(file)->calcDigest(string(method)) : + asicSDoc->fileDigest(uri, method).result(); + if(vector digestValue = ref/DigestValue; digest != digestValue) + THROW("Reference %s digest does not match", uri.c_str()); + } + } } catch (const Exception& e) { @@ -114,6 +140,8 @@ void SignatureTST::validate() const std::vector SignatureTST::dataToSign() const { + if(!file.empty()) + return asicSDoc->fileDigest(file, signatureMethod()).result(); return asicSDoc->dataFiles().front()->calcDigest(signatureMethod()); } diff --git a/src/SignatureTST.h b/src/SignatureTST.h index a375d18d1..b25a948fb 100644 --- a/src/SignatureTST.h +++ b/src/SignatureTST.h @@ -21,7 +21,7 @@ #include "Signature.h" -#include +#include "XMLDocument.h" namespace digidoc { @@ -32,6 +32,7 @@ class SignatureTST final: public Signature { public: SignatureTST(const std::string &data, ASiC_S *asicSDoc); + SignatureTST(std::string current, XMLDocument &&xml, const std::string &data, ASiC_S *asicSDoc); SignatureTST(ASiC_S *asicSDoc); ~SignatureTST(); @@ -58,6 +59,8 @@ class SignatureTST final: public Signature private: DISABLE_COPY(SignatureTST); ASiC_S *asicSDoc {}; + std::string file; + XMLDocument doc; std::unique_ptr timestampToken; }; diff --git a/src/SignatureXAdES_LTA.cpp b/src/SignatureXAdES_LTA.cpp index d1e07c63d..068d55f80 100644 --- a/src/SignatureXAdES_LTA.cpp +++ b/src/SignatureXAdES_LTA.cpp @@ -20,7 +20,6 @@ #include "SignatureXAdES_LTA.h" #include "ASiC_E.h" -#include "Conf.h" #include "DataFile_p.h" #include "crypto/Digest.h" #include "crypto/TS.h" @@ -111,7 +110,7 @@ void SignatureXAdES_LTA::extendSignatureProfile(const string &profile) auto method = canonicalizationMethod(); calcArchiveDigest(&calc, method); - TS tsa(CONF(TSUrl), calc); + TS tsa(calc); auto ts = unsignedSignatureProperties() + ArchiveTimeStamp; ts.setNS(ts.addNS(XADESv141_NS, "xades141")); ts.setProperty("Id", id() + "-A0"); diff --git a/src/SignatureXAdES_T.cpp b/src/SignatureXAdES_T.cpp index 39ae1ef19..e2da1ca33 100644 --- a/src/SignatureXAdES_T.cpp +++ b/src/SignatureXAdES_T.cpp @@ -20,7 +20,6 @@ #include "SignatureXAdES_T.h" #include "ASiC_E.h" -#include "Conf.h" #include "crypto/Digest.h" #include "crypto/OCSP.h" #include "crypto/TS.h" @@ -72,7 +71,7 @@ void SignatureXAdES_T::extendSignatureProfile(const std::string &profile) auto method = canonicalizationMethod(); signatures->c14n(&calc, method, signatureValue()); - TS tsa(CONF(TSUrl), calc); + TS tsa(calc); auto ts = usp + "SignatureTimeStamp"; ts.setProperty("Id", id() + Log::format("-T%zu", i)); (ts + CanonicalizationMethod).setProperty("Algorithm", method); diff --git a/src/crypto/TS.cpp b/src/crypto/TS.cpp index 47ab67394..064814951 100644 --- a/src/crypto/TS.cpp +++ b/src/crypto/TS.cpp @@ -55,7 +55,7 @@ void *OPENSSL_memdup(const void *data, size_t size) } #endif -TS::TS(const string &url, const Digest &digest) +TS::TS(const Digest &digest) { auto req = SCOPE_PTR(TS_REQ, TS_REQ_new()); TS_REQ_set_version(req.get(), 1); @@ -87,7 +87,7 @@ TS::TS(const string &url, const Digest &digest) RAND_bytes(nonce->data, nonce->length); TS_REQ_set_nonce(req.get(), nonce.get()); - Connect::Result result = Connect(url, "POST", 0, CONF(TSCerts)).exec({ + Connect::Result result = Connect(CONF(TSUrl), "POST", 0, CONF(TSCerts)).exec({ {"Content-Type", "application/timestamp-query"}, {"Accept", "application/timestamp-reply"}, {"Connection", "Close"}, diff --git a/src/crypto/TS.h b/src/crypto/TS.h index 98f1d4e26..d22eca87b 100644 --- a/src/crypto/TS.h +++ b/src/crypto/TS.h @@ -30,7 +30,7 @@ class X509Cert; class TS { public: - TS(const std::string &url, const Digest &digest); + TS(const Digest &digest); inline TS(const std::vector &data): TS(data.data(), data.size()) {} TS(const unsigned char *data = nullptr, size_t size = 0);