Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 26 additions & 80 deletions src/ASiC_S.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

#include "ASiC_S.h"

#include "Conf.h"
#include "SignatureTST.h"
#include "SignatureXAdES_LTA.h"
#include "crypto/Signer.h"
Expand All @@ -33,18 +32,6 @@ 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 @@ -59,17 +46,19 @@ ASiC_S::ASiC_S(const string &path)
: ASiContainer(MIMETYPE_ASIC_S)
{
auto z = load(path, false, {mediaType()});
bool foundTimestamp = false;
for(const string &file: z.list())
{
if(file == "mimetype")
continue;
if(file == "META-INF/timestamp.tst")
foundTimestamp = true;
if(file == "META-INF/ASiCArchiveManifest.xml")
{
if(!signatures().empty())
THROW("Can not add signature to ASiC-S container which already contains a signature.");
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)});
addSignature(make_unique<SignatureTST>(true, z, this));
foundTimestamp = false; // Manifest contains timestamp
}
else if(file == "META-INF/signatures.xml")
{
Expand All @@ -80,28 +69,6 @@ ASiC_S::ASiC_S(const string &path)
for(auto s = signatures->signature(); s; s++)
addSignature(make_unique<SignatureXAdES_LTA>(signatures, s, this));
}
else if(file == "META-INF/ASiCArchiveManifest.xml")
{
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 All @@ -112,6 +79,12 @@ ASiC_S::ASiC_S(const string &path)
else
addDataFile(dataStream(file, z), file, "application/octet-stream");
}
if(foundTimestamp)
{
if(!signatures().empty())
THROW("Can not add signature to ASiC-S container which already contains a signature.");
addSignature(make_unique<SignatureTST>(false, z, this));
}

if(dataFiles().empty())
THROW("ASiC-S container does not contain any data objects.");
Expand Down Expand Up @@ -147,20 +120,20 @@ void ASiC_S::canSave()
THROW("ASiC-S container supports only saving TimeStampToken signatures.");
}

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))
return {};
DEBUG("ASiC_S::openInternal(%s)", path.c_str());
return unique_ptr<Container>(new ASiC_S(path));
try
{
if(util::File::fileExtension(path, {"asice", "sce", "bdoc"}))
return {};
return unique_ptr<Container>(new ASiC_S(path));
}
catch(const Exception &)
{
// Ignore the exception: not ASiC/zip document
}
return {};
}

Signature* ASiC_S::prepareSignature(Signer * /*signer*/)
Expand All @@ -170,10 +143,11 @@ Signature* ASiC_S::prepareSignature(Signer * /*signer*/)

void ASiC_S::save(const ZipSerialize &s)
{
if(zproperty("META-INF/manifest.xml").size && !createManifest().save(s.addFile("META-INF/manifest.xml", zproperty("META-INF/manifest.xml")), true))
if(const auto &prop = zproperty("META-INF/manifest.xml");
prop.size && !createManifest().save(s.addFile("META-INF/manifest.xml", prop), true))
THROW("Failed to create manifest XML");
for(const auto &[name, mime, data]: metadata)
s.addFile(name, zproperty(name))(data);
for(Signature *sig: signatures())
static_cast<SignatureTST*>(sig)->save(s);
}

Signature *ASiC_S::sign(Signer *signer)
Expand All @@ -184,31 +158,3 @@ Signature *ASiC_S::sign(Signer *signer)
THROW("ASiC-S container supports only one TimeStampToken signature.");
return addSignature(make_unique<SignatureTST>(this, signer));
}

/**
* Detect ASiC format based on file extentions, mimetype or zip contents.<br/>
* Container format is simple (ASiC-S) or extended (ASiC-E).
*
* @param path Path of the container.
* @throws Exception
*/
bool ASiC_S::isContainerSimpleFormat(const string &path)
{
DEBUG("isContainerSimpleFormat(path = '%s')", path.c_str());
if(util::File::fileExtension(path, {"asice", "sce", "bdoc"}))
return false;
if(util::File::fileExtension(path, {"asics", "scs"}))
return true;
DEBUG("Check if ASiC/zip containter");
try
{
ZipSerialize z(path, false);
vector<string> list = z.list();
return list.front() == "mimetype" && readMimetype(z) == MIMETYPE_ASIC_S;
}
catch(const Exception &)
{
// Ignore the exception: not ASiC/zip document
}
return false;
}
9 changes: 1 addition & 8 deletions src/ASiC_S.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@

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 @@ -39,8 +37,6 @@ 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 @@ -53,9 +49,6 @@ namespace digidoc
void canSave() final;
void save(const ZipSerialize &s) final;

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

struct Data;
std::vector<Data> metadata;
friend class SignatureTST;
};
}
1 change: 1 addition & 0 deletions src/ASiContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ namespace digidoc
//https://signa.mitsoft.lt/static/signa-web/webResources/docs/ADOC_specification_approved20090907_EN.pdf
static constexpr std::string_view MIMETYPE_ADOC = "application/vnd.lt.archyvai.adoc-2008";
static constexpr std::string_view MANIFEST_NS = "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0";
static constexpr std::string_view ASIC_NS = "http://uri.etsi.org/02918/v1.2.1#";

~ASiContainer() override;
std::string mediaType() const override;
Expand Down
101 changes: 76 additions & 25 deletions src/SignatureTST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,62 @@
#include "SignatureTST.h"

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

#include <functional>
#include <sstream>

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};
struct SignatureTST::Data {
std::string name, mime, data;
bool root = false;

SignatureTST::SignatureTST(const string &data, ASiC_S *asicSDoc)
: asicSDoc(asicSDoc)
, timestampToken(make_unique<TS>((const unsigned char*)data.data(), data.size()))
{}
Digest digest(Digest digest = {}) const
{
digest.update((const unsigned char*)data.data(), data.size());
return digest;
}
};

SignatureTST::SignatureTST(string current, XMLDocument &&xml, const string &data, ASiC_S *asicSDoc)
: SignatureTST(data, asicSDoc)
SignatureTST::SignatureTST(bool manifest, const ZipSerialize &z, ASiC_S *asicSDoc)
: asicSDoc(asicSDoc)
{
file = std::move(current);
doc = std::move(xml);
auto data = z.extract<stringstream>("META-INF/timestamp.tst").str();
timestampToken = make_unique<TS>((const unsigned char*)data.data(), data.size());
metadata.push_back({"META-INF/timestamp.tst", "application/vnd.etsi.timestamp-token", std::move(data)});
if(!manifest)
return;
XMLSchema schema(util::File::path(Conf::instance()->xsdPath(), "en_31916201v010101.xsd"));
function<void(const string &, string_view)> add = [this, &schema, &add, &z](const string &file, string_view mime) {
auto xml = z.extract<stringstream>(file);
XMLDocument doc = XMLDocument::openStream(xml, {"ASiCManifest", ASiContainer::ASIC_NS});
schema.validate(doc);

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();
metadata.push_back({file, string(mime), xml.str()});
metadata.push_back({uri, string(ref["MimeType"]), std::move(tst)});
};
add("META-INF/ASiCArchiveManifest.xml", "text/xml");
}

SignatureTST::SignatureTST(ASiC_S *asicSDoc, Signer *signer)
Expand All @@ -55,6 +85,8 @@ SignatureTST::SignatureTST(ASiC_S *asicSDoc, Signer *signer)
Digest digest;
dataFile->digest(digest);
timestampToken = make_unique<TS>(digest, signer->userAgent());
vector<unsigned char> der = *timestampToken;
metadata.push_back({"META-INF/timestamp.tst", "application/vnd.etsi.timestamp-token", {der.cbegin(), der.cend()}});
}

SignatureTST::~SignatureTST() = default;
Expand Down Expand Up @@ -106,28 +138,48 @@ void SignatureTST::validate() const
}
try
{
timestampToken->verify(dataToSign());
if(auto digestMethod = signatureMethod();
!Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) &&
auto digestMethod = signatureMethod();
DataFile *file = asicSDoc->dataFiles().front();
timestampToken->verify(file->calcDigest(digestMethod));
if(!Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) &&
Digest::isWeakDigest(digestMethod))
{
Exception e(EXCEPTION_PARAMS("TimeStamp '%s' digest weak", digestMethod.c_str()));
e.setCode(Exception::ReferenceDigestWeak);
exception.addCause(e);
}
if(doc)
vector<string> list {file->fileName()};
for(const auto &manifest: metadata)
{
DataFile *file = asicSDoc->dataFiles().front();
if(manifest.mime != "text/xml")
continue;
istringstream is(manifest.data);
XMLDocument doc = XMLDocument::openStream(is, {"ASiCManifest", ASiContainer::ASIC_NS});
vector<string> add;
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();
const auto &uri = add.emplace_back(util::File::fromUriPath(ref["URI"]));
vector<unsigned char> digest;
if(file->fileName() == uri)
digest = file->calcDigest(string(method));
else
{
auto i = find_if(metadata.cbegin(), metadata.cend(), [&uri](const auto &d) { return d.name == uri; });
if(i == metadata.cend())
THROW("File not found '%s'.", uri.c_str());
digest = i->digest(method).result();
}
if(vector<unsigned char> digestValue = ref/DigestValue; digest != digestValue)
THROW("Reference %s digest does not match", uri.c_str());
THROW("Reference '%s' digest does not match", uri.c_str());
}
// Check if all files in previous scope are present
for(const string &uri: list)
{
if(!contains(add, uri))
THROW("Reference '%s' not found in manifest", uri.c_str());
}
list = std::move(add);
}
}
catch (const Exception& e)
Expand All @@ -141,8 +193,6 @@ 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 All @@ -162,7 +212,8 @@ string SignatureTST::profile() const
return string(ASiC_S::ASIC_TST_PROFILE);
}

std::vector<unsigned char> SignatureTST::save() const
void SignatureTST::save(const ZipSerialize &z) const
{
return *timestampToken;
for(const auto &[name, mime, data, root]: metadata)
z.addFile(name, asicSDoc->zproperty(name))(data);
}
Loading
Loading