diff --git a/src/DataFile.cpp b/src/DataFile.cpp index d363f50e..85e8cb6c 100644 --- a/src/DataFile.cpp +++ b/src/DataFile.cpp @@ -23,7 +23,6 @@ #include "util/File.h" #include "util/log.h" -#include #include using namespace digidoc; @@ -98,19 +97,18 @@ DataFilePrivate::DataFilePrivate(unique_ptr &&is, string filename, stri m_size = pos < 0 ? 0 : (unsigned long)pos; } -vector DataFilePrivate::calcDigest(const string &method) const +void DataFilePrivate::digest(const Digest &digest) const { - Digest digest(method); - array buf{}; m_is->clear(); m_is->seekg(0); - while(*m_is) - { - m_is->read((char*)buf.data(), streamsize(buf.size())); - if(m_is->gcount() > 0) - digest.update(buf.data(), size_t(m_is->gcount())); - } - return digest.result(); + digest.update(*m_is); +} + +vector DataFilePrivate::calcDigest(const string &method) const +{ + Digest d(method); + digest(d); + return d.result(); } void DataFilePrivate::saveAs(const string& path) const diff --git a/src/DataFile_p.h b/src/DataFile_p.h index 2e197794..f21493ed 100644 --- a/src/DataFile_p.h +++ b/src/DataFile_p.h @@ -26,6 +26,7 @@ namespace digidoc { +class Digest; class DataFilePrivate final: public DataFile { @@ -37,6 +38,7 @@ class DataFilePrivate final: public DataFile unsigned long fileSize() const final { return m_size; } std::string mediaType() const final { return m_mediatype; } + void digest(const Digest &method) const; std::vector calcDigest(const std::string &method) const final; void saveAs(std::ostream &os) const final; void saveAs(const std::string& path) const final; diff --git a/src/SiVaContainer.cpp b/src/SiVaContainer.cpp index 0d4fc96a..5fa400f8 100644 --- a/src/SiVaContainer.cpp +++ b/src/SiVaContainer.cpp @@ -112,7 +112,7 @@ void SignatureSiVa::validate(const string &policy) const for(const Exception &exception: _exceptions) e.addCause(exception); if(!Exception::hasWarningIgnore(Exception::SignatureDigestWeak) && - (_signatureMethod == URI_RSA_SHA1 || _signatureMethod == URI_ECDSA_SHA1)) + Digest::isWeakDigest(_signatureMethod)) { Exception ex(EXCEPTION_PARAMS("Signature digest weak")); ex.setCode(Exception::SignatureDigestWeak); @@ -362,7 +362,7 @@ unique_ptr SiVaContainer::parseDDoc(bool useHashCode) if(!useHashCode) continue; Digest calc(URI_SHA1); - doc.c14n(&calc, XMLDocument::C14D_ID_1_0, dataFile); + doc.c14n(calc, XMLDocument::C14D_ID_1_0, dataFile); dataFile.setProperty("ContentType", "HASHCODE"); dataFile.setProperty("DigestType", "sha1"); dataFile.setProperty("DigestValue", to_base64(calc.result())); diff --git a/src/SignatureTST.cpp b/src/SignatureTST.cpp index 55f48cc7..c1b8cd5f 100644 --- a/src/SignatureTST.cpp +++ b/src/SignatureTST.cpp @@ -96,8 +96,8 @@ void SignatureTST::validate() const const auto *dataFile = static_cast(asicSDoc->dataFiles().front()); timestampToken->verify(dataFile->calcDigest(digestMethod)); - if(digestMethod == URI_SHA1 && - !Exception::hasWarningIgnore(Exception::ReferenceDigestWeak)) + if(!Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) && + Digest::isWeakDigest(digestMethod)) { Exception e(EXCEPTION_PARAMS("TimeStamp '%s' digest weak", digestMethod.c_str())); e.setCode(Exception::ReferenceDigestWeak); diff --git a/src/SignatureXAdES_B.cpp b/src/SignatureXAdES_B.cpp index 15745607..aca9988c 100644 --- a/src/SignatureXAdES_B.cpp +++ b/src/SignatureXAdES_B.cpp @@ -284,7 +284,7 @@ SignatureXAdES_B::SignatureXAdES_B(unsigned int id, ASiContainer *container, Sig } Digest calc(digestMethod); - signatures->c14n(&calc, canonMethod, signedProperties); + signatures->c14n(calc, canonMethod, signedProperties); addReference("#" + nr + "-SignedProperties", calc.uri(), calc.result(), REF_TYPE, canonMethod); } @@ -417,9 +417,8 @@ void SignatureXAdES_B::validate(const string &policy) const // It'll be only thrown in case we have a reason (cause). Exception exception(EXCEPTION_PARAMS("Signature validation")); - if(auto method = signatureMethod(); - !Exception::hasWarningIgnore(Exception::SignatureDigestWeak) && - (method == URI_RSA_SHA1 || method == URI_ECDSA_SHA1)) + if(!Exception::hasWarningIgnore(Exception::SignatureDigestWeak) && + Digest::isWeakDigest(signatureMethod())) { Exception e(EXCEPTION_PARAMS("Signature digest weak")); e.setCode(Exception::SignatureDigestWeak); @@ -496,9 +495,8 @@ void SignatureXAdES_B::validate(const string &policy) const continue; } - if(auto algo = (ref/DigestMethod)["Algorithm"]; - !Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) && - (algo == URI_SHA1 || algo == URI_SHA224)) + if(!Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) && + Digest::isWeakDigest((ref/DigestMethod)["Algorithm"])) { Exception e(EXCEPTION_PARAMS("Reference '%.*s' digest weak", int(uri.size()), uri.data())); e.setCode(Exception::ReferenceDigestWeak); @@ -566,7 +564,7 @@ vector SignatureXAdES_B::dataToSign() const { Digest calc(signatureMethod()); auto signedInfo = signature/"SignedInfo"; - signatures->c14n(&calc, (signedInfo/CanonicalizationMethod)["Algorithm"], signedInfo); + signatures->c14n(calc, (signedInfo/CanonicalizationMethod)["Algorithm"], signedInfo); return calc.result(); } diff --git a/src/SignatureXAdES_LTA.cpp b/src/SignatureXAdES_LTA.cpp index 965dd0e0..43b57a99 100644 --- a/src/SignatureXAdES_LTA.cpp +++ b/src/SignatureXAdES_LTA.cpp @@ -29,7 +29,6 @@ #include "util/File.h" #include -#include using namespace digidoc; using namespace digidoc::util; @@ -40,8 +39,7 @@ namespace digidoc constexpr XMLName ArchiveTimeStamp {"ArchiveTimeStamp", XADESv141_NS}; } -void SignatureXAdES_LTA::calcArchiveDigest(Digest *digest, - string_view canonicalizationMethod) const +void SignatureXAdES_LTA::calcArchiveDigest(const Digest &digest, string_view canonicalizationMethod) const { for(auto ref = signature/"SignedInfo"/"Reference"; ref; ref++) { @@ -66,16 +64,7 @@ void SignatureXAdES_LTA::calcArchiveDigest(Digest *digest, if(file == files.cend()) THROW("Filed to find reference URI in container"); - std::istream *is = static_cast(*file)->m_is.get(); - array buf{}; - is->clear(); - is->seekg(0); - while(*is) - { - is->read((char*)buf.data(), streamsize(buf.size())); - if(is->gcount() > 0) - digest->update(buf.data(), size_t(is->gcount())); - } + static_cast(*file)->digest(digest); } for(const auto *name: {"SignedInfo", "SignatureValue", "KeyInfo"}) @@ -119,7 +108,7 @@ void SignatureXAdES_LTA::extendSignatureProfile(const string &profile) return; Digest calc; auto method = canonicalizationMethod(); - calcArchiveDigest(&calc, method); + calcArchiveDigest(calc, method); TS tsa(CONF(TSUrl), calc); auto ts = unsignedSignatureProperties() + ArchiveTimeStamp; @@ -168,7 +157,7 @@ void SignatureXAdES_LTA::validate(const string &policy) const auto ts = unsignedSignatureProperties()/ArchiveTimeStamp; if(!ts) THROW("Missing ArchiveTimeStamp element"); - verifyTS(ts, exception, [this](Digest *digest, string_view canonicalizationMethod) { + verifyTS(ts, exception, [this](const Digest &digest, string_view canonicalizationMethod) { calcArchiveDigest(digest, canonicalizationMethod); }); } catch(const Exception &e) { diff --git a/src/SignatureXAdES_LTA.h b/src/SignatureXAdES_LTA.h index 9972a39d..2b47553d 100644 --- a/src/SignatureXAdES_LTA.h +++ b/src/SignatureXAdES_LTA.h @@ -37,8 +37,7 @@ class SignatureXAdES_LTA final: public SignatureXAdES_LT private: DISABLE_COPY(SignatureXAdES_LTA); - void calcArchiveDigest(Digest *digest, - std::string_view canonicalizationMethod) const; + void calcArchiveDigest(const Digest &digest, std::string_view canonicalizationMethod) const; TS tsaFromBase64() const; }; diff --git a/src/SignatureXAdES_T.cpp b/src/SignatureXAdES_T.cpp index 39ae1ef1..38ffa5ca 100644 --- a/src/SignatureXAdES_T.cpp +++ b/src/SignatureXAdES_T.cpp @@ -70,7 +70,7 @@ void SignatureXAdES_T::extendSignatureProfile(const std::string &profile) Digest calc; auto method = canonicalizationMethod(); - signatures->c14n(&calc, method, signatureValue()); + signatures->c14n(calc, method, signatureValue()); TS tsa(CONF(TSUrl), calc); auto ts = usp + "SignatureTimeStamp"; @@ -109,7 +109,7 @@ void SignatureXAdES_T::validate(const std::string &policy) const if(ts + 1) THROW("More than one SignatureTimeStamp is not supported"); - TS tsa = verifyTS(ts, exception, [this](Digest *digest, string_view canonicalizationMethod) { + TS tsa = verifyTS(ts, exception, [this](const Digest &digest, string_view canonicalizationMethod) { signatures->c14n(digest, canonicalizationMethod, signatureValue()); }); @@ -163,7 +163,7 @@ void SignatureXAdES_T::validate(const std::string &policy) const for(auto sigAndRefsTS = usp/"SigAndRefsTimeStamp"; sigAndRefsTS; sigAndRefsTS++) { - verifyTS(sigAndRefsTS, exception, [this, usp](Digest *digest, string_view canonicalizationMethod) { + verifyTS(sigAndRefsTS, exception, [this, usp](const Digest &digest, string_view canonicalizationMethod) { signatures->c14n(digest, canonicalizationMethod, signatureValue()); for(const auto *name: { "SignatureTimeStamp", @@ -195,7 +195,7 @@ XMLNode SignatureXAdES_T::unsignedSignatureProperties() const } TS SignatureXAdES_T::verifyTS(XMLNode timestamp, digidoc::Exception &exception, - std::function &&calcDigest) + std::function &&calcDigest) { auto ets = timestamp/EncapsulatedTimeStamp; if(!ets) @@ -205,11 +205,11 @@ TS SignatureXAdES_T::verifyTS(XMLNode timestamp, digidoc::Exception &exception, TS ts(ets); Digest calc(ts.digestMethod()); - calcDigest(&calc, (timestamp/CanonicalizationMethod)["Algorithm"]); + calcDigest(calc, (timestamp/CanonicalizationMethod)["Algorithm"]); ts.verify(calc.result()); - if(ts.digestMethod() == URI_SHA1 && - !Exception::hasWarningIgnore(Exception::ReferenceDigestWeak)) + if(!Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) && + Digest::isWeakDigest(ts.digestMethod())) { Exception e(EXCEPTION_PARAMS("TimeStamp '%s' digest weak", ts.digestMethod().c_str())); e.setCode(Exception::ReferenceDigestWeak); diff --git a/src/SignatureXAdES_T.h b/src/SignatureXAdES_T.h index 5d618097..a0cdcb83 100644 --- a/src/SignatureXAdES_T.h +++ b/src/SignatureXAdES_T.h @@ -47,7 +47,7 @@ class SignatureXAdES_T: public SignatureXAdES_B TS TimeStamp() const; static TS verifyTS(XMLNode timestamp, Exception &exception, - std::function &&calcDigest); + std::function &&calcDigest); private: DISABLE_COPY(SignatureXAdES_T); diff --git a/src/XMLDocument.h b/src/XMLDocument.h index cdd03f4b..2cea1de8 100644 --- a/src/XMLDocument.h +++ b/src/XMLDocument.h @@ -355,7 +355,7 @@ struct XMLDocument: public unique_xml_t, public XMLNode return doc; } - void c14n(Digest *digest, std::string_view algo, XMLNode node) + void c14n(const Digest &digest, std::string_view algo, XMLNode node) { xmlC14NMode mode = XML_C14N_1_0; int with_comments = 0; @@ -383,7 +383,7 @@ struct XMLDocument: public unique_xml_t, public XMLNode auto *digest = static_cast(context); digest->update(pcxmlChar(buffer), size_t(len)); return len; - }, nullptr, digest, nullptr), xmlOutputBufferClose); + }, nullptr, const_cast(&digest), nullptr), xmlOutputBufferClose); int size = xmlC14NExecute(get(), [](void *root, xmlNodePtr node, xmlNodePtr parent) constexpr noexcept { if(root == node) return 1; diff --git a/src/crypto/Digest.cpp b/src/crypto/Digest.cpp index 312da833..263d86cc 100644 --- a/src/crypto/Digest.cpp +++ b/src/crypto/Digest.cpp @@ -25,6 +25,8 @@ #include #include +#include + using namespace std; using namespace digidoc; @@ -44,11 +46,6 @@ Digest::Digest(string_view uri) THROW_OPENSSLEXCEPTION("Failed to initialize %.*s digest calculator", int(uri.size()), uri.data()); } -/** - * Destroys OpenSSL digest calculator. - */ -Digest::~Digest() = default; - vector Digest::addDigestInfo(vector digest, string_view uri) { switch(toMethod(uri)) @@ -116,6 +113,11 @@ bool Digest::isRsaPssUri(string_view uri) #endif } +bool Digest::isWeakDigest(string_view uri) +{ + return toMethod(uri) == NID_sha1; +} + /** * Converts digest method URI to OpenSSL method id (e.g. 'http://www.w3.org/2000/09/xmldsig#sha1' to NID_sha1, * see openssl/obj_mac.h) @@ -224,7 +226,7 @@ std::string Digest::toUri(int nid) * @throws Exception throws exception if update failed. * @see result() */ -void Digest::update(const unsigned char *data, size_t length) +void Digest::update(const unsigned char *data, size_t length) const { if(!data) THROW("Can not update digest value from NULL pointer."); @@ -232,6 +234,25 @@ void Digest::update(const unsigned char *data, size_t length) THROW_OPENSSLEXCEPTION("Failed to update %s digest value", uri().c_str()); } +/** + * Add data for digest calculation. After calling result() SHA context + * is uninitialized and this method should not be called. + * + * @param is stream to add for digest calculation. + * @throws Exception throws exception if update failed. + * @see result() + */ +void Digest::update(istream &is) const +{ + array buf{}; + while(is) + { + is.read((char*)buf.data(), streamsize(buf.size())); + if(is.gcount() > 0) + update(buf.data(), size_t(is.gcount())); + } +} + /** * Calculate message digest. SHA context will be invalid after this call. * For calculating an other digest you must create new Digest class. diff --git a/src/crypto/Digest.h b/src/crypto/Digest.h index 876b8484..d9472fc8 100644 --- a/src/crypto/Digest.h +++ b/src/crypto/Digest.h @@ -71,13 +71,14 @@ namespace digidoc { public: Digest(std::string_view uri = {}); - ~Digest(); - void update(const unsigned char *data, size_t length); + void update(const unsigned char *data, size_t length) const; + void update(std::istream &is) const; std::vector result(const std::vector &data); std::vector result() const; std::string uri() const; static bool isRsaPssUri(std::string_view uri); + static bool isWeakDigest(std::string_view uri); static std::string toRsaUri(const std::string &uri); static std::string toRsaPssUri(std::string uri); static std::string toEcUri(const std::string &uri); @@ -88,7 +89,6 @@ namespace digidoc static std::string digestInfoUri(const std::vector &digest); private: - DISABLE_COPY(Digest); std::unique_ptr d; }; diff --git a/src/crypto/TSL.cpp b/src/crypto/TSL.cpp index e4c8f599..30b07154 100644 --- a/src/crypto/TSL.cpp +++ b/src/crypto/TSL.cpp @@ -611,15 +611,9 @@ bool TSL::validateRemoteDigest(const string &url) digest = File::hexToBin(r.content); } - Digest sha(URI_RSA_SHA256); - array buf{}; + Digest sha(URI_SHA256); ifstream is(path, ifstream::binary); - while(is) - { - is.read((char*)buf.data(), streamsize(buf.size())); - if(is.gcount() > 0) - sha.update(buf.data(), size_t(is.gcount())); - } + sha.update(is); if(!digest.empty() && digest != sha.result()) THROW("TSL %s remote digest does not match local. TSL might be outdated", territory().data()); diff --git a/test/libdigidocpp_boost.cpp b/test/libdigidocpp_boost.cpp index 13da2fc8..771fd68d 100644 --- a/test/libdigidocpp_boost.cpp +++ b/test/libdigidocpp_boost.cpp @@ -332,7 +332,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(signature, Doc, DocTypes) BOOST_CHECK_EQUAL(d->signatures().size(), 2U); if(s3) { - BOOST_CHECK_EQUAL(s3->signatureMethod(), URI_ECDSA_SHA384); + BOOST_CHECK_EQUAL(s3->signatureMethod(), "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384"); BOOST_CHECK_EQUAL(s3->signingCertificate(), signer3.cert()); BOOST_CHECK_NO_THROW(s3->validate()); } @@ -381,7 +381,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(signature, Doc, DocTypes) // RSA PSS tests d = Container::createPtr(Doc::EXT + ".tmp"); BOOST_CHECK_NO_THROW(d->addDataFile("test1.txt", "text/plain")); - signer1.setMethod(URI_RSA_PSS_SHA256); + signer1.setMethod("http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1"); BOOST_CHECK_NO_THROW(s = d->sign(&signer1)); BOOST_CHECK_NO_THROW(s->validate()); BOOST_CHECK_EQUAL(s->signatureMethod(), signer1.method());