Skip to content

Commit

Permalink
ZIP Streaming
Browse files Browse the repository at this point in the history
IB-7625

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma committed Jul 25, 2023
1 parent a3773cb commit a9b50aa
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 91 deletions.
19 changes: 8 additions & 11 deletions src/ASiC_E.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ ASiC_E::ASiC_E(const string &path)
: ASiContainer(MIMETYPE_ASIC_E)
, d(make_unique<Private>())
{
auto zip = load(path, true, {MIMETYPE_ASIC_E, MIMETYPE_ADOC});
auto *zip = load(path, true, {MIMETYPE_ASIC_E, MIMETYPE_ADOC});
parseManifestAndLoadFiles(*zip);
}

Expand Down Expand Up @@ -219,20 +219,19 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
DEBUG("ASiC_E::readManifest()");

const vector<string> &list = z.list();
size_t mcount = size_t(count(list.cbegin(), list.cend(), "META-INF/manifest.xml"));
auto mcount = size_t(count(list.cbegin(), list.cend(), "META-INF/manifest.xml"));
if(mcount < 1)
THROW("Manifest file is missing");
if(mcount > 1)
THROW("Found multiple manifest files");

try
{
stringstream manifestdata;
z.extract("META-INF/manifest.xml", manifestdata);
unique_ptr<istream> manifestdata = z.stream("META-INF/manifest.xml");
xml_schema::Properties p;
p.schema_location(ASiC_E::MANIFEST_NAMESPACE,
File::fullPathUrl(Conf::instance()->xsdPath() + "/OpenDocument_manifest.xsd"));
unique_ptr<xercesc::DOMDocument> doc = SecureDOMParser(p.schema_location(), true).parseIStream(manifestdata);
unique_ptr<xercesc::DOMDocument> doc = SecureDOMParser(p.schema_location(), true).parseIStream(*manifestdata);
unique_ptr<manifest::Manifest> manifest = manifest::manifest(*doc, {}, p);

set<string> manifestFiles;
Expand All @@ -255,7 +254,7 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
if(file.full_path().back() == '/') // Skip Directory entries
continue;

size_t fcount = size_t(count(list.cbegin(), list.cend(), file.full_path()));
auto fcount = size_t(count(list.cbegin(), list.cend(), file.full_path()));
if(fcount < 1)
THROW("File described in manifest '%s' does not exist in container.", file.full_path().c_str());
if(fcount > 1)
Expand All @@ -265,9 +264,9 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
if(mediaType() == MIMETYPE_ADOC &&
(file.full_path().compare(0, 9, "META-INF/") == 0 ||
file.full_path().compare(0, 9, "metadata/") == 0))
d->metadata.push_back(new DataFilePrivate(dataStream(file.full_path(), z), file.full_path(), file.media_type()));
d->metadata.push_back(dataFile(file.full_path(), file.media_type()));
else
addDataFilePrivate(dataStream(file.full_path(), z), file.full_path(), file.media_type());
addDataFilePrivate(file.full_path(), file.media_type());
}
if(!mimeFound)
THROW("Manifest is missing mediatype file entry.");
Expand All @@ -286,9 +285,7 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
THROW("Multiple signature files with same name found '%s'", file.c_str());
try
{
stringstream data;
z.extract(file, data);
auto signatures = make_shared<Signatures>(data, this);
auto signatures = make_shared<Signatures>(*z.stream(file), this);
for(size_t i = 0, count = signatures->count(); i < count; ++i)
addSignature(make_unique<SignatureXAdES_LTA>(signatures, i, this));
}
Expand Down
12 changes: 4 additions & 8 deletions src/ASiC_S.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ ASiC_S::ASiC_S(): ASiContainer(MIMETYPE_ASIC_S)
*/
ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S)
{
auto z = load(path, false, {mediaType()});
auto *z = load(path, false, {mediaType()});
static const string_view metaInf = "META-INF/";

for(const string &file: z->list())
Expand All @@ -55,17 +55,13 @@ ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S)
{
if(!signatures().empty())
THROW("Can not add signature to ASiC-S container which already contains a signature.");
stringstream data;
z->extract(file, data);
addSignature(make_unique<SignatureTST>(data, this));
addSignature(make_unique<SignatureTST>(*z->stream(file), this));
}
if(file == "META-INF/signatures.xml")
{
if(!signatures().empty())
THROW("Can not add signature to ASiC-S container which already contains a signature.");
stringstream data;
z->extract(file, data);
auto signatures = make_shared<Signatures>(data, this);
auto signatures = make_shared<Signatures>(*z->stream(file), this);
for(size_t i = 0, count = signatures->count(); i < count; ++i)
addSignature(make_unique<SignatureXAdES_LTA>(signatures, i, this));
}
Expand All @@ -77,7 +73,7 @@ ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S)
{
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");
addDataFilePrivate(file, "application/octet-stream");
}
}

Expand Down
66 changes: 23 additions & 43 deletions src/ASiContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class ASiContainer::Private
vector<DataFile*> documents;
vector<Signature*> signatures;
map<string, ZipSerialize::Properties> properties;
unique_ptr<ZipSerialize> z;
};

const string_view ASiContainer::ASICE_EXTENSION = "asice";
Expand Down Expand Up @@ -76,12 +77,12 @@ ASiContainer::ASiContainer(const string &mimetype)
* @param supported supported mimetypes.
* @return returns zip serializer for the container.
*/
unique_ptr<ZipSerialize> ASiContainer::load(const string &path, bool mimetypeRequired, const set<string> &supported)
ZipSerialize* ASiContainer::load(const string &path, bool mimetypeRequired, const set<string> &supported)
{
DEBUG("ASiContainer::ASiContainer(path = '%s')", path.c_str());
auto z = make_unique<ZipSerialize>(d->path = path, false);
d->z = make_unique<ZipSerialize>(d->path = path, false);

vector<string> list = z->list();
vector<string> list = d->z->list();
if(list.empty())
THROW("Failed to parse container");

Expand All @@ -91,16 +92,16 @@ unique_ptr<ZipSerialize> ASiContainer::load(const string &path, bool mimetypeReq
// ETSI TS 102 918: mimetype has to be the first in the archive
if(list.front() == "mimetype")
{
d->mimetype = readMimetype(*z);
d->mimetype = readMimetype(*d->z);
DEBUG("mimetype = '%s'", d->mimetype.c_str());
if(supported.find(d->mimetype) == supported.cend())
THROW("Incorrect mimetype '%s'", d->mimetype.c_str());
}

for(const string &file: list)
d->properties[file] = z->properties(file);
d->properties[file] = d->z->properties(file);

return z;
return d->z.get();
}

string ASiContainer::mediaType() const
Expand Down Expand Up @@ -138,25 +139,9 @@ vector<Signature *> ASiContainer::signatures() const
return d->signatures;
}

/**
* <p>
* Read a datafile from container.
* </p>
* If expected size of the data is too big, then stream is written to temp file.
*
* @param path name of the file in zip container stream is used to read from.
* @param z Zip container.
* @return returns data as a stream.
*/
unique_ptr<iostream> ASiContainer::dataStream(const string &path, const ZipSerialize &z) const
DataFilePrivate* ASiContainer::dataFile(const std::string &path, const std::string &mediaType) const
{
unique_ptr<iostream> data;
if(d->properties[path].size > MAX_MEM_FILE)
data = make_unique<fstream>(File::encodeName(File::tempFileName()), fstream::in|fstream::out|fstream::binary|fstream::trunc);
else
data = make_unique<stringstream>();
z.extract(path, *data);
return data;
return new DataFilePrivate(d->z->stream(path), path, mediaType, d->properties[path].size);
}

/**
Expand Down Expand Up @@ -191,15 +176,16 @@ void ASiContainer::addDataFile(const string &path, const string &mediaType)
*data << file.rdbuf();
is = std::move(data);
}
addDataFilePrivate(std::move(is), fileName, mediaType);
d->documents.push_back(new DataFilePrivate(std::move(is), fileName, mediaType, prop.size));
}

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);
istream::pos_type pos = is->tellg();
d->documents.push_back(new DataFilePrivate(std::move(is), fileName, mediaType, pos < 0 ? 0 : (unsigned long)pos));
}

void ASiContainer::addDataFileChecks(const string &fileName, const string &mediaType)
Expand All @@ -214,9 +200,9 @@ void ASiContainer::addDataFileChecks(const string &fileName, const string &media
THROW("MediaType does not meet format requirements (RFC2045, section 5.1) '%s'.", mediaType.c_str());
}

void ASiContainer::addDataFilePrivate(unique_ptr<istream> is, const string &fileName, const string &mediaType)
void ASiContainer::addDataFilePrivate(const string &fileName, const string &mediaType)
{
d->documents.push_back(new DataFilePrivate(std::move(is), fileName, mediaType));
d->documents.push_back(dataFile(fileName, mediaType));
}

/**
Expand Down Expand Up @@ -298,23 +284,17 @@ void ASiContainer::zproperty(const string &file, ZipSerialize::Properties &&prop
string ASiContainer::readMimetype(const ZipSerialize &z)
{
DEBUG("ASiContainer::readMimetype()");
stringstream is;
z.extract("mimetype", is);

array<unsigned char,3> bom{};
is.read((char*)bom.data(), bom.size());
auto is = z.stream("mimetype");
string text;
*is >> text;
if(!is)
THROW("Failed to read mimetype.");
// Contains UTF-16 BOM
if((bom[0] == 0xFF && bom[1] == 0xEF) ||
(bom[0] == 0xEF && bom[1] == 0xFF))
if((text[0] == 0xFF && text[1] == 0xEF) ||

Check warning

Code scanning / CodeQL

Comparison result is always the same Warning

Comparison is always false because call to operator\[\] <= 127.

Check warning

Code scanning / CodeQL

Comparison result is always the same Warning

Comparison is always false because call to operator\[\] <= 127.
(text[0] == 0xEF && text[1] == 0xFF))

Check warning

Code scanning / CodeQL

Comparison result is always the same Warning

Comparison is always false because call to operator\[\] <= 127.

Check warning

Code scanning / CodeQL

Comparison result is always the same Warning

Comparison is always false because call to operator\[\] <= 127.
THROW("Mimetype file must be UTF-8 format.");
// does not contain UTF-8 BOM reset pos
if(bom[0] != 0xEF || bom[1] != 0xBB || bom[2] != 0xBF)
is.seekg(0, ios::beg);

string text;
is >> text;
if(is.fail())
THROW("Failed to read mimetype.");

if(text[0] == 0xEF && text[1] == 0xBB && text[2] != 0xBF)

Check warning

Code scanning / CodeQL

Comparison result is always the same Warning

Comparison is always false because call to operator\[\] <= 127.

Check warning

Code scanning / CodeQL

Comparison result is always the same Warning

Comparison is always false because call to operator\[\] <= 127.

Check warning

Code scanning / CodeQL

Comparison result is always the same Warning

Comparison is always true because call to operator\[\] <= 127.
text.erase(text.cbegin(), text.cbegin() + 3);
return text;
}
7 changes: 4 additions & 3 deletions src/ASiContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

namespace digidoc
{
class DataFilePrivate;
/**
* Base class for the ASiC (Associated Signature Container) documents.
* Implements the operations and data structures common for more specific ASiC
Expand Down Expand Up @@ -61,10 +62,10 @@ namespace digidoc
protected:
ASiContainer(const std::string &mimetype);

void addDataFilePrivate(std::unique_ptr<std::istream> is, const std::string &fileName, const std::string &mediaType);
void addDataFilePrivate(const std::string &fileName, const std::string &mediaType);
Signature* addSignature(std::unique_ptr<Signature> &&signature);
std::unique_ptr<std::iostream> dataStream(const std::string &path, const ZipSerialize &z) const;
std::unique_ptr<ZipSerialize> load(const std::string &path, bool requireMimetype, const std::set<std::string> &supported);
DataFilePrivate *dataFile(const std::string &path, const std::string &mediaType) const;
ZipSerialize* load(const std::string &path, bool requireMimetype, const std::set<std::string> &supported);
void deleteSignature(Signature* s);

void zpath(const std::string &file);
Expand Down
9 changes: 3 additions & 6 deletions src/DataFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,13 @@ DataFile::DataFile() = default;
DataFile::~DataFile() = default;


DataFilePrivate::DataFilePrivate(unique_ptr<istream> &&is, string filename, string mediatype, string id)
DataFilePrivate::DataFilePrivate(unique_ptr<istream> &&is, string filename, string mediatype, unsigned long size, string id)
: m_is(std::move(is))
, m_id(std::move(id))
, m_filename(std::move(filename))
, m_mediatype(std::move(mediatype))
{
m_is->seekg(0, istream::end);
istream::pos_type pos = m_is->tellg();
m_size = pos < 0 ? 0 : (unsigned long)pos;
}
, m_size(size)
{}

vector<unsigned char> DataFilePrivate::calcDigest(const string &method) const
{
Expand Down
2 changes: 1 addition & 1 deletion src/DataFile_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace digidoc
class DataFilePrivate final: public DataFile
{
public:
DataFilePrivate(std::unique_ptr<std::istream> &&is, std::string filename, std::string mediatype, std::string id = {});
DataFilePrivate(std::unique_ptr<std::istream> &&is, std::string filename, std::string mediatype, unsigned long size, std::string id = {});

std::string id() const final { return m_id.empty() ? m_filename : m_id; }
std::string fileName() const final { return m_filename; }
Expand Down
3 changes: 2 additions & 1 deletion src/SiVaContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHash
else
{
d->mediaType = "application/pdf";
d->dataFiles.push_back(new DataFilePrivate(std::move(ifs), fileName, "application/pdf"));
d->dataFiles.push_back(new DataFilePrivate(std::move(ifs), fileName, "application/pdf", File::fileSize(d->path)));
}

array<XMLByte, 48*100> buf{};
Expand Down Expand Up @@ -351,6 +351,7 @@ unique_ptr<istream> SiVaContainer::parseDDoc(bool useHashCode)
d->dataFiles.push_back(new DataFilePrivate(make_unique<stringstream>(base64_decode(b64)),
xml::transcode<char>(item->getAttribute(cpXMLCh(u"Filename"))),
xml::transcode<char>(item->getAttribute(cpXMLCh(u"MimeType"))),
0,
xml::transcode<char>(item->getAttribute(cpXMLCh(u"Id")))));
}

Expand Down
15 changes: 5 additions & 10 deletions src/SignatureTST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,16 @@
#include "util/DateTime.h"
#include "util/log.h"

#include <sstream>

using namespace digidoc;
using namespace std;

SignatureTST::SignatureTST(istream &is, ASiC_S *asicSDoc): asicSDoc(asicSDoc)
{
is.seekg(0, istream::end);
istream::pos_type pos = is.tellg();
const auto size = pos < 0 ? 0 : (unsigned long)pos;
is.clear();
is.seekg(0, istream::beg);

vector<unsigned char> buf(size, 0);
is.read((char*)buf.data(), streamsize(buf.size()));

timestampToken = make_unique<TS>(buf.data(), buf.size());
stringstream data;
data << is.rdbuf();
timestampToken = make_unique<TS>(data.str());
}

SignatureTST::~SignatureTST() = default;
Expand Down
2 changes: 2 additions & 0 deletions src/crypto/TS.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class TS
{
public:
TS(const std::string &url, const Digest &digest);
template <class Container>
inline TS(const Container &data): TS((const unsigned char*)data.data(), data.size()) {}
TS(const unsigned char *data = nullptr, size_t size = 0);

X509Cert cert() const;
Expand Down
Loading

0 comments on commit a9b50aa

Please sign in to comment.