Skip to content

Commit

Permalink
Validate ASiC-S manifest containers
Browse files Browse the repository at this point in the history
IB-8180

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma committed Nov 7, 2024
1 parent 499506d commit a712928
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 13 deletions.
50 changes: 47 additions & 3 deletions src/ASiC_S.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "ASiC_S.h"

#include "Conf.h"
#include "SignatureTST.h"
#include "SignatureXAdES_LTA.h"
#include "crypto/Signer.h"
Expand All @@ -32,6 +33,18 @@ using namespace digidoc;
using namespace digidoc::util;
using namespace std;

struct ASiC_S::Data {
std::string name, mime, data;

Digest digest(Digest digest = {}) const
{
digest.update((const unsigned char*)data.data(), data.size());
return digest;
}
};



/**
* Initialize ASiCS container.
*/
Expand All @@ -58,7 +71,9 @@ ASiC_S::ASiC_S(const string &path)
{
if(!signatures().empty())
THROW("Can not add signature to ASiC-S container which already contains a signature.");
addSignature(make_unique<SignatureTST>(z.extract<stringstream>(file).str(), this));
string tst = z.extract<stringstream>(file).str();
addSignature(make_unique<SignatureTST>(tst, this));
metadata.push_back({file, "application/vnd.etsi.timestamp-token", std::move(tst)});
}
else if(file == "META-INF/signatures.xml")
{
Expand All @@ -70,7 +85,27 @@ ASiC_S::ASiC_S(const string &path)
addSignature(make_unique<SignatureXAdES_LTA>(signatures, s, this));
}
else if(file == "META-INF/ASiCArchiveManifest.xml")
THROW("ASiCArchiveManifest are not supported.");
{
function<void(const string &, string_view)> add = [this, &add, &z](const string &file, string_view mime) {
auto xml = z.extract<stringstream>(file);
XMLDocument doc = XMLDocument::openStream(xml, {"ASiCManifest", ASIC_NS});
doc.validateSchema(util::File::path(Conf::instance()->xsdPath(), "en_31916201v010101.xsd"));

for(auto ref = doc/"DataObjectReference"; ref; ref++)
{
if(ref["Rootfile"] == "true")
add(util::File::fromUriPath(ref["URI"]), ref["MimeType"]);
}

auto ref = doc/"SigReference";
string uri = util::File::fromUriPath(ref["URI"]);
string tst = z.extract<stringstream>(uri).str();
addSignature(make_unique<SignatureTST>(file, ::move(doc), tst, this));
metadata.push_back({file, string(mime), xml.str()});
metadata.push_back({uri, string(ref["MimeType"]), std::move(tst)});
};
add(file, "text/xml");
}
else if(starts_with(file, "META-INF/"))
continue;
else if(const auto directory = File::directory(file);
Expand Down Expand Up @@ -110,6 +145,14 @@ void ASiC_S::addAdESSignature(istream & /*signature*/)
THROW("Not implemented.");
}

Digest ASiC_S::fileDigest(const string &file, string_view method) const
{
if(auto i = find_if(metadata.cbegin(), metadata.cend(), [&file](const auto &d) { return d.name == file; });
i != metadata.cend())
return i->digest(method);
THROW("File not found %s.", file.c_str());
}

unique_ptr<Container> ASiC_S::openInternal(const string &path, ContainerOpenCB * /*cb*/)
{
if (!isContainerSimpleFormat(path))
Expand All @@ -130,7 +173,8 @@ void ASiC_S::save(const ZipSerialize &s)
return;
if(list.front()->profile() != ASIC_TST_PROFILE)
THROW("ASiC-S container supports only TimeStampToken signing.");
s.addFile("META-INF/timestamp.tst", zproperty("META-INF/timestamp.tst"))(dynamic_cast<SignatureTST*>(list.front())->save());
for(const auto &[name, mime, data]: metadata)
s.addFile(name, zproperty(name))(data);
}

Signature *ASiC_S::sign(Signer *signer)
Expand Down
7 changes: 7 additions & 0 deletions src/ASiC_S.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

namespace digidoc
{
class Digest;

/**
* Implements the ASiC-S specification of the timestamped digital document container.
* Container contains a single datafile object and one time assertion file.
Expand All @@ -37,6 +39,8 @@ namespace digidoc
Signature* prepareSignature(Signer *signer) override;
Signature* sign(Signer* signer) override;

Digest fileDigest(const std::string &file, std::string_view method = {}) const;

static std::unique_ptr<Container> createInternal(const std::string &path);
static std::unique_ptr<Container> openInternal(const std::string &path, ContainerOpenCB *cb);

Expand All @@ -49,5 +53,8 @@ namespace digidoc
void save(const ZipSerialize &s) final;

static bool isContainerSimpleFormat(const std::string &path);

struct Data;
std::vector<Data> metadata;
};
}
32 changes: 30 additions & 2 deletions src/SignatureTST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,40 @@
#include "SignatureTST.h"

#include "ASiC_S.h"
#include "Conf.h"
#include "DataFile_p.h"
#include "crypto/TS.h"
#include "crypto/X509Cert.h"
#include "util/DateTime.h"
#include "util/File.h"
#include "util/log.h"

using namespace digidoc;
using namespace std;

constexpr std::string_view DSIG_NS {"http://www.w3.org/2000/09/xmldsig#"};
constexpr XMLName DigestMethod {"DigestMethod", DSIG_NS};
constexpr XMLName DigestValue {"DigestValue", DSIG_NS};

SignatureTST::SignatureTST(const string &data, ASiC_S *asicSDoc)
: asicSDoc(asicSDoc)
, timestampToken(make_unique<TS>((const unsigned char*)data.data(), data.size()))
{}


SignatureTST::SignatureTST(string current, XMLDocument &&xml, const string &data, ASiC_S *asicSDoc)
: SignatureTST(data, asicSDoc)
{
file = std::move(current);
doc = std::move(xml);
}

SignatureTST::SignatureTST(ASiC_S *asicSDoc)
: asicSDoc(asicSDoc)
{
auto *dataFile = dynamic_cast<DataFilePrivate*>(asicSDoc->dataFiles().front());
Digest digest;
dataFile->digest(digest);
timestampToken = make_unique<TS>(CONF(TSUrl), digest);
timestampToken = make_unique<TS>(digest);
}

SignatureTST::~SignatureTST() = default;
Expand Down Expand Up @@ -102,6 +114,20 @@ void SignatureTST::validate() const
e.setCode(Exception::ReferenceDigestWeak);
exception.addCause(e);
}
if(doc)
{
DataFile *file = asicSDoc->dataFiles().front();
for(auto ref = doc/"DataObjectReference"; ref; ref++)
{
string_view method = (ref/DigestMethod)["Algorithm"];
auto uri = util::File::fromUriPath(ref["URI"]);
vector<unsigned char> digest = file->fileName() == uri ?
dynamic_cast<const DataFilePrivate*>(file)->calcDigest(string(method)) :
asicSDoc->fileDigest(uri, method).result();
if(vector<unsigned char> digestValue = ref/DigestValue; digest != digestValue)
THROW("Reference %s digest does not match", uri.c_str());
}
}
}
catch (const Exception& e)
{
Expand All @@ -114,6 +140,8 @@ void SignatureTST::validate() const

std::vector<unsigned char> SignatureTST::dataToSign() const
{
if(!file.empty())
return asicSDoc->fileDigest(file, signatureMethod()).result();
return asicSDoc->dataFiles().front()->calcDigest(signatureMethod());
}

Expand Down
5 changes: 4 additions & 1 deletion src/SignatureTST.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

#include "Signature.h"

#include <memory>
#include "XMLDocument.h"

namespace digidoc
{
Expand All @@ -32,6 +32,7 @@ class SignatureTST final: public Signature
{
public:
SignatureTST(const std::string &data, ASiC_S *asicSDoc);
SignatureTST(std::string current, XMLDocument &&xml, const std::string &data, ASiC_S *asicSDoc);
SignatureTST(ASiC_S *asicSDoc);
~SignatureTST();

Expand All @@ -58,6 +59,8 @@ class SignatureTST final: public Signature
private:
DISABLE_COPY(SignatureTST);
ASiC_S *asicSDoc {};
std::string file;
XMLDocument doc;
std::unique_ptr<TS> timestampToken;
};

Expand Down
3 changes: 1 addition & 2 deletions src/SignatureXAdES_LTA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include "SignatureXAdES_LTA.h"

#include "ASiC_E.h"
#include "Conf.h"
#include "DataFile_p.h"
#include "crypto/Digest.h"
#include "crypto/TS.h"
Expand Down Expand Up @@ -110,7 +109,7 @@ void SignatureXAdES_LTA::extendSignatureProfile(const string &profile)
auto method = canonicalizationMethod();
calcArchiveDigest(calc, method);

TS tsa(CONF(TSUrl), calc);
TS tsa(calc);
auto ts = unsignedSignatureProperties() + ArchiveTimeStamp;
ts.setNS(ts.addNS(XADESv141_NS, "xades141"));
ts.setProperty("Id", id() + "-A0");
Expand Down
3 changes: 1 addition & 2 deletions src/SignatureXAdES_T.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include "SignatureXAdES_T.h"

#include "ASiC_E.h"
#include "Conf.h"
#include "crypto/Digest.h"
#include "crypto/OCSP.h"
#include "crypto/TS.h"
Expand Down Expand Up @@ -72,7 +71,7 @@ void SignatureXAdES_T::extendSignatureProfile(const std::string &profile)
auto method = canonicalizationMethod();
signatures->c14n(calc, method, signatureValue());

TS tsa(CONF(TSUrl), calc);
TS tsa(calc);
auto ts = usp + "SignatureTimeStamp";
ts.setProperty("Id", id() + Log::format("-T%zu", i));
(ts + CanonicalizationMethod).setProperty("Algorithm", method);
Expand Down
4 changes: 2 additions & 2 deletions src/crypto/TS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ void *OPENSSL_memdup(const void *data, size_t size)
}
#endif

TS::TS(const string &url, const Digest &digest)
TS::TS(const Digest &digest)
{
auto req = SCOPE_PTR(TS_REQ, TS_REQ_new());
TS_REQ_set_version(req.get(), 1);
Expand Down Expand Up @@ -87,7 +87,7 @@ TS::TS(const string &url, const Digest &digest)
RAND_bytes(nonce->data, nonce->length);
TS_REQ_set_nonce(req.get(), nonce.get());

Connect::Result result = Connect(url, "POST", 0, CONF(TSCerts)).exec({
Connect::Result result = Connect(CONF(TSUrl), "POST", 0, CONF(TSCerts)).exec({
{"Content-Type", "application/timestamp-query"},
{"Accept", "application/timestamp-reply"},
{"Connection", "Close"},
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/TS.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class X509Cert;
class TS
{
public:
TS(const std::string &url, const Digest &digest);
TS(const Digest &digest);
inline TS(const std::vector<unsigned char> &data): TS(data.data(), data.size()) {}
TS(const unsigned char *data = nullptr, size_t size = 0);

Expand Down

0 comments on commit a712928

Please sign in to comment.