Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate ASiC-S manifest containers #628

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
82 changes: 69 additions & 13 deletions src/ASiC_S.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,32 @@

#include "ASiC_S.h"

#include "Conf.h"
#include "SignatureTST.h"
#include "SignatureXAdES_LTA.h"
#include "crypto/Signer.h"
#include "util/algorithm.h"
#include "util/File.h"
#include "util/log.h"

#include <algorithm>
#include <sstream>

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 @@ -45,10 +59,6 @@ ASiC_S::ASiC_S(const string &path)
: ASiContainer(MIMETYPE_ASIC_S)
{
auto z = load(path, false, {mediaType()});
auto starts_with = [](string_view str, string_view needle) constexpr {
return str.size() >= needle.size() && str.compare(0, needle.size(), needle) == 0;
};

for(const string &file: z.list())
{
if(file == "mimetype")
Expand All @@ -57,7 +67,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 @@ -69,7 +81,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 All @@ -87,9 +119,21 @@ ASiC_S::ASiC_S(const string &path)
THROW("ASiC-S container does not contain any signatures.");
}

unique_ptr<Container> ASiC_S::createInternal(const string & /*path*/)
void ASiC_S::addDataFileChecks(const string &fileName, const string &mediaType)
{
return {};
ASiContainer::addDataFileChecks(fileName, mediaType);
if(!dataFiles().empty())
THROW("Can not add document to ASiC-S container which already contains a document.");
}

unique_ptr<Container> ASiC_S::createInternal(const string &path)
{
if(!util::File::fileExtension(path, {"asics", "scs"}))
return {};
DEBUG("ASiC_S::createInternal(%s)", path.c_str());
auto doc = unique_ptr<ASiC_S>(new ASiC_S());
doc->zpath(path);
return doc;
}

void ASiC_S::addAdESSignature(istream & /*signature*/)
Expand All @@ -103,6 +147,14 @@ 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))
Expand All @@ -120,13 +172,17 @@ 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))
THROW("Failed to create manifest XML");
if(auto list = signatures(); !list.empty())
s.addFile("META-INF/timestamp.tst", zproperty("META-INF/timestamp.tst"))(static_cast<SignatureTST*>(list.front())->save());
for(const auto &[name, mime, data]: metadata)
s.addFile(name, zproperty(name))(data);
}

Signature *ASiC_S::sign(Signer * /*signer*/)
Signature *ASiC_S::sign(Signer *signer)
{
THROW("Not implemented.");
if(signer->profile() != ASIC_TST_PROFILE)
THROW("ASiC-S container supports only TimeStampToken signing.");
if(!signatures().empty())
THROW("ASiC-S container supports only one TimeStampToken signature.");
return addSignature(make_unique<SignatureTST>(this, signer));
}

/**
Expand Down
8 changes: 8 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 @@ -45,9 +49,13 @@ namespace digidoc
ASiC_S(const std::string &path);
DISABLE_COPY(ASiC_S);

void addDataFileChecks(const std::string &path, const std::string &mediaType) override;
void canSave() final;
void save(const ZipSerialize &s) final;

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

struct Data;
std::vector<Data> metadata;
};
}
38 changes: 38 additions & 0 deletions src/SignatureTST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,41 @@
#include "ASiC_S.h"
#include "DataFile_p.h"
#include "crypto/Digest.h"
#include "crypto/Signer.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, Signer *signer)
: asicSDoc(asicSDoc)
{
auto *dataFile = static_cast<DataFilePrivate*>(asicSDoc->dataFiles().front());
Digest digest;
dataFile->digest(digest);
timestampToken = make_unique<TS>(digest, signer->userAgent());
}

SignatureTST::~SignatureTST() = default;

X509Cert SignatureTST::TimeStampCertificate() const
Expand Down Expand Up @@ -93,6 +115,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 @@ -105,6 +141,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
6 changes: 5 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,8 @@ 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, Signer *signer);
~SignatureTST();

std::vector<unsigned char> messageImprint() const override;
Expand All @@ -57,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
8 changes: 5 additions & 3 deletions src/crypto/Signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "Signer.h"

#include "ASiC_E.h"
#include "ASiC_S.h"
#include "Conf.h"
#include "crypto/Digest.h"
#include "crypto/X509Cert.h"
Expand All @@ -38,7 +39,7 @@ class Signer::Private
{
public:
optional<string> method;
string profile = "time-stamp";
string profile{ASiC_E::ASIC_TS_PROFILE};
string userAgent;
string city, streetAddress, stateOrProvince, postalCode, countryName;
vector<string> signerRoles;
Expand Down Expand Up @@ -181,9 +182,10 @@ void Signer::setProfile(const string &profile)
{"TSA", ASiC_E::ASIC_TSA_PROFILE},
{ASiC_E::ASIC_TS_PROFILE, ASiC_E::ASIC_TS_PROFILE},
{ASiC_E::ASIC_TSA_PROFILE, ASiC_E::ASIC_TSA_PROFILE},
{ASiC_S::ASIC_TST_PROFILE, ASiC_S::ASIC_TST_PROFILE},
{"time-stamp-token", ASiC_S::ASIC_TST_PROFILE}
};
if(auto it = std::find_if(profiles.cbegin(), profiles.cend(), [&profile](const auto &elem) { return elem.first == profile; });
it != profiles.cend())
if(auto it = profiles.find(profile); it != profiles.cend())
d->profile = it->second;
else
THROW("Unsupported profile: %s", profile.c_str());
Expand Down
2 changes: 1 addition & 1 deletion src/digidoc-tool.1.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Command websign:
Command sign:
Example: digidoc-tool sign demo-container.asice
Available options:
--profile= - signature profile, TS, TSA, time-stamp, time-stamp-archive
--profile= - signature profile, TS, TSA, time-stamp, time-stamp-archive, TimeStampToken, time-stamp-token
--XAdESEN - use XAdES EN profile
--city= - city of production place
--street= - streetAddress of production place in XAdES EN profile
Expand Down
2 changes: 1 addition & 1 deletion src/digidoc-tool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ static int printUsage(const char *executable)
<< " Command sign:" << endl
<< " Example: " << executable << " sign demo-container.asice" << endl
<< " Available options:" << endl
<< " --profile= - signature profile, TS, TSA, time-stamp, time-stamp-archive" << endl
<< " --profile= - signature profile, TS, TSA, time-stamp, time-stamp-archive, TimeStampToken, time-stamp-token" << endl
<< " --XAdESEN - use XAdES EN profile" << endl
<< " --city= - city of production place" << endl
<< " --street= - streetAddress of production place in XAdES EN profile" << endl
Expand Down