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 8a116fbe5..8e19cff39 100644 --- a/src/SignatureTST.cpp +++ b/src/SignatureTST.cpp @@ -111,20 +111,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 4fcdb9694..6f1723853 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..33d341a8b 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++) { @@ -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 @@ -154,12 +135,12 @@ void SignatureXAdES_LTA::validate(const string &policy) const } try { - auto ts = unsignedSignatureProperties()/ArchiveTimeStamp; - if(!ts) - THROW("Missing ArchiveTimeStamp element"); - verifyTS(ts, exception, [this](const Digest &digest, string_view canonicalizationMethod) { - calcArchiveDigest(digest, canonicalizationMethod); - }); + for(auto ts = unsignedSignatureProperties()/ArchiveTimeStamp; 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 f9e7b94f9..c81cf03e4 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/src/util/File.cpp b/src/util/File.cpp index 2142f6c5a..68b814258 100644 --- a/src/util/File.cpp +++ b/src/util/File.cpp @@ -346,22 +346,25 @@ string File::toUriPath(const string &path) return dst.str(); } +constexpr bool fromHexChar(auto pos, auto end, auto &value) +{ + return distance(pos, end) >= 2 && from_chars(&*pos, &*(pos + 2), value, 16).ec == std::errc{}; +} + string File::fromUriPath(string_view path) { string ret; ret.reserve(path.size()); - char data[] = "00"; + uint8_t value = 0; for(auto i = path.begin(); i != path.end(); ++i) { - if(*i == '%' && (distance(i, path.end()) > 2) && isxdigit(*(i+1)) && isxdigit(*(i+2))) + if(*i == '%' && fromHexChar(i + 1, path.end(), value)) { - data[0] = *(++i); - data[1] = *(++i); - ret += static_cast(strtoul(data, nullptr, 16)); + ret += static_cast(value); + i += 2; } - else { + else ret += *i; - } } return ret; } @@ -370,11 +373,8 @@ vector File::hexToBin(string_view in) { vector out; out.reserve(in.size() / 2); - uint8_t result{}; - for(size_t pos{}; pos + 1 < in.size(); pos += 2) - { - if(auto i = next(in.data(), pos); from_chars(i, i + 2, result, 16).ec == std::errc{}) - out.push_back(result); - } + uint8_t value = 0; + for(auto i = in.begin(); fromHexChar(i, in.end(), value); i += 2) + out.emplace_back(value); return out; } diff --git a/test/libdigidocpp_boost.cpp b/test/libdigidocpp_boost.cpp index 2cafe7a79..d449ef5e4 100644 --- a/test/libdigidocpp_boost.cpp +++ b/test/libdigidocpp_boost.cpp @@ -350,8 +350,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); @@ -500,6 +503,16 @@ BOOST_AUTO_TEST_CASE(FromUriPathPreservesTrailingPercentageSign) BOOST_CHECK_EQUAL(expectedDecodedStr, result); } + +BOOST_AUTO_TEST_CASE(HexToBinConvertsHexEncodedStringToBinaryData) +{ + const string asciiEncodedStr = "3031323334"; + const vector expectedDecodedData { '0', '1', '2', '3', '4' }; + + auto result = util::File::hexToBin(asciiEncodedStr); + + BOOST_CHECK_EQUAL(expectedDecodedData, result); +} BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(ASiCSTestSuite)