diff --git a/libdigidocpp.dox b/libdigidocpp.dox index 9aef5fbdd..7bb2bc84b 100644 --- a/libdigidocpp.dox +++ b/libdigidocpp.dox @@ -777,6 +777,9 @@ You change the default behaviour in the configuration file with the following pa \tableofcontents Note that Libdigidocpp uses internal memory buffers in case of all the operations, so that intermediary data is not written to temporary files on the disk. Also, the data files to be added to a DigiDoc container can be read from a data stream and later extracted from the container to a stream so that the data can be kept in memory. +\subsection string-encoding String encoding +All std::string values passed to or returned from the API are UTF-8 encoded. This applies to file paths, media types, and all other string parameters and return values throughout the library. + \subsection initialization Initialization Libdigidocpp's initialization method conducts the following operations: 1. initializes dependent libraries (see also \ref libraries) 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 a2cc90fd6..a1cffc1b9 100644 --- a/src/ASiC_E.cpp +++ b/src/ASiC_E.cpp @@ -23,13 +23,11 @@ #include "DataFile_p.h" #include "SignatureXAdES_LTA.h" #include "XMLDocument.h" -#include "crypto/Digest.h" #include "crypto/Signer.h" #include "util/File.h" #include #include -#include using namespace digidoc; using namespace digidoc::util; @@ -51,21 +49,83 @@ class ASiC_E::Private /** * Initialize BDOC container. */ -ASiC_E::ASiC_E() - : ASiContainer(MIMETYPE_ASIC_E) +ASiC_E::ASiC_E(const string &path, bool create) try + : ASiContainer(path, MIMETYPE_ASIC_E) , d(make_unique()) { -} + if(create) + return; + auto z = load(true, {MIMETYPE_ASIC_E, MIMETYPE_ADOC}); + auto doc = XMLDocument::open(z.read("META-INF/manifest.xml"), {"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'", STR_VIEW_FMT(full_path), STR_VIEW_FMT(media_type)); -/** - * Opens ASiC container from a file - */ -ASiC_E::ASiC_E(const string &path) - : ASiContainer(MIMETYPE_ASIC_E) - , d(make_unique()) + // ODF does not specify that mimetype should be first in manifest + if(full_path == "/") + { + if(mimeFound) + THROW("Manifest multiple entries defined for file '/'."); + if(mediaType() != media_type) + THROW("Manifest has incorrect container media type defined '%.*s', expecting '%s'.", STR_VIEW_FMT(media_type), mediaType().c_str()); + mimeFound = true; + continue; + } + if(full_path.back() == '/') // Skip Directory entries + continue; + + if(const auto &[pos, inserted] = manifestFiles.insert(full_path); !inserted) + THROW("Manifest multiple entries defined for file '%.*s'.", STR_VIEW_FMT(full_path)); + if(mediaType() == MIMETYPE_ADOC && + (full_path.starts_with("META-INF/") || full_path.starts_with("metadata/"))) + d->metadata.push_back(new DataFilePrivate(z, string(full_path), string(media_type))); + else + addDataFilePrivate(new DataFilePrivate(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.starts_with("META-INF/") && file.contains("signatures")) + { + try + { + loadSignatures(XMLDocument::open(z.read(file)), file); + } + catch(const Exception &e) + { + THROW_CAUSE(e, "Failed to parse signature '%s'.", file.c_str()); + } + continue; + } + + if(file == "mimetype" || file.starts_with("META-INF")) + continue; + if(manifestFiles.erase(file) == 0) + THROW("File '%s' found in container is not described in manifest.", file.c_str()); + } + if(!manifestFiles.empty()) + THROW("Manifest describes files that are not found in container."); +} +catch(const Exception &e) { - auto zip = load(path, true, {MIMETYPE_ASIC_E, MIMETYPE_ADOC}); - parseManifestAndLoadFiles(zip); + THROW_CAUSE(e, "Failed to parse manifest"); +} +catch(...) +{ + THROW("Failed to parse manifest XML: Unknown exception"); } ASiC_E::~ASiC_E() @@ -110,9 +170,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 +204,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(XMLDocument &&doc, const string &file) @@ -157,92 +215,6 @@ void ASiC_E::loadSignatures(XMLDocument &&doc, 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 doc = XMLDocument::open(z.read("META-INF/manifest.xml"), {"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.contains(full_path)) - 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.starts_with("META-INF/") || - full_path.starts_with("metadata/"))) - d->metadata.push_back(new DataFilePrivate(z, string(full_path), string(media_type))); - else - addDataFilePrivate(new DataFilePrivate(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.starts_with("META-INF/") && - file.find("signatures") != string::npos) - { - try - { - loadSignatures(XMLDocument::open(z.read(file)), file); - } - catch(const Exception &e) - { - THROW_CAUSE(e, "Failed to parse signature '%s'.", file.c_str()); - } - continue; - } - - if(file == "mimetype" || file.starts_with("META-INF")) - continue; - if(!manifestFiles.contains(file)) - 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 4dc96c949..9ea12055a 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(XMLDocument &&doc, 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 9ba56669a..770163aba 100644 --- a/src/ASiC_S.cpp +++ b/src/ASiC_S.cpp @@ -27,8 +27,6 @@ #include "util/File.h" #include "util/log.h" -#include - using namespace digidoc; using namespace digidoc::util; using namespace std; @@ -36,17 +34,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()) @@ -108,9 +101,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*/) @@ -131,7 +122,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 d143b709d..15b3ab898 100644 --- a/src/ASiContainer.cpp +++ b/src/ASiContainer.cpp @@ -36,10 +36,9 @@ using namespace digidoc; using namespace digidoc::util; using namespace std; -class ASiContainer::Private +struct ASiContainer::Private { -public: - string mimetype, path; + string path, mimetype; vector documents; vector signatures; map> properties; @@ -48,11 +47,9 @@ class ASiContainer::Private /** * Initialize Container. */ -ASiContainer::ASiContainer(string_view mimetype) - : d(make_unique()) -{ - d->mimetype = string(mimetype); -} +ASiContainer::ASiContainer(const string &path, string_view mimetype) + : d(make_unique(path, string(mimetype))) +{} XMLDocument ASiContainer::createManifest() const { @@ -78,10 +75,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 @@ -247,8 +244,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{}; @@ -269,12 +266,7 @@ void ASiContainer::save(const string &path) save(s); } -void ASiContainer::zpath(const string &file) -{ - d->path = file; -} - -string ASiContainer::zpath() const +string ASiContainer::path() const { return d->path; } diff --git a/src/ASiContainer.h b/src/ASiContainer.h index f6c40fe89..e92674da7 100644 --- a/src/ASiContainer.h +++ b/src/ASiContainer.h @@ -59,19 +59,18 @@ namespace digidoc std::vector signatures() const override; protected: - ASiContainer(std::string_view mimetype); + std::string path() const override; + ASiContainer(const std::string &path, std::string_view mimetype); virtual void addDataFileChecks(const std::string &path, const std::string &mediaType); void addDataFilePrivate(DataFile *dataFile); Signature* addSignature(std::unique_ptr &&signature); virtual void canSave() = 0; XMLDocument createManifest() 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 58f46fea9..a66b34ee8 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 @@ -265,6 +267,16 @@ void Container::addDataFile(unique_ptr /*is*/, const string & /*fileNam THROW("Not implemented."); } +/** + * Returns the path of the container. + * @return path of the container. + * @since 4.5.0 + */ +string Container::path() const +{ + THROW("Not implemented."); +} + /** * Adds signature to the container. * @@ -310,6 +322,113 @@ unique_ptr Container::createPtr(const std::string &path) return ASiC_E::createInternal(path); } +/** + * Extends the validity of signatures in the container by adding a new timestamp. + * + * For ASiC-E containers with valid time-stamp signatures, extends each signature in place + * by adding an archive timestamp. For ASiC-S containers, re-timestamps the existing + * timestamp token. If in-place extension is not possible (expired certificates, unsupported + * profile, or validation errors), wraps the original container as a data file in a new + * ASiC-S container and signs it with a fresh timestamp. + * + * @param doc container whose signatures are to be extended. + * @param signer signer used to create the new timestamp; its profile will be set automatically. + * @return new ASiC-S container if wrapping was necessary; empty unique_ptr if extended in place. + * @throws Exception if the container has no signatures or extension fails. + * @since 4.5.0 + */ +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: + continue; + case Exception::CertificateIssuerMissing: + case Exception::CertificateUnknown: + case Exception::OCSPResponderMissing: + case Exception::OCSPCertMissing: + //break; TODO: should we extend? + default: + break; + } + if(isInvalid(child)) + return true; + } + return e.causes().empty(); + }; + 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 @@ -330,7 +449,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..5d5ddf696 100644 --- a/src/Container.h +++ b/src/Container.h @@ -75,7 +75,10 @@ class DIGIDOCPP_EXPORT Container template static void addContainerImplementation(); + static std::unique_ptr extendContainerValidity(Container &doc, Signer *signer); + protected: + virtual std::string path() const; Container(); unsigned int newSignatureId() const; diff --git a/src/SiVaContainer.cpp b/src/SiVaContainer.cpp index 759b23a12..f15ae80d1 100644 --- a/src/SiVaContainer.cpp +++ b/src/SiVaContainer.cpp @@ -381,6 +381,12 @@ unique_ptr SiVaContainer::parseDDoc(const unique_ptr &ddoc, bo } } +string SiVaContainer::path() const +{ + auto name = d->path.u8string(); + return {reinterpret_cast(name.data()), name.size()}; +} + Signature* SiVaContainer::prepareSignature(Signer * /*signer*/) { THROW("Not implemented."); diff --git a/src/SiVaContainer.h b/src/SiVaContainer.h index 935d938e6..5c9fed2e9 100644 --- a/src/SiVaContainer.h +++ b/src/SiVaContainer.h @@ -102,6 +102,7 @@ class SiVaContainer final: public Container static std::unique_ptr openInternal(const std::string &path, ContainerOpenCB *cb); private: + std::string path() const final; SiVaContainer(const std::string &path, ContainerOpenCB *cb, bool useHashCode); DISABLE_COPY(SiVaContainer); 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; } diff --git a/test/libdigidocpp_boost.cpp b/test/libdigidocpp_boost.cpp index d449ef5e4..97442cd86 100644 --- a/test/libdigidocpp_boost.cpp +++ b/test/libdigidocpp_boost.cpp @@ -602,6 +602,69 @@ BOOST_AUTO_TEST_CASE(OpenInvalidMimetypeContainer) } BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(ExtendValiditySuite) + +BOOST_AUTO_TEST_CASE(ThrowsWithNoSignatures) +{ + PKCS12Signer signer("signer1.p12", "signer1"); + auto d = Container::createPtr("extend.tmp.asice"); + BOOST_CHECK_NO_THROW(d->addDataFile("test1.txt", "text/plain")); + BOOST_CHECK_THROW(Container::extendContainerValidity(*d, &signer), Exception); +} + +BOOST_AUTO_TEST_CASE(ExtendAsiceInPlace) +{ + // Sign with TS profile, extend in place to TSA + PKCS12Signer signer("signer2.p12", "signer2"); + signer.setProfile("time-stamp"); + auto d = Container::createPtr("extend.tmp.asice"); + BOOST_CHECK_NO_THROW(d->addDataFile("test1.txt", "text/plain")); + BOOST_CHECK_NO_THROW(d->sign(&signer)); + BOOST_CHECK_NO_THROW(d->save()); + + d = Container::openPtr("extend.tmp.asice"); + BOOST_REQUIRE_EQUAL(d->signatures().size(), 1U); + + unique_ptr wrapped; + BOOST_CHECK_NO_THROW(wrapped = Container::extendContainerValidity(*d, &signer)); + BOOST_CHECK(!wrapped); // nullptr = extended in place, no new container created + BOOST_CHECK_EQUAL(d->signatures().size(), 1U); + if(!d->signatures().empty()) + BOOST_CHECK_NO_THROW(d->signatures().front()->validate()); +} + +BOOST_AUTO_TEST_CASE(ExtendAsicsInPlace) +{ + // ASiC-S timestamp token is always extended in place (re-timestamped) + PKCS12Signer signer("signer1.p12", "signer1"); + auto d = Container::openPtr("test.asics"); + BOOST_REQUIRE_EQUAL(d->signatures().size(), 1U); + + unique_ptr wrapped; + BOOST_CHECK_NO_THROW(wrapped = Container::extendContainerValidity(*d, &signer)); + BOOST_CHECK(!wrapped); // TST profile always extends in place +} + +BOOST_AUTO_TEST_CASE(WrapContainerWithExpiredSignatures) +{ + // test.asice has signatures with expired certificates, expect wrapping into new ASiC-S + PKCS12Signer signer("signer1.p12", "signer1"); + auto d = Container::openPtr("test.asice"); + BOOST_REQUIRE(!d->signatures().empty()); + + unique_ptr wrapped; + BOOST_CHECK_NO_THROW(wrapped = Container::extendContainerValidity(*d, &signer)); + BOOST_REQUIRE(wrapped); + BOOST_CHECK_EQUAL(wrapped->mediaType(), ASiCS::TYPE); + BOOST_REQUIRE_EQUAL(wrapped->dataFiles().size(), 1U); + BOOST_CHECK_EQUAL(wrapped->dataFiles().front()->fileName(), "test.asice"); + BOOST_CHECK_EQUAL(wrapped->signatures().size(), 1U); + if(!wrapped->signatures().empty()) + BOOST_CHECK_NO_THROW(wrapped->signatures().front()->validate()); +} + +BOOST_AUTO_TEST_SUITE_END() + BOOST_AUTO_TEST_SUITE(XMLTestSuite) BOOST_AUTO_TEST_CASE(XMLBomb) {