Skip to content

Commit

Permalink
ASiC-S TimeStamp creation support
Browse files Browse the repository at this point in the history
IB-8181

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma committed Nov 7, 2024
1 parent 82eee1c commit 499506d
Show file tree
Hide file tree
Showing 15 changed files with 185 additions and 127 deletions.
46 changes: 9 additions & 37 deletions src/ASiC_E.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ using namespace digidoc;
using namespace digidoc::util;
using namespace std;

const string_view ASiC_E::ASIC_TM_PROFILE = "time-mark";
const string_view ASiC_E::ASIC_TS_PROFILE = "time-stamp";
const string_view ASiC_E::ASIC_TSA_PROFILE = "time-stamp-archive";
const string_view ASiC_E::ASIC_TMA_PROFILE = "time-mark-archive";
constexpr string_view MANIFEST_NS {"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"};

class ASiC_E::Private
Expand Down Expand Up @@ -85,40 +81,21 @@ vector<DataFile*> ASiC_E::metaFiles() const
* document does not exist.
* @throws Exception is thrown if ASiC_E class is not correctly initialized.
*/
void ASiC_E::save(const string &path)
void ASiC_E::save(const ZipSerialize &s)
{
if(dataFiles().empty())
THROW("Can not save, container is empty.");
if(mediaType() != MIMETYPE_ASIC_E)
THROW("'%s' format is not supported", mediaType().c_str());

if(!path.empty())
zpath(path);
ZipSerialize s(zpath(), true);

stringstream mimetype;
mimetype << mediaType();
s.addFile("mimetype", mimetype, zproperty("mimetype"), false);

stringstream manifest;
createManifest(manifest);
s.addFile("META-INF/manifest.xml", manifest, zproperty("META-INF/manifest.xml"));

for(const DataFile *file: dataFiles())
s.addFile(file->fileName(), *(static_cast<const DataFilePrivate*>(file)->m_is), zproperty(file->fileName()));
if(!createManifest().save(s.addFile("META-INF/manifest.xml", zproperty("META-INF/manifest.xml"))))
THROW("Failed to create manifest XML");

std::set<Signatures*> saved;
unsigned int i = 0;
for(Signature *iter: signatures())
{
string file = Log::format("META-INF/signatures%u.xml", i++);
auto *signature = static_cast<SignatureXAdES_B*>(iter);
auto *signature = dynamic_cast<SignatureXAdES_B*>(iter);
if(!saved.insert(signature->signatures.get()).second)
continue;
stringstream ofs;
if(!signature->signatures->save(ofs))
string file = Log::format("META-INF/signatures%u.xml", i++);
if(!signature->signatures->save(s.addFile(file, zproperty(file))))
THROW("Failed to create signature XML file.");
s.addFile(file, ofs, zproperty(file));
}
}

Expand Down Expand Up @@ -164,14 +141,10 @@ unique_ptr<Container> ASiC_E::openInternal(const string &path)
/**
* Creates BDoc container manifest file and returns its path.
*
* Note: If non-ascii characters are present in XML data, we depend on the LANG variable to be set properly
* (see iconv --list for the list of supported encoding values for libiconv).
*
*
* @return returns created manifest file path.
* @throws Exception exception is thrown if manifest file creation failed.
*/
void ASiC_E::createManifest(ostream &os)
XMLDocument ASiC_E::createManifest()
{
DEBUG("ASiC_E::createManifest()");
auto doc = XMLDocument::create("manifest", MANIFEST_NS, "manifest");
Expand All @@ -184,8 +157,7 @@ void ASiC_E::createManifest(ostream &os)
add("/", mediaType());
for(const DataFile *file: dataFiles())
add(file->fileName(), file->mediaType());
if(!doc.save(os))
THROW("Failed to create manifest XML");
return doc;
}

/**
Expand Down Expand Up @@ -291,7 +263,7 @@ Signature* ASiC_E::prepareSignature(Signer *signer)

Signature *ASiC_E::sign(Signer* signer)
{
auto *s = static_cast<SignatureXAdES_LTA*>(prepareSignature(signer));
auto *s = dynamic_cast<SignatureXAdES_LTA*>(prepareSignature(signer));
try
{
s->setSignatureValue(signer->sign(s->signatureMethod(), s->dataToSign()));
Expand Down
14 changes: 8 additions & 6 deletions src/ASiC_E.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

namespace digidoc
{
class XMLDocument;

/**
* Implements the BDOC specification of the signed digital document container.
* Container can contain several files and all these files can be signed using
Expand All @@ -34,13 +36,12 @@ namespace digidoc
class ASiC_E final : public ASiContainer
{
public:
static const std::string_view ASIC_TM_PROFILE;
static const std::string_view ASIC_TS_PROFILE;
static const std::string_view ASIC_TMA_PROFILE;
static const std::string_view ASIC_TSA_PROFILE;
static constexpr std::string_view ASIC_TM_PROFILE = "time-mark";
static constexpr std::string_view ASIC_TS_PROFILE = "time-stamp";
static constexpr std::string_view ASIC_TMA_PROFILE = "time-mark-archive";
static constexpr std::string_view ASIC_TSA_PROFILE = "time-stamp-archive";

~ASiC_E() final;
void save(const std::string &path = {}) final;
std::vector<DataFile*> metaFiles() const;

void addAdESSignature(std::istream &data) final;
Expand All @@ -54,8 +55,9 @@ namespace digidoc
ASiC_E();
ASiC_E(const std::string &path);
DISABLE_COPY(ASiC_E);
void createManifest(std::ostream &os);
XMLDocument createManifest();
void parseManifestAndLoadFiles(const ZipSerialize &z);
void save(const ZipSerialize &s) final;

class Private;
std::unique_ptr<Private> d;
Expand Down
92 changes: 59 additions & 33 deletions src/ASiC_S.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

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

Expand All @@ -34,47 +35,51 @@ using namespace std;
/**
* Initialize ASiCS container.
*/
ASiC_S::ASiC_S(): ASiContainer(MIMETYPE_ASIC_S)
ASiC_S::ASiC_S()
: ASiContainer(MIMETYPE_ASIC_S)
{}

/**
* Opens ASiC-S container from a file
*/
ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S)
ASiC_S::ASiC_S(const string &path)
: ASiContainer(MIMETYPE_ASIC_S)
{
auto z = load(path, false, {mediaType()});
static const string_view metaInf = "META-INF/";
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" ||
(metaInf.size() < file.size() && file.compare(0, metaInf.size(), metaInf) == 0))
{
if(file == "META-INF/timestamp.tst")
{
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));
}
if(file == "META-INF/signatures.xml")
{
if(!signatures().empty())
THROW("Can not add signature to ASiC-S container which already contains a signature.");
auto data = z.extract<stringstream>(file);
auto signatures = make_shared<Signatures>(data, this);
for(auto s = signatures->signature(); s; s++)
addSignature(make_unique<SignatureXAdES_LTA>(signatures, s, this));
}
if(file == "mimetype")
continue;
if(file == "META-INF/timestamp.tst")
{
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));
}

const auto directory = File::directory(file);
if(directory.empty() || directory == "/" || directory == "./")
else if(file == "META-INF/signatures.xml")
{
if(!dataFiles().empty())
THROW("Can not add document to ASiC-S container which already contains a document.");
addDataFile(dataStream(file, z), file, "application/octet-stream");
if(!signatures().empty())
THROW("Can not add signature to ASiC-S container which already contains a signature.");
auto data = z.extract<stringstream>(file);
auto signatures = make_shared<Signatures>(data, this);
for(auto s = signatures->signature(); s; s++)
addSignature(make_unique<SignatureXAdES_LTA>(signatures, s, this));
}
else if(file == "META-INF/ASiCArchiveManifest.xml")
THROW("ASiCArchiveManifest are not supported.");
else if(starts_with(file, "META-INF/"))
continue;
else if(const auto directory = File::directory(file);
!directory.empty() && directory != "/" && directory != "./")
THROW("Subfolders are not supported %s", directory.c_str());
else if(!dataFiles().empty())
THROW("Can not add document to ASiC-S container which already contains a document.");
else
addDataFile(dataStream(file, z), file, "application/octet-stream");
}

if(dataFiles().empty())
Expand All @@ -83,14 +88,21 @@ ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S)
THROW("ASiC-S container does not contain any signatures.");
}

void ASiC_S::save(const string & /*path*/)
void ASiC_S::addDataFileChecks(const string &fileName, const string &mediaType)
{
THROW("Not implemented.");
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*/)
unique_ptr<Container> ASiC_S::createInternal(const string &path)
{
return {};
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 @@ -111,9 +123,23 @@ Signature* ASiC_S::prepareSignature(Signer * /*signer*/)
THROW("Not implemented.");
}

Signature *ASiC_S::sign(Signer * /*signer*/)
void ASiC_S::save(const ZipSerialize &s)
{
THROW("Not implemented.");
auto list = signatures();
if(list.empty())
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());
}

Signature *ASiC_S::sign(Signer *signer)
{
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));
}

/**
Expand Down
6 changes: 4 additions & 2 deletions src/ASiC_S.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@ namespace digidoc
*/
class ASiC_S : public ASiContainer
{

public:
void save(const std::string &path = {}) override;
static constexpr std::string_view ASIC_TST_PROFILE = "TimeStampToken";

void addAdESSignature(std::istream &sigdata) override;
Signature* prepareSignature(Signer *signer) override;
Expand All @@ -46,6 +45,9 @@ namespace digidoc
ASiC_S(const std::string &path);
DISABLE_COPY(ASiC_S);

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

static bool isContainerSimpleFormat(const std::string &path);
};
}
39 changes: 34 additions & 5 deletions src/ASiContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "util/log.h"

#include <algorithm>
#include <array>
#include <ctime>
#include <fstream>
#include <map>
Expand All @@ -51,7 +52,7 @@ class ASiContainer::Private
ASiContainer::ASiContainer(string_view mimetype)
: d(make_unique<Private>())
{
d->mimetype = mimetype;
d->mimetype = string(mimetype);
}

/**
Expand Down Expand Up @@ -166,15 +167,15 @@ void ASiContainer::addDataFile(const string &path, const string &mediaType)
is = std::move(data);
}
d->properties[fileName] = { appInfo(), File::modifiedTime(path), size };
addDataFilePrivate(std::move(is), std::move(fileName), mediaType);
d->documents.push_back(new DataFilePrivate(std::move(is), std::move(fileName), mediaType));
}

void ASiContainer::addDataFile(unique_ptr<istream> is, const string &fileName, const string &mediaType)
{
addDataFileChecks(fileName, mediaType);
if(fileName.find_last_of("/\\") != string::npos)
THROW("Document file '%s' cannot contain directory path.", fileName.c_str());
addDataFilePrivate(std::move(is), fileName, mediaType);
d->documents.push_back(new DataFilePrivate(std::move(is), fileName, mediaType));
}

void ASiContainer::addDataFileChecks(const string &fileName, const string &mediaType)
Expand Down Expand Up @@ -241,6 +242,34 @@ void ASiContainer::deleteSignature(Signature* s)
delete s;
}

void ASiContainer::save(const string &path)
{
if(dataFiles().empty())
THROW("Can not save, container is empty.");

if(!path.empty())
zpath(path);
ZipSerialize s(zpath(), true);
s.addFile("mimetype", zproperty("mimetype"), false)(mediaType());

array<char,10240> buf{};
for(const DataFile *file: dataFiles())
{
auto f = s.addFile(file->fileName(), zproperty(file->fileName()));
const auto &is = dynamic_cast<const DataFilePrivate*>(file)->m_is;
is->clear();
is->seekg(0);
while(*is)
{
is->read(buf.data(), buf.size());
if(auto size = is->gcount(); size > 0)
f(buf.data(), size_t(size));
}
}

save(s);
}

void ASiContainer::zpath(const string &file)
{
d->path = file;
Expand All @@ -251,11 +280,11 @@ string ASiContainer::zpath() const
return d->path;
}

const ZipSerialize::Properties& ASiContainer::zproperty(const string &file) const
const ZipSerialize::Properties& ASiContainer::zproperty(string_view file) const
{
if(auto i = d->properties.find(file); i != d->properties.cend())
return i->second;
return d->properties[file] = { appInfo(), time(nullptr), 0 };
return d->properties[string(file)] = { appInfo(), time(nullptr), 0 };
}

/**
Expand Down
Loading

0 comments on commit 499506d

Please sign in to comment.