Skip to content
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
3 changes: 3 additions & 0 deletions libdigidocpp.dox
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,9 @@ You change the default behaviour in the configuration file with the following pa
\tableofcontents
Note that Libdigidocpp uses internal memory buffers in case of all the operations, so that intermediary data is not written to temporary files on the disk. Also, the data files to be added to a DigiDoc container can be read from a data stream and later extracted from the container to a stream so that the data can be kept in memory.

\subsection string-encoding String encoding
All <tt>std::string</tt> values passed to or returned from the API are UTF-8 encoded. This applies to file paths, media types, and all other string parameters and return values throughout the library.

\subsection initialization Initialization
Libdigidocpp's initialization method conducts the following operations:
1. initializes dependent libraries (see also \ref libraries)
Expand Down
9 changes: 9 additions & 0 deletions libdigidocpp.i
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,11 @@ static std::vector<unsigned char>* SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *je
// std::unique_ptr is since swig 4.1
%ignore digidoc::Container::createPtr;
%ignore digidoc::Container::openPtr;
%ignore digidoc::Container::extendContainerValidity;

%newobject digidoc::Container::open;
%newobject digidoc::Container::create;
%newobject digidoc::Container::extendContainerValidity;

%immutable digidoc::TSAInfo::cert;
%immutable digidoc::TSAInfo::time;
Expand Down Expand Up @@ -280,7 +282,14 @@ def transfer(self):
%template(Signatures) std::vector<digidoc::Signature*>;
%template(TSAInfos) std::vector<digidoc::TSAInfo>;

%rename("%s") digidoc::Container::extendContainerValidity;

%extend digidoc::Container {
static Container* extendContainerValidity(Container &doc, Signer *signer)
{
return digidoc::Container::extendContainerValidity(doc, signer).release();
}

static digidoc::Container* open(const std::string &path, digidoc::ContainerOpenCB *cb)
{
return digidoc::Container::openPtr(path, cb).release();
Expand Down
178 changes: 75 additions & 103 deletions src/ASiC_E.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,11 @@
#include "DataFile_p.h"
#include "SignatureXAdES_LTA.h"
#include "XMLDocument.h"
#include "crypto/Digest.h"
#include "crypto/Signer.h"
#include "util/File.h"

#include <algorithm>
#include <set>
#include <sstream>

using namespace digidoc;
using namespace digidoc::util;
Expand All @@ -51,21 +49,83 @@
/**
* Initialize BDOC container.
*/
ASiC_E::ASiC_E()
: ASiContainer(MIMETYPE_ASIC_E)
ASiC_E::ASiC_E(const string &path, bool create) try
: ASiContainer(path, MIMETYPE_ASIC_E)
, d(make_unique<Private>())
{
}
if(create)
return;
auto z = load(true, {MIMETYPE_ASIC_E, MIMETYPE_ADOC});
auto doc = XMLDocument::open(z.read("META-INF/manifest.xml"), {"manifest", MANIFEST_NS});
doc.validateSchema(File::path(Conf::instance()->xsdPath(), "OpenDocument_manifest_v1_2.xsd"));

set<string_view> manifestFiles;
bool mimeFound = false;
for(auto file = doc/"file-entry"; file; file++)
{
auto full_path = file[{"full-path", MANIFEST_NS}];
auto media_type = file[{"media-type", MANIFEST_NS}];
DEBUG("full_path = '%.*s', media_type = '%.*s'", STR_VIEW_FMT(full_path), STR_VIEW_FMT(media_type));

/**
* Opens ASiC container from a file
*/
ASiC_E::ASiC_E(const string &path)
: ASiContainer(MIMETYPE_ASIC_E)
, d(make_unique<Private>())
// ODF does not specify that mimetype should be first in manifest
if(full_path == "/")
{
if(mimeFound)
THROW("Manifest multiple entries defined for file '/'.");
if(mediaType() != media_type)
THROW("Manifest has incorrect container media type defined '%.*s', expecting '%s'.", STR_VIEW_FMT(media_type), mediaType().c_str());
mimeFound = true;
continue;
}
if(full_path.back() == '/') // Skip Directory entries
continue;

if(const auto &[pos, inserted] = manifestFiles.insert(full_path); !inserted)

Check notice

Code scanning / CodeQL

Unused local variable Note

Variable pos is not used.
THROW("Manifest multiple entries defined for file '%.*s'.", STR_VIEW_FMT(full_path));
if(mediaType() == MIMETYPE_ADOC &&
(full_path.starts_with("META-INF/") || full_path.starts_with("metadata/")))
d->metadata.push_back(new DataFilePrivate(z, string(full_path), string(media_type)));
else
addDataFilePrivate(new DataFilePrivate(z, string(full_path), string(media_type)));
}
if(!mimeFound)
THROW("Manifest is missing mediatype file entry.");

for(const string &file: z.list())
{
/**
* http://www.etsi.org/deliver/etsi_ts/102900_102999/102918/01.03.01_60/ts_102918v010301p.pdf
* 6.2.2 Contents of Container
* 3) The root element of each "*signatures*.xml" content shall be either:
*/
if(file.starts_with("META-INF/") && file.contains("signatures"))
{
try
{
loadSignatures(XMLDocument::open(z.read(file)), file);
}
catch(const Exception &e)
{
THROW_CAUSE(e, "Failed to parse signature '%s'.", file.c_str());
}
continue;
}

if(file == "mimetype" || file.starts_with("META-INF"))
continue;
if(manifestFiles.erase(file) == 0)
THROW("File '%s' found in container is not described in manifest.", file.c_str());
}
if(!manifestFiles.empty())
THROW("Manifest describes files that are not found in container.");
}
catch(const Exception &e)
{
auto zip = load(path, true, {MIMETYPE_ASIC_E, MIMETYPE_ADOC});
parseManifestAndLoadFiles(zip);
THROW_CAUSE(e, "Failed to parse manifest");
}
catch(...)
{
THROW("Failed to parse manifest XML: Unknown exception");
}

ASiC_E::~ASiC_E()
Expand Down Expand Up @@ -110,9 +170,7 @@
unique_ptr<Container> ASiC_E::createInternal(const string &path)
{
DEBUG("ASiC_E::createInternal(%s)", path.c_str());
unique_ptr<ASiC_E> doc = unique_ptr<ASiC_E>(new ASiC_E);
doc->zpath(path);
return doc;
return unique_ptr<Container>(new ASiC_E(path, true));
}

/**
Expand Down Expand Up @@ -146,7 +204,7 @@
unique_ptr<Container> ASiC_E::openInternal(const string &path)
{
DEBUG("ASiC_E::openInternal(%s)", path.c_str());
return unique_ptr<Container>(new ASiC_E(path));
return unique_ptr<Container>(new ASiC_E(path, false));
}

void ASiC_E::loadSignatures(XMLDocument &&doc, const string &file)
Expand All @@ -157,92 +215,6 @@
addSignature(make_unique<SignatureXAdES_LTA>(signatures, s, this));
}

/**
* Parses manifest file and checks that files described in manifest exist, also
* checks that no extra file do exist that are not described in manifest.xml.
*
* @param path directory on disk of the BDOC container.
* @throws Exception exception is thrown if the manifest.xml file parsing failed.
*/
void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
{
DEBUG("ASiC_E::readManifest()");

try
{
auto doc = XMLDocument::open(z.read("META-INF/manifest.xml"), {"manifest", MANIFEST_NS});
doc.validateSchema(File::path(Conf::instance()->xsdPath(), "OpenDocument_manifest_v1_2.xsd"));

set<string_view> manifestFiles;
bool mimeFound = false;
for(auto file = doc/"file-entry"; file; file++)
{
auto full_path = file[{"full-path", MANIFEST_NS}];
auto media_type = file[{"media-type", MANIFEST_NS}];
DEBUG("full_path = '%s', media_type = '%s'", full_path.data(), media_type.data());

if(manifestFiles.contains(full_path))
THROW("Manifest multiple entries defined for file '%s'.", full_path.data());

// ODF does not specify that mimetype should be first in manifest
if(full_path == "/")
{
if(mediaType() != media_type)
THROW("Manifest has incorrect container media type defined '%s', expecting '%s'.", media_type.data(), mediaType().c_str());
mimeFound = true;
continue;
}
if(full_path.back() == '/') // Skip Directory entries
continue;

manifestFiles.insert(full_path);
if(mediaType() == MIMETYPE_ADOC &&
(full_path.starts_with("META-INF/") ||
full_path.starts_with("metadata/")))
d->metadata.push_back(new DataFilePrivate(z, string(full_path), string(media_type)));
else
addDataFilePrivate(new DataFilePrivate(z, string(full_path), string(media_type)));
}
if(!mimeFound)
THROW("Manifest is missing mediatype file entry.");

for(const string &file: z.list())
{
/**
* http://www.etsi.org/deliver/etsi_ts/102900_102999/102918/01.03.01_60/ts_102918v010301p.pdf
* 6.2.2 Contents of Container
* 3) The root element of each "*signatures*.xml" content shall be either:
*/
if(file.starts_with("META-INF/") &&
file.find("signatures") != string::npos)
{
try
{
loadSignatures(XMLDocument::open(z.read(file)), file);
}
catch(const Exception &e)
{
THROW_CAUSE(e, "Failed to parse signature '%s'.", file.c_str());
}
continue;
}

if(file == "mimetype" || file.starts_with("META-INF"))
continue;
if(!manifestFiles.contains(file))
THROW("File '%s' found in container is not described in manifest.", file.c_str());
}
}
catch(const Exception &e)
{
THROW_CAUSE(e, "Failed to parse manifest");
}
catch(...)
{
THROW("Failed to parse manifest XML: Unknown exception");
}
}

Signature* ASiC_E::prepareSignature(Signer *signer)
{
if(mediaType() != MIMETYPE_ASIC_E)
Expand Down
4 changes: 1 addition & 3 deletions src/ASiC_E.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,10 @@ namespace digidoc
static std::unique_ptr<Container> openInternal(const std::string &path);

private:
ASiC_E();
ASiC_E(const std::string &path);
ASiC_E(const std::string &path, bool create);
DISABLE_COPY(ASiC_E);
void canSave() final;
void loadSignatures(XMLDocument &&doc, const std::string &file);
void parseManifestAndLoadFiles(const ZipSerialize &z);
void save(const ZipSerialize &s) final;

class Private;
Expand Down
23 changes: 7 additions & 16 deletions src/ASiC_S.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,19 @@
#include "util/File.h"
#include "util/log.h"

#include <sstream>

using namespace digidoc;
using namespace digidoc::util;
using namespace std;

/**
* Initialize ASiCS container.
*/
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, bool create)
: ASiContainer(path, MIMETYPE_ASIC_S)
{
auto z = load(path, false, {mediaType()});
if(create)
return;
auto z = load(false, {mediaType()});
bool foundTimestamp = false;
bool foundManifest = false;
for(const string &file: z.list())
Expand Down Expand Up @@ -108,9 +101,7 @@ 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;
return unique_ptr<Container>(new ASiC_S(path, true));
}

void ASiC_S::addAdESSignature(istream & /*signature*/)
Expand All @@ -131,7 +122,7 @@ unique_ptr<Container> ASiC_S::openInternal(const string &path, ContainerOpenCB *
{
if(util::File::fileExtension(path, {"asice", "sce", "bdoc"}))
return {};
return unique_ptr<Container>(new ASiC_S(path));
return unique_ptr<Container>(new ASiC_S(path, false));
}
catch(const Exception &)
{
Expand Down
3 changes: 1 addition & 2 deletions src/ASiC_S.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ namespace digidoc
static std::unique_ptr<Container> openInternal(const std::string &path, ContainerOpenCB *cb);

private:
ASiC_S();
ASiC_S(const std::string &path);
ASiC_S(const std::string &path, bool create);
DISABLE_COPY(ASiC_S);

void addDataFileChecks(const std::string &path, const std::string &mediaType) override;
Expand Down
Loading