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

ASiC-S TimeStamp save support #643

Merged
merged 1 commit into from
Nov 25, 2024
Merged
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
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