From 823bfaf5d8199f7b01677800de457f7604c29f17 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Mon, 7 Sep 2020 13:03:40 +0300 Subject: [PATCH] Implement SiVa V3 changes (#354) IB-6010 Signed-off-by: Raul Metsma --- src/SiVaContainer.cpp | 107 ++++++++++++++++++++++++++++++++---------- src/SiVaContainer.h | 33 ++++++++++--- 2 files changed, 110 insertions(+), 30 deletions(-) diff --git a/src/SiVaContainer.cpp b/src/SiVaContainer.cpp index 39a83e002..a3fb433c9 100644 --- a/src/SiVaContainer.cpp +++ b/src/SiVaContainer.cpp @@ -31,6 +31,7 @@ #include "crypto/Connect.h" #include "crypto/Digest.h" #include "util/File.h" +#include "xml/xml.hxx" #include "xml/SecureDOMParser.h" #include "jsonxx.cc" @@ -61,7 +62,7 @@ vector SignatureSiVa::dataToSign() const THROW("Not implemented."); } -void SignatureSiVa::setSignatureValue(const vector &) +void SignatureSiVa::setSignatureValue(const vector & /*signatureValue*/) { THROW("Not implemented."); } @@ -76,36 +77,36 @@ void SignatureSiVa::validate(const string &policy) const static const set QES = { "QESIG", "QES", "QESEAL", "ADESEAL_QC", "ADESEAL" }; // Special treamtent for E-Seals Exception e(EXCEPTION_PARAMS("Signature validation")); + for(const Exception &exception: _exceptions) + e.addCause(exception); if(_indication == "TOTAL-PASSED") { - if(QES.find(_signatureLevel) != QES.cend() || _signatureLevel.empty() || policy == POLv1) + if(QES.count(_signatureLevel) || _signatureLevel.empty() || policy == POLv1) + { + if(!e.causes().empty()) + throw e; return; + } Exception ex(EXCEPTION_PARAMS("Signing certificate does not meet Qualification requirements")); ex.setCode(Exception::CertificateIssuerMissing); e.addCause(ex); } - else - { - for(const Exception &error: _errors) - e.addCause(error); - } if(!e.causes().empty()) throw e; } -SiVaContainer::SiVaContainer(const string &path, const string &ext) +SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHashCode) : d(new Private) { - DEBUG("SiVaContainer::SiVaContainer(%s, %s)", path.c_str(), ext.c_str()); + DEBUG("SiVaContainer::SiVaContainer(%s, %s, %d)", path.c_str(), ext.c_str(), useHashCode); unique_ptr ifs(new ifstream(File::encodeName(path).c_str(), ifstream::binary)); istream *is = ifs.get(); - unique_ptr ddoc; if(ext == "DDOC") { d->mediaType = "application/x-ddoc"; - ddoc.reset(parseDDoc(ifs.get())); - is = ddoc.get(); + ifs.reset(parseDDoc(move(ifs), useHashCode)); + is = ifs.get(); } else { @@ -131,12 +132,12 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext) } string url = CONF(verifyServiceUri); - jsonxx::Object reqObj = jsonxx::Object() - <<"filename" << File::fileName(path) + jsonxx::Object reqObj = jsonxx::Object() + << "filename" << File::fileName(path) << "document" << b64 << "signaturePolicy" << "POLv4"; string req = reqObj.json(); - Connect::Result r = Connect(url, "POST", 0, string(), CONF(verifyServiceCert)).exec({ + Connect::Result r = Connect(url, "POST", 0, {}, CONF(verifyServiceCert)).exec({ {"Content-Type", "application/json;charset=UTF-8"} }, (const unsigned char*)req.c_str(), req.size()); @@ -160,22 +161,70 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext) jsonxx::Object report = result.get("validationReport"); jsonxx::Object base = report.get("validationConclusion"); - for(const jsonxx::Value *obj: base.get("signatures", jsonxx::Array()).values()) + for(const jsonxx::Value *obj: base.get("signatures", {}).values()) { SignatureSiVa *s = new SignatureSiVa; jsonxx::Object signature = obj->get(); s->_id = signature.get("id"); s->_signingTime = signature.get("claimedSigningTime"); - s->_bestTime = signature.get("info", jsonxx::Object()).get("bestSignatureTime", string()); + s->_bestTime = signature.get("info", {}).get("bestSignatureTime", {}); s->_profile = signature.get("signatureFormat"); s->_indication = signature.get("indication"); - s->_subIndication = signature.get("subIndication", string()); + s->_subIndication = signature.get("subIndication", {}); s->_signedBy = signature.get("signedBy"); - s->_signatureLevel = signature.get("signatureLevel", string()); - for(const jsonxx::Value *error: signature.get("errors", jsonxx::Array()).values()) + s->_signatureMethod = signature.get("signatureMethod", {}); + s->_signatureLevel = signature.get("signatureLevel", {}); + jsonxx::Object info = signature.get("info", {}); + if(info.has("timeAssertionMessageImprint")) + { + string base64 = info.get("timeAssertionMessageImprint"); + XMLSize_t size = 0; + XMLByte *message = Base64::decode((const XMLByte*)base64.c_str(), &size); + s->_messageImprint.assign(message, message + size); + delete message; + } + for(const jsonxx::Value *signerRole: info.get("signerRole", {}).values()) + s->_signerRoles.push_back(signerRole->get().get("claimedRole")); + jsonxx::Object signatureProductionPlace = info.get("signatureProductionPlace", {}); + s->_city = signatureProductionPlace.get("city", {}); + s->_stateOrProvince = signatureProductionPlace.get("stateOrProvince", {}); + s->_postalCode = signatureProductionPlace.get("postalCode", {}); + s->_country = signatureProductionPlace.get("countryName", {}); + for(const jsonxx::Value *certificates: signature.get("certificates", {}).values()) + { + jsonxx::Object certificate = certificates->get(); + string content = certificate.get("content"); + XMLSize_t size = 0; + XMLByte *der = Base64::decode((const XMLByte*)content.c_str(), &size); + if(certificate.get("type") == "SIGNING") + s->_signingCertificate = X509Cert(der, size, X509Cert::Der); + if(certificate.get("type") == "REVOCATION") + s->_ocspCertificate = X509Cert(der, size, X509Cert::Der); + if(certificate.get("type") == "SIGNATURE_TIMESTAMP") + s->_tsCertificate = X509Cert(der, size, X509Cert::Der); + if(certificate.get("type") == "ARCHIVE_TIMESTAMP") + s->_tsaCertificate = X509Cert(der, size, X509Cert::Der); + delete der; + } + for(const jsonxx::Value *error: signature.get("errors", {}).values()) { string message = error->get().get("content"); - s->_errors.emplace_back(Exception(EXCEPTION_PARAMS(message.c_str()))); + if(message.find("Bad digest for DataFile") == 0 && useHashCode) + THROW(message.c_str()); + s->_exceptions.emplace_back(EXCEPTION_PARAMS(message.c_str())); + } + for(const jsonxx::Value *warning: signature.get("warnings", {}).values()) + { + string message = warning->get().get("content"); + Exception ex(EXCEPTION_PARAMS(message.c_str())); + if(message == "X509IssuerName has none or invalid namespace: null" || + message == "X509SerialNumber has none or invalid namespace: null") + ex.setCode(Exception::IssuerNameSpaceWarning); + else if(message.find("Bad digest for DataFile") == 0) + ex.setCode(Exception::DataFileNameSpaceWarning); + else if(message == "Old and unsupported format: SK-XML version: 1.0") + continue; + WARN("%s", message.c_str()); } d->signatures.push_back(s); } @@ -205,7 +254,7 @@ void SiVaContainer::addAdESSignature(istream & /*signature*/) THROW("Not supported."); } -unique_ptr SiVaContainer::createInternal(const string & /*unused*/) +unique_ptr SiVaContainer::createInternal(const string & /*path*/) { return {}; } @@ -225,10 +274,18 @@ unique_ptr SiVaContainer::openInternal(const string &path) static const set supported = {"PDF", "DDOC"}; string ext = File::fileExtension(path); transform(ext.begin(), ext.end(), ext.begin(), ::toupper); - return unique_ptr(supported.find(ext) != supported.cend() ? new SiVaContainer(path, ext) : nullptr); + if(!supported.count(ext)) + return {}; + try { + return unique_ptr(new SiVaContainer(path, ext, true)); + } catch(const Exception &e) { + if(e.msg().find("Bad digest for DataFile") == 0) + return unique_ptr(new SiVaContainer(path, ext, false)); + throw; + } } -stringstream* SiVaContainer::parseDDoc(istream *is) +stringstream* SiVaContainer::parseDDoc(std::unique_ptr is, bool useHashCode) { try { @@ -252,6 +309,8 @@ stringstream* SiVaContainer::parseDDoc(istream *is) delete data; } + if(!useHashCode) + continue; Digest calc(URI_SHA1); SecureDOMParser::calcDigestOnNode(&calc, "http://www.w3.org/TR/2001/REC-xml-c14n-20010315", dom.get(), item); vector digest = calc.result(); diff --git a/src/SiVaContainer.h b/src/SiVaContainer.h index 0cdb2f350..5ba26ca37 100644 --- a/src/SiVaContainer.h +++ b/src/SiVaContainer.h @@ -34,9 +34,9 @@ class SignatureSiVa: public Signature std::string id() const override { return _id; } std::string claimedSigningTime() const override { return _signingTime; } std::string trustedSigningTime() const override { return _bestTime.empty() ? _signingTime : _bestTime; } - X509Cert signingCertificate() const override { return X509Cert(); } + X509Cert signingCertificate() const override { return _signingCertificate; } std::string signedBy() const override { return _signedBy; } - std::string signatureMethod() const override { return std::string(); } + std::string signatureMethod() const override { return _signatureMethod; } void validate() const override; void validate(const std::string &policy) const override; std::vector dataToSign() const override; @@ -44,13 +44,34 @@ class SignatureSiVa: public Signature // Xades properties std::string profile() const override { return _profile; } + std::string city() const override { return _city; }; + std::string stateOrProvince() const override { return _stateOrProvince; }; + std::string postalCode() const override { return _postalCode; }; + std::string countryName() const override { return _country; }; + std::vector signerRoles() const override { return _signerRoles; }; + + //TM profile properties + X509Cert OCSPCertificate() const override { return _ocspCertificate; }; + + //TS profile properties + X509Cert TimeStampCertificate() const override { return _tsCertificate; }; + + //TSA profile properties + X509Cert ArchiveTimeStampCertificate() const override { return _tsaCertificate; }; + + // Other + std::vector messageImprint() const override { return _messageImprint; }; private: SignatureSiVa() = default; DISABLE_COPY(SignatureSiVa); - std::string _id, _profile, _signedBy, _signingTime, _bestTime, _indication, _subIndication, _signatureLevel; - std::vector _errors; + X509Cert _signingCertificate, _ocspCertificate, _tsCertificate, _tsaCertificate; + std::string _id, _profile, _signedBy, _signatureMethod, _signingTime, _bestTime, _indication, _subIndication, _signatureLevel; + std::string _city, _stateOrProvince, _postalCode, _country; + std::vector _signerRoles; + std::vector _messageImprint; + std::vector _exceptions; friend SiVaContainer; }; @@ -78,10 +99,10 @@ class SiVaContainer: public Container static std::unique_ptr openInternal(const std::string &path); private: - SiVaContainer(const std::string &path, const std::string &ext); + SiVaContainer(const std::string &path, const std::string &ext, bool useHashCode); DISABLE_COPY(SiVaContainer); - std::stringstream* parseDDoc(std::istream *is); + std::stringstream* parseDDoc(std::unique_ptr is, bool useHashCode); class Private; Private *d;