Skip to content

Commit

Permalink
Implement SiVa V3 changes (#354)
Browse files Browse the repository at this point in the history
IB-6010

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma authored Sep 7, 2020
1 parent 5327cc3 commit 823bfaf
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 30 deletions.
107 changes: 83 additions & 24 deletions src/SiVaContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -61,7 +62,7 @@ vector<unsigned char> SignatureSiVa::dataToSign() const
THROW("Not implemented.");
}

void SignatureSiVa::setSignatureValue(const vector<unsigned char> &)
void SignatureSiVa::setSignatureValue(const vector<unsigned char> & /*signatureValue*/)
{
THROW("Not implemented.");
}
Expand All @@ -76,36 +77,36 @@ void SignatureSiVa::validate(const string &policy) const
static const set<string> 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<istream> ifs(new ifstream(File::encodeName(path).c_str(), ifstream::binary));
istream *is = ifs.get();
unique_ptr<stringstream> 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
{
Expand All @@ -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());

Expand All @@ -160,22 +161,70 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext)

jsonxx::Object report = result.get<jsonxx::Object>("validationReport");
jsonxx::Object base = report.get<jsonxx::Object>("validationConclusion");
for(const jsonxx::Value *obj: base.get<jsonxx::Array>("signatures", jsonxx::Array()).values())
for(const jsonxx::Value *obj: base.get<jsonxx::Array>("signatures", {}).values())
{
SignatureSiVa *s = new SignatureSiVa;
jsonxx::Object signature = obj->get<jsonxx::Object>();
s->_id = signature.get<string>("id");
s->_signingTime = signature.get<string>("claimedSigningTime");
s->_bestTime = signature.get<jsonxx::Object>("info", jsonxx::Object()).get<string>("bestSignatureTime", string());
s->_bestTime = signature.get<jsonxx::Object>("info", {}).get<string>("bestSignatureTime", {});
s->_profile = signature.get<string>("signatureFormat");
s->_indication = signature.get<string>("indication");
s->_subIndication = signature.get<string>("subIndication", string());
s->_subIndication = signature.get<string>("subIndication", {});
s->_signedBy = signature.get<string>("signedBy");
s->_signatureLevel = signature.get<string>("signatureLevel", string());
for(const jsonxx::Value *error: signature.get<jsonxx::Array>("errors", jsonxx::Array()).values())
s->_signatureMethod = signature.get<string>("signatureMethod", {});
s->_signatureLevel = signature.get<string>("signatureLevel", {});
jsonxx::Object info = signature.get<jsonxx::Object>("info", {});
if(info.has<string>("timeAssertionMessageImprint"))
{
string base64 = info.get<string>("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<jsonxx::Array>("signerRole", {}).values())
s->_signerRoles.push_back(signerRole->get<jsonxx::Object>().get<string>("claimedRole"));
jsonxx::Object signatureProductionPlace = info.get<jsonxx::Object>("signatureProductionPlace", {});
s->_city = signatureProductionPlace.get<string>("city", {});
s->_stateOrProvince = signatureProductionPlace.get<string>("stateOrProvince", {});
s->_postalCode = signatureProductionPlace.get<string>("postalCode", {});
s->_country = signatureProductionPlace.get<string>("countryName", {});
for(const jsonxx::Value *certificates: signature.get<jsonxx::Array>("certificates", {}).values())
{
jsonxx::Object certificate = certificates->get<jsonxx::Object>();
string content = certificate.get<string>("content");
XMLSize_t size = 0;
XMLByte *der = Base64::decode((const XMLByte*)content.c_str(), &size);
if(certificate.get<string>("type") == "SIGNING")
s->_signingCertificate = X509Cert(der, size, X509Cert::Der);
if(certificate.get<string>("type") == "REVOCATION")
s->_ocspCertificate = X509Cert(der, size, X509Cert::Der);
if(certificate.get<string>("type") == "SIGNATURE_TIMESTAMP")
s->_tsCertificate = X509Cert(der, size, X509Cert::Der);
if(certificate.get<string>("type") == "ARCHIVE_TIMESTAMP")
s->_tsaCertificate = X509Cert(der, size, X509Cert::Der);
delete der;
}
for(const jsonxx::Value *error: signature.get<jsonxx::Array>("errors", {}).values())
{
string message = error->get<jsonxx::Object>().get<string>("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<jsonxx::Array>("warnings", {}).values())
{
string message = warning->get<jsonxx::Object>().get<string>("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);
}
Expand Down Expand Up @@ -205,7 +254,7 @@ void SiVaContainer::addAdESSignature(istream & /*signature*/)
THROW("Not supported.");
}

unique_ptr<Container> SiVaContainer::createInternal(const string & /*unused*/)
unique_ptr<Container> SiVaContainer::createInternal(const string & /*path*/)
{
return {};
}
Expand All @@ -225,10 +274,18 @@ unique_ptr<Container> SiVaContainer::openInternal(const string &path)
static const set<string> supported = {"PDF", "DDOC"};
string ext = File::fileExtension(path);
transform(ext.begin(), ext.end(), ext.begin(), ::toupper);
return unique_ptr<Container>(supported.find(ext) != supported.cend() ? new SiVaContainer(path, ext) : nullptr);
if(!supported.count(ext))
return {};
try {
return unique_ptr<Container>(new SiVaContainer(path, ext, true));
} catch(const Exception &e) {
if(e.msg().find("Bad digest for DataFile") == 0)
return unique_ptr<Container>(new SiVaContainer(path, ext, false));
throw;
}
}

stringstream* SiVaContainer::parseDDoc(istream *is)
stringstream* SiVaContainer::parseDDoc(std::unique_ptr<std::istream> is, bool useHashCode)
{
try
{
Expand All @@ -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<unsigned char> digest = calc.result();
Expand Down
33 changes: 27 additions & 6 deletions src/SiVaContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,44 @@ 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<unsigned char> dataToSign() const override;
void setSignatureValue(const std::vector<unsigned char> &signatureValue) override;

// 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<std::string> 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<unsigned char> messageImprint() const override { return _messageImprint; };

private:
SignatureSiVa() = default;
DISABLE_COPY(SignatureSiVa);

std::string _id, _profile, _signedBy, _signingTime, _bestTime, _indication, _subIndication, _signatureLevel;
std::vector<Exception> _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<std::string> _signerRoles;
std::vector<unsigned char> _messageImprint;
std::vector<Exception> _exceptions;

friend SiVaContainer;
};
Expand Down Expand Up @@ -78,10 +99,10 @@ class SiVaContainer: public Container
static std::unique_ptr<Container> 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<std::istream> is, bool useHashCode);

class Private;
Private *d;
Expand Down

0 comments on commit 823bfaf

Please sign in to comment.