From 5aa8bbd8eed4bc6b6b610a6281d649d3503e2c76 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Tue, 16 Sep 2025 15:37:32 +0300 Subject: [PATCH 1/2] Extend XAdES LTA signatures IB-7995 Signed-off-by: Raul Metsma --- src/Signature.cpp | 14 ++++++-- src/SignatureTST.cpp | 14 -------- src/SignatureTST.h | 2 -- src/SignatureXAdES_B.cpp | 3 ++ src/SignatureXAdES_LTA.cpp | 72 +++++++++++++++---------------------- src/SignatureXAdES_LTA.h | 6 ++-- src/XMLDocument.h | 5 +++ src/digidoc-tool.1.cmake | 7 ++++ src/digidoc-tool.cpp | 21 +++++++++-- test/libdigidocpp_boost.cpp | 7 ++-- 10 files changed, 80 insertions(+), 71 deletions(-) diff --git a/src/Signature.cpp b/src/Signature.cpp index c7fc58fef..7314c00f6 100644 --- a/src/Signature.cpp +++ b/src/Signature.cpp @@ -223,12 +223,22 @@ string Signature::TimeStampTime() const { return {}; } /** * Returns signature Archive TimeStampToken certificate. */ -X509Cert Signature::ArchiveTimeStampCertificate() const { return X509Cert(); } +X509Cert Signature::ArchiveTimeStampCertificate() const +{ + if(auto list = ArchiveTimeStamps(); !list.empty()) + return list.back().cert; + return X509Cert(); +} /** * Returns signature Archive TimeStampToken time. */ -string Signature::ArchiveTimeStampTime() const { return {}; } +string Signature::ArchiveTimeStampTime() const +{ + if(auto list = ArchiveTimeStamps(); !list.empty()) + return list.back().time; + return {}; +} /** * Returns signature Archive TimeStampTokens. diff --git a/src/SignatureTST.cpp b/src/SignatureTST.cpp index ad28c7c39..7767a4c7e 100644 --- a/src/SignatureTST.cpp +++ b/src/SignatureTST.cpp @@ -110,20 +110,6 @@ SignatureTST::SignatureTST(ASiC_S *asicSDoc, Signer *signer) SignatureTST::~SignatureTST() = default; -X509Cert SignatureTST::ArchiveTimeStampCertificate() const -{ - if(auto list = ArchiveTimeStamps(); !list.empty()) - return list.front().cert; - return X509Cert(); -} - -string SignatureTST::ArchiveTimeStampTime() const -{ - if(auto list = ArchiveTimeStamps(); !list.empty()) - return list.front().time; - return {}; -} - vector SignatureTST::ArchiveTimeStamps() const { vector result; diff --git a/src/SignatureTST.h b/src/SignatureTST.h index bf3bcbd86..d6da41b0a 100644 --- a/src/SignatureTST.h +++ b/src/SignatureTST.h @@ -56,8 +56,6 @@ class SignatureTST final: public Signature std::string profile() const final; //TSA profile properties - X509Cert ArchiveTimeStampCertificate() const final; - std::string ArchiveTimeStampTime() const final; std::vector ArchiveTimeStamps() const final; void save(const ZipSerialize &s) const; diff --git a/src/SignatureXAdES_B.cpp b/src/SignatureXAdES_B.cpp index f84d870af..f19db6f64 100644 --- a/src/SignatureXAdES_B.cpp +++ b/src/SignatureXAdES_B.cpp @@ -333,6 +333,9 @@ SignatureXAdES_B::SignatureXAdES_B(const shared_ptr &signatures, XML "AttrAuthoritiesCertValues", "AttributeRevocationValues", "ArchiveTimeStamp"}) if(usp/elem) THROW("%s is not supported", elem); + for(const char *elem: {"CompleteCertificateRefsV2", "AttributeCertificateRefsV2", "SigAndRefsTimeStampV2", "RefsOnlyTimeStampV2"}) + if(usp/XMLName{elem, XADESv141_NS}) + THROW("%s is not supported", elem); for(const char *elem: {"CompleteCertificateRefs", "CompleteRevocationRefs", "SigAndRefsTimeStamp", "TimeStampValidationData"}) if(usp/elem) WARN("%s are not supported", elem); diff --git a/src/SignatureXAdES_LTA.cpp b/src/SignatureXAdES_LTA.cpp index bfa1128b7..f899db758 100644 --- a/src/SignatureXAdES_LTA.cpp +++ b/src/SignatureXAdES_LTA.cpp @@ -39,7 +39,7 @@ namespace digidoc constexpr XMLName ArchiveTimeStamp {"ArchiveTimeStamp", XADESv141_NS}; } -void SignatureXAdES_LTA::calcArchiveDigest(const Digest &digest, string_view canonicalizationMethod) const +void SignatureXAdES_LTA::calcArchiveDigest(const Digest &digest, string_view canonicalizationMethod, XMLNode ts) const { for(auto ref = signature/"SignedInfo"/"Reference"; ref; ref++) { @@ -64,7 +64,7 @@ void SignatureXAdES_LTA::calcArchiveDigest(const Digest &digest, string_view can if(file == files.cend()) THROW("Filed to find reference URI in container"); - static_cast(*file)->digest(digest); + dynamic_cast(*file)->digest(digest); } for(const auto *name: {"SignedInfo", "SignatureValue", "KeyInfo"}) @@ -75,65 +75,46 @@ void SignatureXAdES_LTA::calcArchiveDigest(const Digest &digest, string_view can DEBUG("Element %s not found", name); } - auto usp = unsignedSignatureProperties(); - for(const auto *name: { - "SignatureTimeStamp", - "CounterSignature", - "CompleteCertificateRefs", - "CompleteRevocationRefs", - "AttributeCertificateRefs", - "AttributeRevocationRefs", - "CertificateValues", - "RevocationValues", - "SigAndRefsTimeStamp", - "RefsOnlyTimeStamp" }) + for(auto elem: unsignedSignatureProperties()) { - if(auto elem = usp/name) - signatures->c14n(digest, canonicalizationMethod, elem); - else - DEBUG("Element %s not found", name); - } - - if(auto elem = usp/XMLName{"TimeStampValidationData", XADESv141_NS}) + if(elem == ts) + break; signatures->c14n(digest, canonicalizationMethod, elem); - else - DEBUG("Element TimeStampValidationData not found"); + } //ds:Object } void SignatureXAdES_LTA::extendSignatureProfile(Signer *signer) { - SignatureXAdES_LT::extendSignatureProfile(signer); + if(SignatureXAdES_LTA::profile().find(ASiC_E::ASIC_TS_PROFILE) == string::npos) + SignatureXAdES_LT::extendSignatureProfile(signer); if(signer->profile() != ASiC_E::ASIC_TSA_PROFILE) return; + + int i = 0; + for(auto ts = unsignedSignatureProperties()/ArchiveTimeStamp; ts; ts++, ++i); + Digest calc; auto method = canonicalizationMethod(); - calcArchiveDigest(calc, method); + calcArchiveDigest(calc, method, {}); TS tsa(calc, signer->userAgent()); auto ts = unsignedSignatureProperties() + ArchiveTimeStamp; ts.setNS(ts.addNS(XADESv141_NS, "xades141")); - ts.setProperty("Id", id() + "-A0"); + ts.setProperty("Id", id() + "-A" + to_string(i)); (ts + CanonicalizationMethod).setProperty("Algorithm", method); ts + EncapsulatedTimeStamp = tsa; } -TS SignatureXAdES_LTA::tsaFromBase64() const -{ - try { - return {unsignedSignatureProperties()/ArchiveTimeStamp/EncapsulatedTimeStamp}; - } catch(const Exception &) {} - return {}; -} - -X509Cert SignatureXAdES_LTA::ArchiveTimeStampCertificate() const -{ - return tsaFromBase64().cert(); -} - -string SignatureXAdES_LTA::ArchiveTimeStampTime() const +vector SignatureXAdES_LTA::ArchiveTimeStamps() const { - return date::to_string(tsaFromBase64().time()); + vector result; + for(auto ts = unsignedSignatureProperties()/ArchiveTimeStamp; ts; ts++) + { + TS t(ts/EncapsulatedTimeStamp); + result.push_back({t.cert(), util::date::to_string(t.time())}); + } + return result; } void SignatureXAdES_LTA::validate(const string &policy) const @@ -157,9 +138,12 @@ void SignatureXAdES_LTA::validate(const string &policy) const auto ts = unsignedSignatureProperties()/ArchiveTimeStamp; if(!ts) THROW("Missing ArchiveTimeStamp element"); - verifyTS(ts, exception, [this](const Digest &digest, string_view canonicalizationMethod) { - calcArchiveDigest(digest, canonicalizationMethod); - }); + for(; ts; ts++) + { + verifyTS(ts, exception, [this, ts](const Digest &digest, string_view canonicalizationMethod) { + calcArchiveDigest(digest, canonicalizationMethod, ts); + }); + } } catch(const Exception &e) { exception.addCause(e); } catch(...) { diff --git a/src/SignatureXAdES_LTA.h b/src/SignatureXAdES_LTA.h index 1c8848cc3..34c5b5202 100644 --- a/src/SignatureXAdES_LTA.h +++ b/src/SignatureXAdES_LTA.h @@ -29,16 +29,14 @@ class SignatureXAdES_LTA final: public SignatureXAdES_LT public: using SignatureXAdES_LT::SignatureXAdES_LT; - X509Cert ArchiveTimeStampCertificate() const final; - std::string ArchiveTimeStampTime() const final; + std::vector ArchiveTimeStamps() const final; void validate(const std::string &policy) const final; void extendSignatureProfile(Signer *signer) final; private: DISABLE_COPY(SignatureXAdES_LTA); - void calcArchiveDigest(const Digest &digest, std::string_view canonicalizationMethod) const; - TS tsaFromBase64() const; + void calcArchiveDigest(const Digest &digest, std::string_view canonicalizationMethod, XMLNode node) const; }; } diff --git a/src/XMLDocument.h b/src/XMLDocument.h index c05d0a7e4..67bbd4bbf 100644 --- a/src/XMLDocument.h +++ b/src/XMLDocument.h @@ -142,6 +142,11 @@ struct XMLElem return bool(d); } + constexpr bool operator==(XMLElem other) const noexcept + { + return d == other.d; + } + constexpr auto& operator++() noexcept { d = d ? find(d->next, d->type) : nullptr; diff --git a/src/digidoc-tool.1.cmake b/src/digidoc-tool.1.cmake index edd974c84..6eca6a05b 100644 --- a/src/digidoc-tool.1.cmake +++ b/src/digidoc-tool.1.cmake @@ -69,6 +69,13 @@ Command sign: --dontValidate - Don't validate container on signature creation --userAgent - Additional info info that is sent to TSA or OCSP service +Command extend: + Example: " << executable << " extend --signature=0 demo-container.asice + Available options: + --profile= - signature profile, TS, TSA, time-stamp, time-stamp-archive + --signature= - signature to extend + --dontValidate - Don't validate container on signature creation + All commands: --nocolor - Disable terminal colors --loglevel=[0,1,2,3,4] - Log level 0 - none, 1 - error, 2 - warning, 3 - info, 4 - debug diff --git a/src/digidoc-tool.cpp b/src/digidoc-tool.cpp index cdafc8642..e5adc2ca7 100644 --- a/src/digidoc-tool.cpp +++ b/src/digidoc-tool.cpp @@ -401,6 +401,12 @@ static int printUsage(const char *executable) << " --tsurl - option to change TS URL (default " << CONF(TSUrl) << ")" << endl << " --dontValidate - Don't validate container on signature creation" << endl << endl << " --userAgent - Additional info info that is sent to TSA or OCSP service" << endl << endl + << " Command extend:" << endl + << " Example: " << executable << " extend --signature=0 demo-container.asice" << endl + << " Available options:" << endl + << " --profile= - signature profile, TS, TSA, time-stamp, time-stamp-archive" << endl + << " --signature= - signature to extend" << endl + << " --dontValidate - Don't validate container on signature creation" << endl << endl << " All commands:" << endl << " --nocolor - Disable terminal colors" << endl << " --loglevel=[0,1,2,3,4] - Log level 0 - none, 1 - error, 2 - warning, 3 - info, 4 - debug" << endl @@ -711,7 +717,7 @@ static int open(int argc, char* argv[]) */ static int extend(int argc, char *argv[]) { - vector signatures; + vector extendId; bool dontValidate = false; CertSigner signer(X509Cert{}); value path; @@ -721,7 +727,7 @@ static int extend(int argc, char *argv[]) if(value v{arg, "--profile="}) signer.setProfile(v); else if(value v{arg, "--signature="}) - signatures.push_back(unsigned(atoi(v.data()))); + extendId.push_back(unsigned(atoi(v.data()))); else if(arg == "--dontValidate") dontValidate = true; else @@ -740,8 +746,17 @@ static int extend(int argc, char *argv[]) return EXIT_FAILURE; } - for(unsigned int i : signatures) + auto signatures = doc->signatures(); + if(signatures.empty()) + { + cout << " Container does not contain signatures\n"; + return EXIT_SUCCESS; + } + + for(unsigned int i : extendId) { + if(i >= signatures.size()) + THROW("Incorrect signature id %u, there are only %zu signatures in container.", i, signatures.size()); cout << " Extending signature " << i << " to " << signer.profile() << endl; Signature *s = doc->signatures().at(i); s->extendSignatureProfile(&signer); diff --git a/test/libdigidocpp_boost.cpp b/test/libdigidocpp_boost.cpp index 2adeccd16..fcb1f3967 100644 --- a/test/libdigidocpp_boost.cpp +++ b/test/libdigidocpp_boost.cpp @@ -354,8 +354,11 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(signature, Doc, DocTypes) // TSA signature signer2.setProfile("time-stamp-archive"); BOOST_CHECK_NO_THROW(s3 = d->sign(&signer2)); - //BOOST_CHECK_EQUAL(s3->TSCertificate(), signer2.cert()); - //BOOST_CHECK_NO_THROW(s3->validate()); + BOOST_CHECK_EQUAL(s3->signingCertificate(), signer2.cert()); + BOOST_CHECK_NO_THROW(s3->validate()); + // Extend TSA + BOOST_CHECK_NO_THROW(s3->extendSignatureProfile(&signer2)); + BOOST_CHECK_NO_THROW(s3->validate()); BOOST_CHECK_NO_THROW(d->save(Doc::EXT + "-TSA.tmp")); BOOST_CHECK_NO_THROW(d->removeSignature(1U)); BOOST_CHECK_EQUAL(d->signatures().size(), 1U); From 7f91c9ba9a5e10265f25a7d650171ad4bef9fa4d Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Fri, 6 Dec 2024 12:04:42 +0200 Subject: [PATCH 2/2] Add extend Container validity method IB-8265 Signed-off-by: Raul Metsma --- libdigidocpp.i | 9 ++ src/ASiC_E.cpp | 186 +++++++++++++++++++----------------------- src/ASiC_E.h | 4 +- src/ASiC_S.cpp | 21 ++--- src/ASiC_S.h | 3 +- src/ASiContainer.cpp | 22 ++--- src/ASiContainer.h | 7 +- src/Container.cpp | 102 ++++++++++++++++++++++- src/Container.h | 4 + src/SiVaContainer.cpp | 5 ++ src/SiVaContainer.h | 2 + src/digidoc-tool.cpp | 10 +++ 12 files changed, 233 insertions(+), 142 deletions(-) diff --git a/libdigidocpp.i b/libdigidocpp.i index b365b6d99..7ff62b041 100644 --- a/libdigidocpp.i +++ b/libdigidocpp.i @@ -214,9 +214,11 @@ static std::vector* SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *je // std::unique_ptr is since swig 4.1 %ignore digidoc::Container::createPtr; %ignore digidoc::Container::openPtr; +%ignore digidoc::Container::extendContainerValidity; %newobject digidoc::Container::open; %newobject digidoc::Container::create; +%newobject digidoc::Container::extendContainerValidity; %immutable digidoc::TSAInfo::cert; %immutable digidoc::TSAInfo::time; @@ -280,7 +282,14 @@ def transfer(self): %template(Signatures) std::vector; %template(TSAInfos) std::vector; +%rename("%s") digidoc::Container::extendContainerValidity; + %extend digidoc::Container { + static Container* extendContainerValidity(Container &doc, Signer *signer) + { + return digidoc::Container::extendContainerValidity(doc, signer).release(); + } + static digidoc::Container* open(const std::string &path, digidoc::ContainerOpenCB *cb) { return digidoc::Container::openPtr(path, cb).release(); diff --git a/src/ASiC_E.cpp b/src/ASiC_E.cpp index 5b2b584b3..a73fb6e1d 100644 --- a/src/ASiC_E.cpp +++ b/src/ASiC_E.cpp @@ -51,21 +51,89 @@ class ASiC_E::Private /** * Initialize BDOC container. */ -ASiC_E::ASiC_E() - : ASiContainer(MIMETYPE_ASIC_E) +ASiC_E::ASiC_E(const string &path, bool create) + : ASiContainer(path, MIMETYPE_ASIC_E) , d(make_unique()) { -} + if(create) + return; + auto z = load(true, {MIMETYPE_ASIC_E, MIMETYPE_ADOC}); -/** - * Opens ASiC container from a file - */ -ASiC_E::ASiC_E(const string &path) - : ASiContainer(MIMETYPE_ASIC_E) - , d(make_unique()) -{ - auto zip = load(path, true, {MIMETYPE_ASIC_E, MIMETYPE_ADOC}); - parseManifestAndLoadFiles(zip); + try + { + auto manifestdata = z.extract("META-INF/manifest.xml"); + auto doc = XMLDocument::openStream(manifestdata, {"manifest", MANIFEST_NS}); + doc.validateSchema(File::path(Conf::instance()->xsdPath(), "OpenDocument_manifest_v1_2.xsd")); + + set manifestFiles; + bool mimeFound = false; + for(auto file = doc/"file-entry"; file; file++) + { + auto full_path = file[{"full-path", MANIFEST_NS}]; + auto media_type = file[{"media-type", MANIFEST_NS}]; + DEBUG("full_path = '%s', media_type = '%s'", full_path.data(), media_type.data()); + + if(manifestFiles.find(full_path) != manifestFiles.end()) + THROW("Manifest multiple entries defined for file '%s'.", full_path.data()); + + // ODF does not specify that mimetype should be first in manifest + if(full_path == "/") + { + if(mediaType() != media_type) + THROW("Manifest has incorrect container media type defined '%s', expecting '%s'.", media_type.data(), mediaType().c_str()); + mimeFound = true; + continue; + } + if(full_path.back() == '/') // Skip Directory entries + continue; + + manifestFiles.insert(full_path); + if(mediaType() == MIMETYPE_ADOC && + (full_path.compare(0, 9, "META-INF/") == 0 || + full_path.compare(0, 9, "metadata/") == 0)) + d->metadata.push_back(new DataFilePrivate(dataStream(full_path, z), string(full_path), string(media_type))); + else + addDataFilePrivate(dataStream(full_path, z), string(full_path), string(media_type)); + } + if(!mimeFound) + THROW("Manifest is missing mediatype file entry."); + + for(const string &file: z.list()) + { + /** + * http://www.etsi.org/deliver/etsi_ts/102900_102999/102918/01.03.01_60/ts_102918v010301p.pdf + * 6.2.2 Contents of Container + * 3) The root element of each "*signatures*.xml" content shall be either: + */ + if(file.compare(0, 9, "META-INF/") == 0 && + file.find("signatures") != string::npos) + { + try + { + auto data = z.extract(file); + loadSignatures(data, file); + } + catch(const Exception &e) + { + THROW_CAUSE(e, "Failed to parse signature '%s'.", file.c_str()); + } + continue; + } + + if(file == "mimetype" || file.compare(0, 8,"META-INF") == 0) + continue; + if(manifestFiles.count(file) == 0) + THROW("File '%s' found in container is not described in manifest.", file.c_str()); + } + } + catch(const Exception &e) + { + THROW_CAUSE(e, "Failed to parse manifest"); + } + catch(...) + { + THROW("Failed to parse manifest XML: Unknown exception"); + } } ASiC_E::~ASiC_E() @@ -110,9 +178,7 @@ void ASiC_E::save(const ZipSerialize &s) unique_ptr ASiC_E::createInternal(const string &path) { DEBUG("ASiC_E::createInternal(%s)", path.c_str()); - unique_ptr doc = unique_ptr(new ASiC_E); - doc->zpath(path); - return doc; + return unique_ptr(new ASiC_E(path, true)); } /** @@ -146,7 +212,7 @@ void ASiC_E::canSave() unique_ptr ASiC_E::openInternal(const string &path) { DEBUG("ASiC_E::openInternal(%s)", path.c_str()); - return unique_ptr(new ASiC_E(path)); + return unique_ptr(new ASiC_E(path, false)); } void ASiC_E::loadSignatures(istream &data, const string &file) @@ -157,94 +223,6 @@ void ASiC_E::loadSignatures(istream &data, const string &file) addSignature(make_unique(signatures, s, this)); } -/** - * Parses manifest file and checks that files described in manifest exist, also - * checks that no extra file do exist that are not described in manifest.xml. - * - * @param path directory on disk of the BDOC container. - * @throws Exception exception is thrown if the manifest.xml file parsing failed. - */ -void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z) -{ - DEBUG("ASiC_E::readManifest()"); - - try - { - auto manifestdata = z.extract("META-INF/manifest.xml"); - auto doc = XMLDocument::openStream(manifestdata, {"manifest", MANIFEST_NS}); - doc.validateSchema(File::path(Conf::instance()->xsdPath(), "OpenDocument_manifest_v1_2.xsd")); - - set manifestFiles; - bool mimeFound = false; - for(auto file = doc/"file-entry"; file; file++) - { - auto full_path = file[{"full-path", MANIFEST_NS}]; - auto media_type = file[{"media-type", MANIFEST_NS}]; - DEBUG("full_path = '%s', media_type = '%s'", full_path.data(), media_type.data()); - - if(manifestFiles.find(full_path) != manifestFiles.end()) - THROW("Manifest multiple entries defined for file '%s'.", full_path.data()); - - // ODF does not specify that mimetype should be first in manifest - if(full_path == "/") - { - if(mediaType() != media_type) - THROW("Manifest has incorrect container media type defined '%s', expecting '%s'.", media_type.data(), mediaType().c_str()); - mimeFound = true; - continue; - } - if(full_path.back() == '/') // Skip Directory entries - continue; - - manifestFiles.insert(full_path); - if(mediaType() == MIMETYPE_ADOC && - (full_path.compare(0, 9, "META-INF/") == 0 || - full_path.compare(0, 9, "metadata/") == 0)) - d->metadata.push_back(new DataFilePrivate(dataStream(full_path, z), string(full_path), string(media_type))); - else - addDataFilePrivate(dataStream(full_path, z), string(full_path), string(media_type)); - } - if(!mimeFound) - THROW("Manifest is missing mediatype file entry."); - - for(const string &file: z.list()) - { - /** - * http://www.etsi.org/deliver/etsi_ts/102900_102999/102918/01.03.01_60/ts_102918v010301p.pdf - * 6.2.2 Contents of Container - * 3) The root element of each "*signatures*.xml" content shall be either: - */ - if(file.compare(0, 9, "META-INF/") == 0 && - file.find("signatures") != string::npos) - { - try - { - auto data = z.extract(file); - loadSignatures(data, file); - } - catch(const Exception &e) - { - THROW_CAUSE(e, "Failed to parse signature '%s'.", file.c_str()); - } - continue; - } - - if(file == "mimetype" || file.compare(0, 8,"META-INF") == 0) - continue; - if(manifestFiles.count(file) == 0) - THROW("File '%s' found in container is not described in manifest.", file.c_str()); - } - } - catch(const Exception &e) - { - THROW_CAUSE(e, "Failed to parse manifest"); - } - catch(...) - { - THROW("Failed to parse manifest XML: Unknown exception"); - } -} - Signature* ASiC_E::prepareSignature(Signer *signer) { if(mediaType() != MIMETYPE_ASIC_E) diff --git a/src/ASiC_E.h b/src/ASiC_E.h index 88b0d93a1..0e11a8ff8 100644 --- a/src/ASiC_E.h +++ b/src/ASiC_E.h @@ -50,12 +50,10 @@ namespace digidoc static std::unique_ptr openInternal(const std::string &path); private: - ASiC_E(); - ASiC_E(const std::string &path); + ASiC_E(const std::string &path, bool create); DISABLE_COPY(ASiC_E); void canSave() final; void loadSignatures(std::istream &data, const std::string &file); - void parseManifestAndLoadFiles(const ZipSerialize &z); void save(const ZipSerialize &s) final; class Private; diff --git a/src/ASiC_S.cpp b/src/ASiC_S.cpp index fec68d721..b0946a3d8 100644 --- a/src/ASiC_S.cpp +++ b/src/ASiC_S.cpp @@ -35,17 +35,12 @@ using namespace std; /** * Initialize ASiCS container. */ -ASiC_S::ASiC_S() - : ASiContainer(MIMETYPE_ASIC_S) -{} - -/** - * Opens ASiC-S container from a file - */ -ASiC_S::ASiC_S(const string &path) - : ASiContainer(MIMETYPE_ASIC_S) +ASiC_S::ASiC_S(const string &path, bool create) + : ASiContainer(path, MIMETYPE_ASIC_S) { - auto z = load(path, false, {mediaType()}); + if(create) + return; + auto z = load(false, {mediaType()}); bool foundTimestamp = false; bool foundManifest = false; for(const string &file: z.list()) @@ -105,9 +100,7 @@ unique_ptr ASiC_S::createInternal(const string &path) if(!util::File::fileExtension(path, {"asics", "scs"})) return {}; DEBUG("ASiC_S::createInternal(%s)", path.c_str()); - auto doc = unique_ptr(new ASiC_S()); - doc->zpath(path); - return doc; + return unique_ptr(new ASiC_S(path, true)); } void ASiC_S::addAdESSignature(istream & /*signature*/) @@ -128,7 +121,7 @@ unique_ptr ASiC_S::openInternal(const string &path, ContainerOpenCB * { if(util::File::fileExtension(path, {"asice", "sce", "bdoc"})) return {}; - return unique_ptr(new ASiC_S(path)); + return unique_ptr(new ASiC_S(path, false)); } catch(const Exception &) { diff --git a/src/ASiC_S.h b/src/ASiC_S.h index 5397a6802..6353f2086 100644 --- a/src/ASiC_S.h +++ b/src/ASiC_S.h @@ -41,8 +41,7 @@ namespace digidoc static std::unique_ptr openInternal(const std::string &path, ContainerOpenCB *cb); private: - ASiC_S(); - ASiC_S(const std::string &path); + ASiC_S(const std::string &path, bool create); DISABLE_COPY(ASiC_S); void addDataFileChecks(const std::string &path, const std::string &mediaType) override; diff --git a/src/ASiContainer.cpp b/src/ASiContainer.cpp index 37abaa8ec..623fd78fe 100644 --- a/src/ASiContainer.cpp +++ b/src/ASiContainer.cpp @@ -41,7 +41,7 @@ constexpr unsigned long MAX_MEM_FILE = 500UL*1024UL*1024UL; class ASiContainer::Private { public: - string mimetype, path; + string path, mimetype; vector documents; vector signatures; map> properties; @@ -50,9 +50,10 @@ class ASiContainer::Private /** * Initialize Container. */ -ASiContainer::ASiContainer(string_view mimetype) +ASiContainer::ASiContainer(string_view path, string_view mimetype) : d(make_unique()) { + d->path = string(path); d->mimetype = string(mimetype); } @@ -80,10 +81,10 @@ XMLDocument ASiContainer::createManifest() const * @param supported supported mimetypes. * @return returns zip serializer for the container. */ -ZipSerialize ASiContainer::load(const string &path, bool mimetypeRequired, const set &supported) +ZipSerialize ASiContainer::load(bool mimetypeRequired, const set &supported) { - DEBUG("ASiContainer::ASiContainer(path = '%s')", path.c_str()); - ZipSerialize z(d->path = path, false); + DEBUG("ASiContainer::ASiContainer(path = '%s')", d->path.c_str()); + ZipSerialize z(d->path, false); vector list = z.list(); // ETSI TS 102 918: mimetype has to be the first in the archive @@ -266,8 +267,8 @@ void ASiContainer::save(const string &path) THROW("Can not save, container is empty."); canSave(); if(!path.empty()) - zpath(path); - ZipSerialize s(zpath(), true); + d->path = path; + ZipSerialize s(d->path, true); s.addFile("mimetype", zproperty("mimetype"), false)(mediaType()); array buf{}; @@ -288,12 +289,7 @@ void ASiContainer::save(const string &path) save(s); } -void ASiContainer::zpath(const string &file) -{ - d->path = file; -} - -string ASiContainer::zpath() const +const string& ASiContainer::path() const { return d->path; } diff --git a/src/ASiContainer.h b/src/ASiContainer.h index cd2772287..bb2fc5265 100644 --- a/src/ASiContainer.h +++ b/src/ASiContainer.h @@ -59,9 +59,10 @@ namespace digidoc std::vector signatures() const override; static std::string readMimetype(const ZipSerialize &z); + const std::string& path() const override; protected: - ASiContainer(std::string_view mimetype); + ASiContainer(std::string_view path, std::string_view mimetype); virtual void addDataFileChecks(const std::string &path, const std::string &mediaType); void addDataFilePrivate(std::unique_ptr is, std::string fileName, std::string mediaType); @@ -69,12 +70,10 @@ namespace digidoc virtual void canSave() = 0; XMLDocument createManifest() const; std::unique_ptr dataStream(std::string_view path, const ZipSerialize &z) const; - ZipSerialize load(const std::string &path, bool requireMimetype, const std::set &supported); + ZipSerialize load(bool requireMimetype, const std::set &supported); virtual void save(const ZipSerialize &s) = 0; void deleteSignature(Signature* s); - void zpath(const std::string &file); - std::string zpath() const; const ZipSerialize::Properties& zproperty(std::string_view file) const; private: diff --git a/src/Container.cpp b/src/Container.cpp index b3c01df1f..4f749134b 100644 --- a/src/Container.cpp +++ b/src/Container.cpp @@ -25,7 +25,9 @@ #include "PDF.h" #include "SiVaContainer.h" #include "XmlConf.h" +#include "crypto/Signer.h" #include "crypto/X509CertStore.h" +#include "util/algorithm.h" #include "util/File.h" #include "util/log.h" @@ -36,7 +38,7 @@ #include #include -#include +#include #include #include @@ -305,6 +307,102 @@ unique_ptr Container::createPtr(const std::string &path) return ASiC_E::createInternal(path); } +/** + * Extends Container Validity + * + * @returns Container if new container is created new reference is returned + */ +std::unique_ptr Container::extendContainerValidity(Container &doc, Signer *signer) try +{ + if(doc.signatures().empty()) + THROW("Container does not contain signatures"); + + if(doc.mediaType() == ASiContainer::MIMETYPE_ASIC_S || + doc.mediaType() == ASiContainer::MIMETYPE_ASIC_E) + { + bool extendInPlace = true; + for(Signature *s: doc.signatures()) + { + if(s->profile().find(ASiC_S::ASIC_TST_PROFILE) != string::npos) + break; + if(s->profile().find(ASiC_E::ASIC_TS_PROFILE) == string::npos) + { + extendInPlace = false; + break; + } + + auto signingCert = s->signingCertificate(); + if(auto list = s->ArchiveTimeStamps(); !list.empty() && !list.back().cert.isValid()) + { + extendInPlace = false; + break; + } + else if(list.empty() && (!s->TimeStampCertificate().isValid() || !signingCert.isValid())) + { + extendInPlace = false; + break; + } + else if(!s->OCSPCertificate().isValid()) + { + extendInPlace = false; + break; + } + + try { + s->validate(); + } catch(const Exception &e) { + std::function isInvalid; + isInvalid = [&](const Exception &e) { + for(const Exception &child: e.causes()) + { + switch(child.code()) + { + case Exception::MimeTypeWarning: + return false; + case Exception::CertificateIssuerMissing: + case Exception::CertificateUnknown: + case Exception::OCSPResponderMissing: + case Exception::OCSPCertMissing: + //break; TODO: should we extend? + default: + break; + } + return isInvalid(child); + } + return true; + }; + if(isInvalid(e)) + { + extendInPlace = false; + break; + } + } + } + + if(extendInPlace) + { + for(Signature *s: doc.signatures()) + { + if(contains(s->signingCertificate().qcStatements(), digidoc::X509Cert::QCT_ESEAL)) + continue; + if(s->profile().find(ASiC_E::ASIC_TS_PROFILE) != string::npos) + signer->setProfile(string(ASiC_E::ASIC_TSA_PROFILE)); + else + signer->setProfile(string(ASiC_S::ASIC_TST_PROFILE)); + s->extendSignatureProfile(signer); + } + return {}; + } + } + + signer->setProfile(string(ASiC_S::ASIC_TST_PROFILE)); + auto asics = ASiC_S::createInternal(doc.path() + ".asics"); + asics->addDataFile(doc.path(), doc.mediaType()); + asics->sign(signer); + return asics; +} catch(const Exception &e) { + THROW_CAUSE(e, "Failed to extend signature"); +} /** * @fn digidoc::Container::dataFiles @@ -325,7 +423,7 @@ unsigned int Container::newSignatureId() const { vector list = signatures(); for(unsigned int id = 0; ; ++id) - if(!any_of(list.cbegin(), list.cend(), [id](Signature *s){ return s->id() == Log::format("S%u", id); })) + if(!any_of(list, [id](Signature *s){ return s->id() == Log::format("S%u", id); })) return id; } diff --git a/src/Container.h b/src/Container.h index 92655e978..23fbeb19c 100644 --- a/src/Container.h +++ b/src/Container.h @@ -75,6 +75,10 @@ class DIGIDOCPP_EXPORT Container template static void addContainerImplementation(); + static std::unique_ptr extendContainerValidity(Container &doc, Signer *signer); + + virtual const std::string& path() const = 0; + protected: Container(); unsigned int newSignatureId() const; diff --git a/src/SiVaContainer.cpp b/src/SiVaContainer.cpp index 234c17ae9..bb682fbcc 100644 --- a/src/SiVaContainer.cpp +++ b/src/SiVaContainer.cpp @@ -379,6 +379,11 @@ unique_ptr SiVaContainer::parseDDoc(bool useHashCode) } } +const string& SiVaContainer::path() const +{ + return d->path; +} + Signature* SiVaContainer::prepareSignature(Signer * /*signer*/) { THROW("Not implemented."); diff --git a/src/SiVaContainer.h b/src/SiVaContainer.h index 9624771ff..3d3c2b5fe 100644 --- a/src/SiVaContainer.h +++ b/src/SiVaContainer.h @@ -98,6 +98,8 @@ class SiVaContainer final: public Container void removeSignature(unsigned int id) final; Signature* sign(Signer* signer) final; + const std::string& path() const final; + static std::unique_ptr createInternal(const std::string &path); static std::unique_ptr openInternal(const std::string &path, ContainerOpenCB *cb); diff --git a/src/digidoc-tool.cpp b/src/digidoc-tool.cpp index e5adc2ca7..5e68ba5ae 100644 --- a/src/digidoc-tool.cpp +++ b/src/digidoc-tool.cpp @@ -764,6 +764,16 @@ static int extend(int argc, char *argv[]) validateSignature(s); } + if(extendId.empty()) + { + cout << " Extending " << signatures.size() << " signature(s)\n"; + if(auto wrapped = Container::extendContainerValidity(*doc, &signer)) + { + doc = std::move(wrapped); + cout << " Wrapped to new container\n"; + } + } + doc->save(); return EXIT_SUCCESS; }