diff --git a/examples/DigiDocCSharp/Program.cs b/examples/DigiDocCSharp/Program.cs index c5f25cfa..4da521af 100644 --- a/examples/DigiDocCSharp/Program.cs +++ b/examples/DigiDocCSharp/Program.cs @@ -208,6 +208,11 @@ private static void Verify(string file) Console.WriteLine("Time: " + s.trustedSigningTime()); Console.WriteLine("Cert: " + s.signingCertificate().Subject); Console.WriteLine("TimeStamp: " + s.TimeStampCertificate().Subject); + foreach (TSAInfo tsaInfo in s.ArchiveTimeStamps()) + { + Console.WriteLine("Archive Time: " + tsaInfo.time); + Console.WriteLine("Archive Cert: " + tsaInfo.cert.Subject); + } s.validate(); Console.WriteLine("Signature is valid"); diff --git a/examples/java/src/main/java/ee/ria/libdigidocpp/libdigidocpp.java b/examples/java/src/main/java/ee/ria/libdigidocpp/libdigidocpp.java index bf605022..b7882046 100644 --- a/examples/java/src/main/java/ee/ria/libdigidocpp/libdigidocpp.java +++ b/examples/java/src/main/java/ee/ria/libdigidocpp/libdigidocpp.java @@ -151,6 +151,10 @@ static void verify(String file) { System.out.println("Time: " + signature.trustedSigningTime()); System.out.println("Cert: " + signature.signingCertificate().getSubjectDN().toString()); System.out.println("TimeStamp Cert: " + signature.TimeStampCertificate().getSubjectDN().toString()); + for(TSAInfo tsaInfo : signature.ArchiveTimeStamps()) { + System.out.println("Archive Time: " + tsaInfo.getTime()); + System.out.println("Archive Cert: " + tsaInfo.getCert().getSubjectDN().toString()); + } try { diff --git a/libdigidocpp.i b/libdigidocpp.i index ef2e5884..10e9ec33 100644 --- a/libdigidocpp.i +++ b/libdigidocpp.i @@ -101,15 +101,17 @@ static std::vector* SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *je %{ $1 = SWIG_JavaArrayToVectorUnsignedChar(jenv, $input); %} %typemap(out, fragment="SWIG_VectorUnsignedCharToJavaArray") std::vector, digidoc::X509Cert %{ $result = SWIG_VectorUnsignedCharToJavaArray(jenv, $1); %} -%typemap(jtype) std::vector, digidoc::X509Cert "byte[]" +%typemap(out, fragment="SWIG_VectorUnsignedCharToJavaArray") digidoc::X509Cert * +%{ $result = SWIG_VectorUnsignedCharToJavaArray(jenv, *$1); %} +%typemap(jtype) std::vector, digidoc::X509Cert, digidoc::X509Cert * "byte[]" %typemap(jstype) std::vector "byte[]" -%typemap(jstype) digidoc::X509Cert "java.security.cert.X509Certificate" -%typemap(jni) std::vector, digidoc::X509Cert "jbyteArray" +%typemap(jstype) digidoc::X509Cert, digidoc::X509Cert* "java.security.cert.X509Certificate" +%typemap(jni) std::vector, digidoc::X509Cert, digidoc::X509Cert * "jbyteArray" %typemap(javain) std::vector, digidoc::X509Cert "$javainput" %typemap(javaout) std::vector { return $jnicall; } -%typemap(javaout, throws="java.security.cert.CertificateException, java.io.IOException") digidoc::X509Cert { +%typemap(javaout, throws="java.security.cert.CertificateException, java.io.IOException") digidoc::X509Cert, digidoc::X509Cert * { byte[] der = $jnicall; java.security.cert.CertificateFactory cf = java.security.cert.CertificateFactory.getInstance("X509"); try (java.io.ByteArrayInputStream is = new java.io.ByteArrayInputStream(der)) { @@ -119,7 +121,7 @@ static std::vector* SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *je #elif defined(SWIGCSHARP) %typemap(cstype) std::vector "byte[]" -%typemap(cstype) digidoc::X509Cert "System.Security.Cryptography.X509Certificates.X509Certificate2" +%typemap(cstype) digidoc::X509Cert, digidoc::X509Cert* "System.Security.Cryptography.X509Certificates.X509Certificate2" %typemap(csin, pre= " global::System.IntPtr cPtr$csinput = digidocPINVOKE.ByteVector_to($csinput, $csinput.Length); var handleRef$csinput = new global::System.Runtime.InteropServices.HandleRef(this, cPtr$csinput);" ) std::vector "handleRef$csinput" @@ -137,6 +139,14 @@ static std::vector* SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *je $modulePINVOKE.ByteVector_free(cPtr); return new System.Security.Cryptography.X509Certificates.X509Certificate2(der); } +%typemap(csvarout, excode=SWIGEXCODE2) digidoc::X509Cert * %{ + get { + global::System.IntPtr cPtr = $imcall;$excode + byte[] der = new byte[$modulePINVOKE.ByteVector_size(cPtr)]; + global::System.Runtime.InteropServices.Marshal.Copy($modulePINVOKE.ByteVector_data(cPtr), der, 0, der.Length); + $modulePINVOKE.ByteVector_free(cPtr); + return new System.Security.Cryptography.X509Certificates.X509Certificate2(der); + } %} %typemap(out) std::vector %{ $result = new std::vector(std::move($1)); %} %typemap(out) digidoc::X509Cert %{ $result = new std::vector($1); %} @@ -159,6 +169,10 @@ static std::vector* SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *je std::vector temp = $1; $result = PyBytes_FromStringAndSize((const char*)temp.data(), temp.size()); } +%typemap(out) digidoc::X509Cert * { + std::vector temp = *$1; + $result = PyBytes_FromStringAndSize((const char*)temp.data(), temp.size()); +} #endif %typemap(freearg) std::vector %{ delete $1; %} @@ -210,6 +224,9 @@ static std::vector* SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *je %newobject digidoc::Container::open; %newobject digidoc::Container::create; +%immutable digidoc::TSAInfo::cert; +%immutable digidoc::TSAInfo::time; + %feature("director") digidoc::ContainerOpenCB; %typemap(javacode) digidoc::Conf %{ @@ -267,6 +284,7 @@ def transfer(self): %template(StringMap) std::map; %template(DataFiles) std::vector; %template(Signatures) std::vector; +%template(TSAInfos) std::vector; %extend digidoc::Container { static digidoc::Container* open(const std::string &path, digidoc::ContainerOpenCB *cb) diff --git a/src/Signature.cpp b/src/Signature.cpp index abe30ed2..1b13ad5d 100644 --- a/src/Signature.cpp +++ b/src/Signature.cpp @@ -214,6 +214,11 @@ X509Cert Signature::ArchiveTimeStampCertificate() const { return X509Cert(); } */ string Signature::ArchiveTimeStampTime() const { return {}; } +/** + * Returns signature Archive TimeStampTokens. + */ +vector Signature::ArchiveTimeStamps() const { return {}; } + struct Signature::Validator::Private { Status result = Valid; diff --git a/src/Signature.h b/src/Signature.h index c078e1e0..58e74e2d 100644 --- a/src/Signature.h +++ b/src/Signature.h @@ -21,12 +21,17 @@ #include "Exception.h" -#include -#include +#include "crypto/X509Cert.h" namespace digidoc { class X509Cert; + + struct TSAInfo { + X509Cert cert; + std::string time; + }; + class DIGIDOCPP_EXPORT Signature { public: @@ -85,18 +90,18 @@ namespace digidoc virtual std::string countryName() const; virtual std::vector signerRoles() const; - //TM profile properties + // TM profile properties virtual std::string OCSPProducedAt() const; virtual X509Cert OCSPCertificate() const; DIGIDOCPP_DEPRECATED virtual std::vector OCSPNonce() const; - //TS profile properties + // TS profile properties virtual X509Cert TimeStampCertificate() const; virtual std::string TimeStampTime() const; - //TSA profile properties - virtual X509Cert ArchiveTimeStampCertificate() const; - virtual std::string ArchiveTimeStampTime() const; + // TSA profile properties + DIGIDOCPP_DEPRECATED virtual X509Cert ArchiveTimeStampCertificate() const; + DIGIDOCPP_DEPRECATED virtual std::string ArchiveTimeStampTime() const; // Xades properties virtual std::string streetAddress() const; @@ -110,6 +115,9 @@ namespace digidoc // Other virtual std::vector messageImprint() const; + //TSA profile properties + virtual std::vector ArchiveTimeStamps() const; + protected: Signature(); diff --git a/src/SignatureXAdES_B.cpp b/src/SignatureXAdES_B.cpp index aca9988c..d5291027 100644 --- a/src/SignatureXAdES_B.cpp +++ b/src/SignatureXAdES_B.cpp @@ -343,6 +343,9 @@ SignatureXAdES_B::SignatureXAdES_B(const std::shared_ptr &signatures "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 43b57a99..92ec3ad0 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,60 @@ 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(const string &profile) { - SignatureXAdES_LT::extendSignatureProfile(profile); + if(SignatureXAdES_LTA::profile().find(ASiC_E::ASIC_TS_PROFILE) == string::npos) + SignatureXAdES_LT::extendSignatureProfile(profile); if(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(CONF(TSUrl), calc); 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 +X509Cert SignatureXAdES_LTA::ArchiveTimeStampCertificate() const { - try { - return {unsignedSignatureProperties()/ArchiveTimeStamp/EncapsulatedTimeStamp}; - } catch(const Exception &) {} - return {}; + if(auto list = ArchiveTimeStamps(); !list.empty()) + return list.back().cert; + return X509Cert(); } -X509Cert SignatureXAdES_LTA::ArchiveTimeStampCertificate() const +string SignatureXAdES_LTA::ArchiveTimeStampTime() const { - return tsaFromBase64().cert(); + if(auto list = ArchiveTimeStamps(); !list.empty()) + return list.back().time; + return {}; } -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 +152,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); } diff --git a/src/SignatureXAdES_LTA.h b/src/SignatureXAdES_LTA.h index 2b47553d..c73d183f 100644 --- a/src/SignatureXAdES_LTA.h +++ b/src/SignatureXAdES_LTA.h @@ -31,14 +31,14 @@ class SignatureXAdES_LTA final: public 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(const std::string &profile) 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 2cea1de8..1ea44485 100644 --- a/src/XMLDocument.h +++ b/src/XMLDocument.h @@ -159,6 +159,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 857a9e18..aab8f228 100644 --- a/src/digidoc-tool.1.cmake +++ b/src/digidoc-tool.1.cmake @@ -68,6 +68,13 @@ Command sign: --tsurl - option to change TS URL (default http://demo.sk.ee/tsa) --dontValidate - Don't validate container on signature creation +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 73183007..d028a10d 100644 --- a/src/digidoc-tool.cpp +++ b/src/digidoc-tool.cpp @@ -252,6 +252,18 @@ string ConsolePinSigner::pin(const X509Cert &certificate) const return result; } +struct value: string_view { + constexpr value(string_view arg, string_view param) noexcept + : string_view(arg.size() > param.size() && arg.compare(0, param.size(), param) == 0 ? + arg.substr(param.size()) : string_view{}) + {} + + constexpr operator bool() const noexcept + { + return !empty(); + } +}; + class ToolConfig final: public XmlConfCurrent { public: @@ -367,6 +379,12 @@ static int printUsage(const char *executable) << " --rsapss - Use RSA PSS padding" << endl << " --tsurl - option to change TS URL (default " << CONF(TSUrl) << ")" << endl << " --dontValidate - Don't validate container on signature creation" << 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 @@ -384,40 +402,39 @@ ToolConfig::ToolConfig(int argc, char *argv[]) for(int i = 2; i < argc; i++) { string_view arg(argv[i]); - if(arg.find("--profile=") == 0) - profile = arg.substr(10); - else if(arg.find("--file=") == 0) + if(value v{arg, "--profile="}) profile = v; + else if(value v{arg, "--file="}) { - string_view arg2(i+1 < argc ? argv[i+1] : string_view()); - files.emplace(arg.substr(7), - arg2.find("--mime=") == 0 ? toUTF8(arg2.substr(7)) : "application/octet-stream"); + value mime(i+1 < argc ? argv[i+1] : string_view(), "--mime="); + files.emplace(v, + mime ? toUTF8(mime) : "application/octet-stream"); } #ifdef _WIN32 else if(arg == "--cng") cng = true; else if(arg == "--selectFirst") selectFirst = true; - else if(arg.find("--thumbprint=") == 0) thumbprint = File::hexToBin(arg.substr(arg.find('=') + 1)); + else if(value v{arg, "--thumbprint="}) thumbprint = File::hexToBin(v); #endif else if(arg.find("--pkcs11") == 0) { cng = false; - if(arg.find('=') != string::npos) - pkcs11 = toUTF8(arg.substr(arg.find('=') + 1)); + if(value v{arg, "--pkcs11="}) + pkcs11 = toUTF8(v); } - else if(arg.find("--pkcs12=") == 0) + else if(value v{arg, "--pkcs12="}) { cng = false; - pkcs12 = toUTF8(arg.substr(9)); + pkcs12 = toUTF8(v); } else if(arg == "--dontValidate") dontValidate = true; else if(arg == "--XAdESEN") XAdESEN = true; - else if(arg.find("--pin=") == 0) pin = arg.substr(6); - else if(arg.find("--cert=") == 0) cert = toUTF8(arg.substr(7)); - else if(arg.find("--city=") == 0) city = toUTF8(arg.substr(7)); - else if(arg.find("--street=") == 0) street = toUTF8(arg.substr(9)); - else if(arg.find("--state=") == 0) state = toUTF8(arg.substr(8)); - else if(arg.find("--postalCode=") == 0) postalCode = toUTF8(arg.substr(13)); - else if(arg.find("--country=") == 0) country = toUTF8(arg.substr(10)); - else if(arg.find("--role=") == 0) roles.push_back(toUTF8(arg.substr(7))); + else if(value v{arg, "--pin="}) pin = v; + else if(value v{arg, "--cert="}) cert = toUTF8(v); + else if(value v{arg, "--city="}) city = toUTF8(v); + else if(value v{arg, "--street="}) street = toUTF8(v); + else if(value v{arg, "--state="}) state = toUTF8(v); + else if(value v{arg, "--postalCode="}) postalCode = toUTF8(v); + else if(value v{arg, "--country="}) country = toUTF8(v); + else if(value v{arg, "--role="}) roles.push_back(toUTF8(v)); else if(arg == "--sha224") uri = URI_SHA224; else if(arg == "--sha256") uri = URI_SHA256; else if(arg == "--sha384") uri = URI_SHA384; @@ -432,14 +449,14 @@ ToolConfig::ToolConfig(int argc, char *argv[]) else if(arg == "--sigpsssha512") { siguri = URI_SHA512; rsaPss = true; } else if(arg == "--rsapkcs15") rsaPss = false; else if(arg == "--rsapss") rsaPss = true; - else if(arg.find("--tsurl") == 0) tsurl = arg.substr(8); - else if(arg.find("--tslurl=") == 0) tslurl = arg.substr(9); - else if(arg.find("--tslcert=") == 0) tslcerts = vector{ X509Cert(toUTF8(arg.substr(10))) }; + else if(value v{arg, "--tsurl="}) tsurl = v; + else if(value v{arg, "--tslurl="}) tslurl = v; + else if(value v{arg, "--tslcert="}) tslcerts = vector{ X509Cert(toUTF8(v)) }; else if(arg == "--TSLAllowExpired") expired = true; else if(arg == "--dontsign") doSign = false; else if(arg == "--nocolor") RED = GREEN = YELLOW = RESET = {}; - else if(arg.find("--loglevel=") == 0) _logLevel = atoi(arg.substr(11).data()); - else if(arg.find("--logfile=") == 0) _logFile = toUTF8(arg.substr(10)); + else if(value v{arg, "--loglevel="}) _logLevel = atoi(v.data()); + else if(value v{arg, "--logfile="}) _logFile = toUTF8(v); else path = toUTF8(arg); } } @@ -471,7 +488,7 @@ unique_ptr ToolConfig::getSigner(bool getwebsigner) const #ifdef _WIN32 else if(cng) { - unique_ptr win = make_unique(pin, selectFirst); + auto win = make_unique(pin, selectFirst); win->setThumbprint(thumbprint); signer = std::move(win); } @@ -489,6 +506,12 @@ unique_ptr ToolConfig::getSigner(bool getwebsigner) const return signer; } +/** + * Validate signature. + * + * @param signature Signature to validated + * @return EXIT_FAILURE (1) - failure, EXIT_SUCCESS (0) - success + */ static int validateSignature(const Signature *s, ToolConfig::Warning warning = ToolConfig::WWarning) { int returnCode = EXIT_SUCCESS; @@ -549,20 +572,20 @@ static int open(int argc, char* argv[]) // Parse command line arguments. for(int i = 2; i < argc; i++) { - string arg(ToolConfig::toUTF8(argv[i])); + string_view arg(argv[i]); if(arg == "--list") continue; - if(arg.find("--warnings=") == 0) + if(value v{arg, "--warnings="}) { - if(arg.substr(11, 6) == "ignore") reportwarnings = ToolConfig::WIgnore; - if(arg.substr(11, 5) == "error") reportwarnings = ToolConfig::WError; + if(v == "ignore") reportwarnings = ToolConfig::WIgnore; + if(v == "error") reportwarnings = ToolConfig::WError; } else if(arg.find("--extractAll") == 0) { extractPath = fs::current_path(); if(auto pos = arg.find('='); pos != string::npos) { - fs::path newPath = fs::u8path(arg.substr(pos + 1)); + fs::path newPath = fs::path(arg.substr(pos + 1)); extractPath = newPath.is_relative() ? extractPath / newPath : std::move(newPath); } if(!fs::is_directory(extractPath)) @@ -570,12 +593,12 @@ static int open(int argc, char* argv[]) } else if(arg == "--validateOnExtract") validateOnExtract = true; - else if(arg.find("--policy=") == 0) - policy = arg.substr(9); - else if(arg.find("--offline") == 0) + else if(value v{arg, "--policy="}) + policy = v; + else if(arg == "--offline") cb.online = false; else - path = std::move(arg); + path = ToolConfig::toUTF8(arg); } if(path.empty()) @@ -664,15 +687,69 @@ static int open(int argc, char* argv[]) << " OCSP Responder: " << s->OCSPCertificate() << endl << " Message imprint (" << msgImprint.size() << "): " << msgImprint << endl << " TS: " << s->TimeStampCertificate() << endl - << " TS time: " << s->TimeStampTime() << endl - << " TSA: " << s->ArchiveTimeStampCertificate() << endl - << " TSA time: " << s->ArchiveTimeStampTime() << endl; + << " TS time: " << s->TimeStampTime() << endl; + for(const auto &tsaInfo: s->ArchiveTimeStamps()) + { + cout + << " TSA: " << tsaInfo.cert << '\n' + << " TSA time: " << tsaInfo.time << '\n'; + } } if(returnCode == EXIT_SUCCESS && !extractPath.empty()) return extractFiles(); return returnCode; } +/** + * Extend signatures in container. + * + * @param argc number of command line arguments. + * @param argv command line arguments. + * @return EXIT_FAILURE (1) - failure, EXIT_SUCCESS (0) - success + */ +static int extend(int argc, char *argv[]) +{ + vector signatures; + bool dontValidate = false; + string path, profile; + for(int i = 2; i < argc; i++) + { + string_view arg(argv[i]); + if(value v{arg, "--profile="}) + profile = v; + else if(value v{arg, "--signature="}) + signatures.push_back(unsigned(atoi(v.data()))); + else if(arg == "--dontValidate") + dontValidate = true; + else + path = ToolConfig::toUTF8(arg); + } + + if(path.empty()) + return printUsage(argv[0]); + + unique_ptr doc; + try { + doc = Container::openPtr(path); + } catch(const Exception &e) { + cout << "Failed to parse container" << endl; + cout << " Exception:" << endl << e; + return EXIT_FAILURE; + } + + for(unsigned int i : signatures) + { + cout << " Extending signature " << i << " to " << profile << endl; + Signature *s = doc->signatures().at(i); + s->extendSignatureProfile(profile); + if(!dontValidate) + validateSignature(s); + } + + doc->save(); + return EXIT_SUCCESS; +} + /** * Remove items from container. * @@ -686,13 +763,13 @@ static int remove(int argc, char *argv[]) string path; for(int i = 2; i < argc; i++) { - string arg(ToolConfig::toUTF8(argv[i])); - if(arg.find("--document=") == 0) - documents.push_back(unsigned(stoi(arg.substr(11)))); - else if(arg.find("--signature=") == 0) - signatures.push_back(unsigned(stoi(arg.substr(12)))); + string_view arg(argv[i]); + if(value v{arg, "--document="}) + documents.push_back(unsigned(atoi(v.data()))); + else if(value v{arg, "--signature="}) + signatures.push_back(unsigned(atoi(v.data()))); else - path = std::move(arg); + path = ToolConfig::toUTF8(arg); } if(path.empty()) @@ -764,18 +841,7 @@ static int add(const ToolConfig &p, const char *program) static int signContainer(Container *doc, const unique_ptr &signer, bool dontValidate = false) { if(Signature *signature = doc->sign(signer.get())) - { - if(dontValidate) - return EXIT_SUCCESS; - try { - signature->validate(); - cout << " Validation: " << ToolConfig::GREEN << "OK" << ToolConfig::RESET << endl; - return EXIT_SUCCESS; - } catch(const Exception &e) { - cout << " Validation: " << ToolConfig::RED << "FAILED" << ToolConfig::RESET << endl; - cout << " Exception:" << endl << e; - } - } + return dontValidate ? EXIT_SUCCESS : validateSignature(signature); return EXIT_FAILURE; } @@ -1027,6 +1093,8 @@ int main(int argc, char *argv[]) try return remove(argc, argv); if(command == "sign") return sign(*conf, argv[0]); + if(command == "extend") + return extend(argc, argv); if(command == "websign") return websign(*conf, argv[0]); if(command == "tsl") diff --git a/test/libdigidocpp_boost.cpp b/test/libdigidocpp_boost.cpp index 771fd68d..b1e0613f 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.profile())); + 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);