From e18d0c435693637ede3901de5b6458c938bdd2fb 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 | 51 ++++++++++++++++++++++++++++++++++++++++---- src/ASiC_S.h | 7 ++++++ src/SignatureTST.cpp | 29 +++++++++++++++++++++++++ src/SignatureTST.h | 5 ++++- 4 files changed, 87 insertions(+), 5 deletions(-) diff --git a/src/ASiC_S.cpp b/src/ASiC_S.cpp index 6cfdfc071..a8b1dc5e7 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. */ @@ -54,7 +67,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.extract(file).str(), this)); + string tst = z.extract(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") { @@ -66,7 +81,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) { + auto xml = z.extract(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.extract(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); @@ -112,6 +147,14 @@ void ASiC_S::canSave() THROW("ASiC-S container supports only saving TimeStampToken signatures."); } +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)) @@ -129,8 +172,8 @@ void ASiC_S::save(const ZipSerialize &s) { if(zproperty("META-INF/manifest.xml").size && !createManifest().save(s.addFile("META-INF/manifest.xml", zproperty("META-INF/manifest.xml")), true)) THROW("Failed to create manifest XML"); - if(auto list = signatures(); !list.empty()) - s.addFile("META-INF/timestamp.tst", zproperty("META-INF/timestamp.tst"))(static_cast(list.front())->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 af32d5673..6bbca3758 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); @@ -50,5 +54,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 4a65b90bc..0c0601981 100644 --- a/src/SignatureTST.cpp +++ b/src/SignatureTST.cpp @@ -24,16 +24,29 @@ #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) { @@ -101,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) { @@ -113,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; };