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 47168f7 commit 26c7c33
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 108 deletions.
32 changes: 9 additions & 23 deletions src/ASiC_E.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,28 +88,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 +104,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,6 +139,12 @@ void ASiC_E::addAdESSignature(istream &data)
}
}

void ASiC_E::canSave()
{
if(mediaType() != MIMETYPE_ASIC_E)
THROW("'%s' format is not supported", mediaType().c_str());
}

unique_ptr<Container> ASiC_E::openInternal(const string &path)
{
DEBUG("ASiC_E::openInternal(%s)", path.c_str());
Expand Down
3 changes: 2 additions & 1 deletion src/ASiC_E.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,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 +55,11 @@ namespace digidoc
ASiC_E();
ASiC_E(const std::string &path);
DISABLE_COPY(ASiC_E);
void canSave() final;
XMLDocument createManifest() const;
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
75 changes: 43 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,12 @@ Signature* ASiC_S::prepareSignature(Signer * /*signer*/)
THROW("Not implemented.");
}

void ASiC_S::save(const ZipSerialize &s)
{
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);
};
}
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.");
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 +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
8 changes: 5 additions & 3 deletions src/ASiContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,28 +51,30 @@ namespace digidoc
std::vector<DataFile*> dataFiles() const override;
void removeDataFile(unsigned int id) override;
void removeSignature(unsigned int id) override;
void save(const std::string &path) override;
std::vector<Signature*> signatures() const override;

static std::string readMimetype(const ZipSerialize &z);

protected:
ASiContainer(std::string_view mimetype);

virtual void addDataFileChecks(const std::string &path, const std::string &mediaType);
void addDataFilePrivate(std::unique_ptr<std::istream> is, std::string fileName, std::string mediaType);
Signature* addSignature(std::unique_ptr<Signature> &&signature);
virtual void canSave() = 0;
std::unique_ptr<std::iostream> dataStream(std::string_view path, const ZipSerialize &z) const;
ZipSerialize load(const std::string &path, bool requireMimetype, const std::set<std::string_view> &supported);
virtual void save(const ZipSerialize &s) = 0;
void deleteSignature(Signature* s);

void zpath(const std::string &file);
std::string zpath() const;
const ZipSerialize::Properties &zproperty(const std::string &file) const;
const ZipSerialize::Properties& zproperty(std::string_view file) const;

private:
DISABLE_COPY(ASiContainer);

void addDataFileChecks(const std::string &path, const std::string &mediaType);

class Private;
std::unique_ptr<Private> d;
};
Expand Down
3 changes: 2 additions & 1 deletion src/SiVaContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,8 @@ unique_ptr<istream> SiVaContainer::parseDDoc(bool useHashCode)
dataFile = std::string_view{};
}
auto result = make_unique<stringstream>();
doc.save(*result);
if(!doc.save([&result](const char *data, size_t size) { result->write(data, streamsize(size)); }))
THROW("Failed to save DDoc");
return result;
}
catch(const Exception &)
Expand Down
21 changes: 15 additions & 6 deletions src/SignatureTST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,9 @@ void SignatureTST::validate() const
}
try
{
const string digestMethod = timestampToken->digestMethod();
timestampToken->verify(asicSDoc->dataFiles().front()->calcDigest(digestMethod));

if(!Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) &&
timestampToken->verify(dataToSign());
if(auto digestMethod = signatureMethod();
!Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) &&
Digest::isWeakDigest(digestMethod))
{
Exception e(EXCEPTION_PARAMS("TimeStamp '%s' digest weak", digestMethod.c_str()));
Expand All @@ -105,7 +104,12 @@ void SignatureTST::validate() const

std::vector<unsigned char> SignatureTST::dataToSign() const
{
THROW("Not implemented.");
return asicSDoc->dataFiles().front()->calcDigest(signatureMethod());
}

vector<unsigned char> SignatureTST::messageImprint() const
{
return timestampToken->messageImprint();
}

void SignatureTST::setSignatureValue(const std::vector<unsigned char> & /*signatureValue*/)
Expand All @@ -116,5 +120,10 @@ void SignatureTST::setSignatureValue(const std::vector<unsigned char> & /*signat
// Xades properties
string SignatureTST::profile() const
{
return "TimeStampToken";
return string(ASiC_S::ASIC_TST_PROFILE);
}

std::vector<unsigned char> SignatureTST::save() const
{
return *timestampToken;
}
Loading

0 comments on commit 26c7c33

Please sign in to comment.