Skip to content

Commit e4bcf86

Browse files
metsmakristelmerilain
authored andcommitted
Fix ASiC-E/S CAdES signature save
IB-8309 Signed-off-by: Raul Metsma <[email protected]>
1 parent 963eb07 commit e4bcf86

File tree

10 files changed

+85
-75
lines changed

10 files changed

+85
-75
lines changed

src/ASiC_E.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
182182
auto media_type = file[{"media-type", MANIFEST_NS}];
183183
DEBUG("full_path = '%s', media_type = '%s'", full_path.data(), media_type.data());
184184

185-
if(manifestFiles.find(full_path) != manifestFiles.end())
185+
if(manifestFiles.contains(full_path))
186186
THROW("Manifest multiple entries defined for file '%s'.", full_path.data());
187187

188188
// ODF does not specify that mimetype should be first in manifest
@@ -198,11 +198,11 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
198198

199199
manifestFiles.insert(full_path);
200200
if(mediaType() == MIMETYPE_ADOC &&
201-
(full_path.compare(0, 9, "META-INF/") == 0 ||
202-
full_path.compare(0, 9, "metadata/") == 0))
203-
d->metadata.push_back(new DataFilePrivate(dataStream(full_path, z), string(full_path), string(media_type)));
201+
(full_path.starts_with("META-INF/") ||
202+
full_path.starts_with("metadata/")))
203+
d->metadata.push_back(new DataFilePrivate(z, string(full_path), string(media_type)));
204204
else
205-
addDataFilePrivate(dataStream(full_path, z), string(full_path), string(media_type));
205+
addDataFilePrivate(new DataFilePrivate(z, string(full_path), string(media_type)));
206206
}
207207
if(!mimeFound)
208208
THROW("Manifest is missing mediatype file entry.");
@@ -214,7 +214,7 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
214214
* 6.2.2 Contents of Container
215215
* 3) The root element of each "*signatures*.xml" content shall be either:
216216
*/
217-
if(file.compare(0, 9, "META-INF/") == 0 &&
217+
if(file.starts_with("META-INF/") &&
218218
file.find("signatures") != string::npos)
219219
{
220220
try
@@ -229,9 +229,9 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
229229
continue;
230230
}
231231

232-
if(file == "mimetype" || file.compare(0, 8,"META-INF") == 0)
232+
if(file == "mimetype" || file.starts_with("META-INF"))
233233
continue;
234-
if(manifestFiles.count(file) == 0)
234+
if(!manifestFiles.contains(file))
235235
THROW("File '%s' found in container is not described in manifest.", file.c_str());
236236
}
237237
}

src/ASiC_S.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "ASiC_S.h"
2121

22+
#include "DataFile_p.h"
2223
#include "SignatureTST.h"
2324
#include "SignatureXAdES_LTA.h"
2425
#include "crypto/Signer.h"
@@ -78,7 +79,10 @@ ASiC_S::ASiC_S(const string &path)
7879
else if(!dataFiles().empty())
7980
THROW("Can not add document to ASiC-S container which already contains a document.");
8081
else
81-
addDataFile(dataStream(file, z), file, "application/octet-stream");
82+
{
83+
addDataFileChecks(file, "application/octet-stream");
84+
addDataFilePrivate(new DataFilePrivate(z, file, "application/octet-stream"));
85+
}
8286
}
8387
if(foundTimestamp && !foundManifest)
8488
{

src/ASiContainer.cpp

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ using namespace digidoc;
3636
using namespace digidoc::util;
3737
using namespace std;
3838

39-
constexpr unsigned long MAX_MEM_FILE = 500UL*1024UL*1024UL;
40-
4139
class ASiContainer::Private
4240
{
4341
public:
@@ -139,23 +137,6 @@ vector<Signature *> ASiContainer::signatures() const
139137
return d->signatures;
140138
}
141139

142-
/**
143-
* <p>
144-
* Read a datafile from container.
145-
* </p>
146-
* If expected size of the data is too big, then stream is written to temp file.
147-
*
148-
* @param path name of the file in zip container stream is used to read from.
149-
* @param z Zip container.
150-
* @return returns data as a stream.
151-
*/
152-
unique_ptr<iostream> ASiContainer::dataStream(string_view path, const ZipSerialize &z) const
153-
{
154-
if(auto i = d->properties.find(path); i != d->properties.cend() && i->second.size > MAX_MEM_FILE)
155-
return make_unique<fstream>(z.extract<fstream>(path));
156-
return make_unique<stringstream>(z.extract<stringstream>(path));
157-
}
158-
159140
/**
160141
* Adds document to the container. Documents can be removed from container only
161142
* after all signatures are removed.
@@ -208,9 +189,9 @@ void ASiContainer::addDataFileChecks(const string &fileName, const string &media
208189
THROW("MediaType does not meet format requirements (RFC2045, section 5.1) '%s'.", mediaType.c_str());
209190
}
210191

211-
void ASiContainer::addDataFilePrivate(unique_ptr<istream> is, string fileName, string mediaType)
192+
void ASiContainer::addDataFilePrivate(DataFile *dataFile)
212193
{
213-
d->documents.push_back(new DataFilePrivate(std::move(is), std::move(fileName), std::move(mediaType)));
194+
d->documents.push_back(dataFile);
214195
}
215196

216197
/**

src/ASiContainer.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,10 @@ namespace digidoc
6464
ASiContainer(std::string_view mimetype);
6565

6666
virtual void addDataFileChecks(const std::string &path, const std::string &mediaType);
67-
void addDataFilePrivate(std::unique_ptr<std::istream> is, std::string fileName, std::string mediaType);
67+
void addDataFilePrivate(DataFile *dataFile);
6868
Signature* addSignature(std::unique_ptr<Signature> &&signature);
6969
virtual void canSave() = 0;
7070
XMLDocument createManifest() const;
71-
std::unique_ptr<std::iostream> dataStream(std::string_view path, const ZipSerialize &z) const;
7271
ZipSerialize load(const std::string &path, bool requireMimetype, const std::set<std::string_view> &supported);
7372
virtual void save(const ZipSerialize &s) = 0;
7473
void deleteSignature(Signature* s);

src/DataFile.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,15 @@
2222
#include "crypto/Digest.h"
2323
#include "util/File.h"
2424
#include "util/log.h"
25+
#include "util/ZipSerialize.h"
2526

2627
#include <fstream>
28+
#include <sstream>
2729

2830
using namespace digidoc;
2931
using namespace digidoc::util;
3032
using namespace std;
3133

32-
3334
/**
3435
* @class digidoc::DataFile
3536
*
@@ -96,6 +97,19 @@ DataFilePrivate::DataFilePrivate(unique_ptr<istream> &&is, string filename, stri
9697
{
9798
}
9899

100+
DataFilePrivate::DataFilePrivate(const ZipSerialize &z, string filename, string mediatype)
101+
: d(make_unique<Private>())
102+
, m_filename(std::move(filename))
103+
, m_mediatype(std::move(mediatype))
104+
{
105+
auto prop = z.properties(m_filename);
106+
d->size.emplace(prop.size);
107+
if(d->size.value() > MAX_MEM_FILE)
108+
m_is = make_unique<fstream>(z.extract<fstream>(m_filename));
109+
else
110+
m_is = make_unique<stringstream>(z.extract<stringstream>(m_filename));
111+
}
112+
99113
void DataFilePrivate::digest(const Digest &digest) const
100114
{
101115
m_is->clear();

src/DataFile_p.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,17 @@
2727

2828
namespace digidoc
2929
{
30+
31+
constexpr unsigned long MAX_MEM_FILE = 500UL*1024UL*1024UL;
32+
3033
class Digest;
34+
class ZipSerialize;
3135

3236
class DataFilePrivate final: public DataFile
3337
{
3438
public:
3539
DataFilePrivate(std::unique_ptr<std::istream> &&is, std::string filename, std::string mediatype, std::string id = {});
40+
DataFilePrivate(const ZipSerialize &z, std::string filename, std::string mediatype);
3641

3742
std::string id() const final { return m_id.empty() ? m_filename : m_id; }
3843
std::string fileName() const final { return m_filename; }

src/SiVaContainer.cpp

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,14 @@ static auto base64_decode(string_view data) {
5959
0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x64, 0x64, 0x64, 0x64, 0x64
6060
};
6161

62-
auto out = make_unique<stringstream>();
62+
string buf;
63+
buf.reserve(base64_enc_size(data.size()));
64+
auto out = make_unique<stringstream>(std::move(buf));
6365
int value = 0;
6466
int bits = -8;
6567
for(auto c: data)
6668
{
67-
if(c == '\r' || c == '\n' || c == ' ')
69+
if(c == '\r' || c == '\n' || c == ' ' || static_cast<uint8_t>(c) > 128)
6870
continue;
6971
uint8_t check = T[c];
7072
if(check == 0x64)
@@ -83,8 +85,8 @@ static auto base64_decode(string_view data) {
8385
class SiVaContainer::Private
8486
{
8587
public:
86-
string path, mediaType;
87-
unique_ptr<istream> ddoc;
88+
std::filesystem::path path;
89+
string mediaType;
8890
vector<DataFile*> dataFiles;
8991
vector<Signature*> signatures;
9092
};
@@ -109,26 +111,32 @@ void SignatureSiVa::validate(const string &policy) const
109111
static const set<string_view> QES = { "QESIG", "QES", "QESEAL",
110112
"ADESEAL_QC", "ADESEAL" }; // Special treamtent for E-Seals
111113
Exception e(EXCEPTION_PARAMS("Signature validation"));
112-
for(const Exception &exception: _exceptions)
113-
e.addCause(exception);
114-
if(!Exception::hasWarningIgnore(Exception::SignatureDigestWeak) &&
115-
Digest::isWeakDigest(_signatureMethod))
116-
{
117-
Exception ex(EXCEPTION_PARAMS("Signature digest weak"));
118-
ex.setCode(Exception::SignatureDigestWeak);
119-
e.addCause(ex);
120-
}
121-
if(_indication == "TOTAL-PASSED")
122-
{
123-
if(QES.count(_signatureLevel) || _signatureLevel.empty() || policy == POLv1)
114+
try {
115+
for(const Exception &exception: _exceptions)
116+
e.addCause(exception);
117+
if(!Exception::hasWarningIgnore(Exception::SignatureDigestWeak) &&
118+
Digest::isWeakDigest(_signatureMethod))
119+
{
120+
Exception ex(EXCEPTION_PARAMS("Signature digest weak"));
121+
ex.setCode(Exception::SignatureDigestWeak);
122+
e.addCause(ex);
123+
}
124+
if(_indication == "TOTAL-PASSED")
124125
{
125-
if(!e.causes().empty())
126-
throw e;
127-
return;
126+
if(QES.contains(_signatureLevel) || _signatureLevel.empty() || policy == POLv1)
127+
{
128+
if(!e.causes().empty())
129+
throw e;
130+
return;
131+
}
132+
Exception ex(EXCEPTION_PARAMS("Signing certificate does not meet Qualification requirements"));
133+
ex.setCode(Exception::CertificateIssuerMissing);
134+
e.addCause(ex);
128135
}
129-
Exception ex(EXCEPTION_PARAMS("Signing certificate does not meet Qualification requirements"));
130-
ex.setCode(Exception::CertificateIssuerMissing);
136+
} catch(const Exception &ex) {
131137
e.addCause(ex);
138+
} catch(...) {
139+
EXCEPTION_ADD(e, "Failed to validate signature");
132140
}
133141
if(!e.causes().empty())
134142
throw e;
@@ -139,14 +147,13 @@ SiVaContainer::SiVaContainer(const string &path, ContainerOpenCB *cb, bool useHa
139147
: d(make_unique<Private>())
140148
{
141149
DEBUG("SiVaContainer::SiVaContainer(%s, %d)", path.c_str(), useHashCode);
142-
unique_ptr<istream> ifs = make_unique<ifstream>(File::encodeName(d->path = path), ifstream::binary);
150+
unique_ptr<istream> ifs = make_unique<ifstream>(d->path = File::encodeName(path), ifstream::binary);
143151
auto fileName = File::fileName(path);
144152
istream *is = ifs.get();
145153
if(File::fileExtension(path, {"ddoc"}))
146154
{
147155
d->mediaType = "application/x-ddoc";
148-
d->ddoc = std::move(ifs);
149-
ifs = parseDDoc(useHashCode);
156+
ifs = parseDDoc(ifs, useHashCode);
150157
is = ifs.get();
151158
}
152159
else if(File::fileExtension(path, {"pdf"}))
@@ -156,7 +163,6 @@ SiVaContainer::SiVaContainer(const string &path, ContainerOpenCB *cb, bool useHa
156163
}
157164
else if(File::fileExtension(path, {"asice", "sce", "asics", "scs"}))
158165
{
159-
static const string_view metaInf = "META-INF/";
160166
ZipSerialize z(path, false);
161167
vector<string> list = z.list();
162168
if(list.front() != "mimetype")
@@ -165,17 +171,17 @@ SiVaContainer::SiVaContainer(const string &path, ContainerOpenCB *cb, bool useHa
165171
d->mediaType != ASiContainer::MIMETYPE_ASIC_E && d->mediaType != ASiContainer::MIMETYPE_ASIC_S)
166172
THROW("Unknown file");
167173
if(none_of(list.cbegin(), list.cend(), [](const string &file) {
168-
return file.rfind(metaInf, 0) == 0 && util::File::fileExtension(file, {"p7s"});
174+
return file.starts_with("META-INF/") && util::File::fileExtension(file, {"p7s"});
169175
}))
170176
THROW("Unknown file");
171177

172178
for(const string &file: list)
173179
{
174-
if(file == "mimetype" || file.rfind(metaInf, 0) == 0)
180+
if(file == "mimetype" || file.starts_with("META-INF/"))
175181
continue;
176182
if(const auto directory = File::directory(file);
177183
directory.empty() || directory == "/" || directory == "./")
178-
d->dataFiles.push_back(new DataFilePrivate(make_unique<stringstream>(z.extract<stringstream>(file)), file, "application/octet-stream"));
184+
d->dataFiles.push_back(new DataFilePrivate(z, file, "application/octet-stream"));
179185
}
180186
}
181187
else
@@ -339,11 +345,11 @@ unique_ptr<Container> SiVaContainer::openInternal(const string &path, ContainerO
339345
}
340346
}
341347

342-
unique_ptr<istream> SiVaContainer::parseDDoc(bool useHashCode)
348+
unique_ptr<istream> SiVaContainer::parseDDoc(const unique_ptr<istream> &ddoc, bool useHashCode)
343349
{
344350
try
345351
{
346-
auto doc = XMLDocument::openStream(*d->ddoc, {}, true);
352+
auto doc = XMLDocument::openStream(*ddoc, {}, true);
347353
for(auto dataFile = doc/"DataFile"; dataFile; dataFile++)
348354
{
349355
auto contentType = dataFile["ContentType"];
@@ -401,16 +407,13 @@ void SiVaContainer::removeSignature(unsigned int /*index*/)
401407

402408
void SiVaContainer::save(const string &path)
403409
{
404-
string to = path.empty() ? d->path : path;
405-
if(d->ddoc)
406-
{
407-
d->ddoc->clear();
408-
d->ddoc->seekg(0);
409-
if(ofstream out{File::encodeName(to), ofstream::binary})
410-
out << d->ddoc->rdbuf();
411-
}
412-
else
413-
d->dataFiles[0]->saveAs(to);
410+
if(d->path.empty() || path.empty())
411+
return; // This is readonly container
412+
auto dest = File::encodeName(path);
413+
if(std::error_code ec;
414+
!std::filesystem::copy_file(d->path, dest, ec))
415+
THROW("Failed to save container");
416+
d->path = std::move(dest);
414417
}
415418

416419
Signature *SiVaContainer::sign(Signer * /*signer*/)

src/SiVaContainer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ class SiVaContainer final: public Container
105105
SiVaContainer(const std::string &path, ContainerOpenCB *cb, bool useHashCode);
106106
DISABLE_COPY(SiVaContainer);
107107

108-
std::unique_ptr<std::istream> parseDDoc(bool useHashCode);
108+
std::unique_ptr<std::istream> parseDDoc(const std::unique_ptr<std::istream> &ddoc, bool useHashCode);
109109

110110
class Private;
111111
std::unique_ptr<Private> d;

src/util/ZipSerialize.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ ZipSerialize::Properties ZipSerialize::properties(const string &file) const
198198
{
199199
int unzResult = unzLocateFile(d.get(), file.c_str(), 1);
200200
if(unzResult != UNZ_OK)
201-
THROW("Failed to open file inside ZIP container. ZLib error: %d", unzResult);
201+
THROW("Failed to locate '%s' inside ZIP container. ZLib error: %d", file.c_str(), unzResult);
202202

203203
unz_file_info info;
204204
unzResult = unzGetCurrentFileInfo(d.get(), &info, nullptr, 0, nullptr, 0, nullptr, 0);

test/libdigidocpp_boost.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,8 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(signature, Doc, DocTypes)
335335
// Reload from file and validate
336336
d = Container::openPtr(Doc::EXT + ".tmp");
337337
BOOST_CHECK_EQUAL(d->signatures().size(), 2U);
338+
if(d->signatures().empty())
339+
return;
338340
if(s3 = d->signatures().back(); s3)
339341
{
340342
BOOST_CHECK_EQUAL(s3->signingCertificate(), signer3.cert());
@@ -401,6 +403,8 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(files, Doc, DocTypes)
401403
d->save(data + Doc::EXT + ".tmp");
402404
d = Container::openPtr(data + Doc::EXT + ".tmp");
403405
BOOST_CHECK_EQUAL(d->signatures().size(), 1U);
406+
if(d->signatures().empty())
407+
return;
404408
s1 = d->signatures().front();
405409
s1->validate();
406410
}

0 commit comments

Comments
 (0)