Skip to content

Commit

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

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma committed Nov 18, 2024
1 parent aba89db commit 1c129e6
Show file tree
Hide file tree
Showing 13 changed files with 176 additions and 135 deletions.
56 changes: 9 additions & 47 deletions src/ASiC_E.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ using namespace digidoc;
using namespace digidoc::util;
using namespace std;

constexpr string_view MANIFEST_NS {"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"};

class ASiC_E::Private
{
public:
Expand Down Expand Up @@ -88,28 +86,10 @@ 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;
if(!createManifest().save(manifest))
if(!createManifest().save(s.addFile("META-INF/manifest.xml", zproperty("META-INF/manifest.xml")), true))
THROW("Failed to create manifest XML");
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()));

std::set<Signatures*> saved;
for(Signature *iter: signatures())
Expand All @@ -122,10 +102,8 @@ void ASiC_E::save(const string &path)
});
if(name == d->signatures.cend())
THROW("Unkown signature object");
stringstream ofs;
if(!signatures->save(ofs))
if(!signatures->save(s.addFile(name->first, zproperty(name->first))))
THROW("Failed to create signature XML file.");
s.addFile(name->first, ofs, zproperty(name->first));
}
}

Expand Down Expand Up @@ -159,32 +137,16 @@ void ASiC_E::addAdESSignature(istream &data)
}
}

unique_ptr<Container> ASiC_E::openInternal(const string &path)
void ASiC_E::canSave()
{
DEBUG("ASiC_E::openInternal(%s)", path.c_str());
return unique_ptr<Container>(new ASiC_E(path));
if(mediaType() != MIMETYPE_ASIC_E)
THROW("'%s' format is not supported", mediaType().c_str());
}

/**
* Creates BDoc container manifest file and returns its path.
*
* @return returns created manifest file path.
* @throws Exception exception is thrown if manifest file creation failed.
*/
XMLDocument ASiC_E::createManifest() const
unique_ptr<Container> ASiC_E::openInternal(const string &path)
{
DEBUG("ASiC_E::createManifest()");
auto doc = XMLDocument::create("manifest", MANIFEST_NS, "manifest");
doc.setProperty("version", "1.2", MANIFEST_NS);
auto add = [&doc](string_view path, string_view mime) {
auto file = doc+"file-entry";
file.setProperty("full-path", path, MANIFEST_NS);
file.setProperty("media-type", mime, MANIFEST_NS);
};
add("/", mediaType());
for(const DataFile *file: dataFiles())
add(file->fileName(), file->mediaType());
return doc;
DEBUG("ASiC_E::openInternal(%s)", path.c_str());
return unique_ptr<Container>(new ASiC_E(path));
}

void ASiC_E::loadSignatures(istream &data, const string &file)
Expand Down
6 changes: 2 additions & 4 deletions src/ASiC_E.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@

namespace digidoc
{
struct 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 @@ -42,7 +40,6 @@ namespace digidoc
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 @@ -56,9 +53,10 @@ namespace digidoc
ASiC_E();
ASiC_E(const std::string &path);
DISABLE_COPY(ASiC_E);
XMLDocument createManifest() const;
void canSave() final;
void loadSignatures(std::istream &data, const std::string &file);
void parseManifestAndLoadFiles(const ZipSerialize &z);
void save(const ZipSerialize &s) final;

class Private;
std::unique_ptr<Private> d;
Expand Down
77 changes: 45 additions & 32 deletions src/ASiC_S.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,47 +34,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, mediaType());
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, mediaType());
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,11 +87,6 @@ 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*/)
{
THROW("Not implemented.");
}

unique_ptr<Container> ASiC_S::createInternal(const string & /*path*/)
{
return {};
Expand All @@ -98,6 +97,12 @@ void ASiC_S::addAdESSignature(istream & /*signature*/)
THROW("Not implemented.");
}

void ASiC_S::canSave()
{
if(auto list = signatures(); !list.empty() && list.front()->profile() != ASIC_TST_PROFILE)
THROW("ASiC-S container supports only saving TimeStampToken signatures.");
}

unique_ptr<Container> ASiC_S::openInternal(const string &path, ContainerOpenCB * /*cb*/)
{
if (!isContainerSimpleFormat(path))
Expand All @@ -111,6 +116,14 @@ Signature* ASiC_S::prepareSignature(Signer * /*signer*/)
THROW("Not implemented.");
}

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());
}

Signature *ASiC_S::sign(Signer * /*signer*/)
{
THROW("Not implemented.");
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 canSave() final;
void save(const ZipSerialize &s) final;

static bool isContainerSimpleFormat(const std::string &path);
};
}
56 changes: 51 additions & 5 deletions src/ASiContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@

#include "DataFile_p.h"
#include "Signature.h"
#include "XMLDocument.h"
#include "util/File.h"
#include "util/log.h"

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

XMLDocument ASiContainer::createManifest() const
{
DEBUG("ASiContainer::createManifest()");
auto doc = XMLDocument::create("manifest", MANIFEST_NS, "manifest");
doc.setProperty("version", "1.2", MANIFEST_NS);
auto add = [&doc](string_view path, string_view mime) {
auto file = doc+"file-entry";
file.setProperty("full-path", path, MANIFEST_NS);
file.setProperty("media-type", mime, MANIFEST_NS);
};
add("/", mediaType());
for(const DataFile *file: dataFiles())
add(file->fileName(), file->mediaType());
return doc;
}

/**
Expand Down Expand Up @@ -166,15 +184,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 +259,34 @@ void ASiContainer::deleteSignature(Signature* s)
delete s;
}

void ASiContainer::save(const string &path)
{
if(dataFiles().empty())
THROW("Can not save, container is empty.");
canSave();
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 = static_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 +297,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 1c129e6

Please sign in to comment.