Skip to content

Commit 26c7c33

Browse files
committed
ASiC-S TimeStamp save support
IB-8290 Signed-off-by: Raul Metsma <raul@metsma.ee>
1 parent 47168f7 commit 26c7c33

13 files changed

Lines changed: 153 additions & 108 deletions

src/ASiC_E.cpp

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -88,28 +88,10 @@ vector<DataFile*> ASiC_E::metaFiles() const
8888
* document does not exist.
8989
* @throws Exception is thrown if ASiC_E class is not correctly initialized.
9090
*/
91-
void ASiC_E::save(const string &path)
91+
void ASiC_E::save(const ZipSerialize &s)
9292
{
93-
if(dataFiles().empty())
94-
THROW("Can not save, container is empty.");
95-
if(mediaType() != MIMETYPE_ASIC_E)
96-
THROW("'%s' format is not supported", mediaType().c_str());
97-
98-
if(!path.empty())
99-
zpath(path);
100-
ZipSerialize s(zpath(), true);
101-
102-
stringstream mimetype;
103-
mimetype << mediaType();
104-
s.addFile("mimetype", mimetype, zproperty("mimetype"), false);
105-
106-
stringstream manifest;
107-
if(!createManifest().save(manifest))
93+
if(!createManifest().save(s.addFile("META-INF/manifest.xml", zproperty("META-INF/manifest.xml")), true))
10894
THROW("Failed to create manifest XML");
109-
s.addFile("META-INF/manifest.xml", manifest, zproperty("META-INF/manifest.xml"));
110-
111-
for(const DataFile *file: dataFiles())
112-
s.addFile(file->fileName(), *(static_cast<const DataFilePrivate*>(file)->m_is), zproperty(file->fileName()));
11395

11496
std::set<Signatures*> saved;
11597
for(Signature *iter: signatures())
@@ -122,10 +104,8 @@ void ASiC_E::save(const string &path)
122104
});
123105
if(name == d->signatures.cend())
124106
THROW("Unkown signature object");
125-
stringstream ofs;
126-
if(!signatures->save(ofs))
107+
if(!signatures->save(s.addFile(name->first, zproperty(name->first))))
127108
THROW("Failed to create signature XML file.");
128-
s.addFile(name->first, ofs, zproperty(name->first));
129109
}
130110
}
131111

@@ -159,6 +139,12 @@ void ASiC_E::addAdESSignature(istream &data)
159139
}
160140
}
161141

142+
void ASiC_E::canSave()
143+
{
144+
if(mediaType() != MIMETYPE_ASIC_E)
145+
THROW("'%s' format is not supported", mediaType().c_str());
146+
}
147+
162148
unique_ptr<Container> ASiC_E::openInternal(const string &path)
163149
{
164150
DEBUG("ASiC_E::openInternal(%s)", path.c_str());

src/ASiC_E.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ namespace digidoc
4242
static constexpr std::string_view ASIC_TSA_PROFILE = "time-stamp-archive";
4343

4444
~ASiC_E() final;
45-
void save(const std::string &path = {}) final;
4645
std::vector<DataFile*> metaFiles() const;
4746

4847
void addAdESSignature(std::istream &data) final;
@@ -56,9 +55,11 @@ namespace digidoc
5655
ASiC_E();
5756
ASiC_E(const std::string &path);
5857
DISABLE_COPY(ASiC_E);
58+
void canSave() final;
5959
XMLDocument createManifest() const;
6060
void loadSignatures(std::istream &data, const std::string &file);
6161
void parseManifestAndLoadFiles(const ZipSerialize &z);
62+
void save(const ZipSerialize &s) final;
6263

6364
class Private;
6465
std::unique_ptr<Private> d;

src/ASiC_S.cpp

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,47 +34,51 @@ using namespace std;
3434
/**
3535
* Initialize ASiCS container.
3636
*/
37-
ASiC_S::ASiC_S(): ASiContainer(MIMETYPE_ASIC_S)
37+
ASiC_S::ASiC_S()
38+
: ASiContainer(MIMETYPE_ASIC_S)
3839
{}
3940

4041
/**
4142
* Opens ASiC-S container from a file
4243
*/
43-
ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S)
44+
ASiC_S::ASiC_S(const string &path)
45+
: ASiContainer(MIMETYPE_ASIC_S)
4446
{
4547
auto z = load(path, false, {mediaType()});
46-
static const string_view metaInf = "META-INF/";
48+
auto starts_with = [](string_view str, string_view needle) constexpr {
49+
return str.size() >= needle.size() && str.compare(0, needle.size(), needle) == 0;
50+
};
4751

4852
for(const string &file: z.list())
4953
{
50-
if(file == "mimetype" ||
51-
(metaInf.size() < file.size() && file.compare(0, metaInf.size(), metaInf) == 0))
52-
{
53-
if(file == "META-INF/timestamp.tst")
54-
{
55-
if(!signatures().empty())
56-
THROW("Can not add signature to ASiC-S container which already contains a signature.");
57-
addSignature(make_unique<SignatureTST>(z.extract<stringstream>(file).str(), this));
58-
}
59-
if(file == "META-INF/signatures.xml")
60-
{
61-
if(!signatures().empty())
62-
THROW("Can not add signature to ASiC-S container which already contains a signature.");
63-
auto data = z.extract<stringstream>(file);
64-
auto signatures = make_shared<Signatures>(data, mediaType());
65-
for(auto s = signatures->signature(); s; s++)
66-
addSignature(make_unique<SignatureXAdES_LTA>(signatures, s, this));
67-
}
54+
if(file == "mimetype")
6855
continue;
56+
if(file == "META-INF/timestamp.tst")
57+
{
58+
if(!signatures().empty())
59+
THROW("Can not add signature to ASiC-S container which already contains a signature.");
60+
addSignature(make_unique<SignatureTST>(z.extract<stringstream>(file).str(), this));
6961
}
70-
71-
const auto directory = File::directory(file);
72-
if(directory.empty() || directory == "/" || directory == "./")
62+
else if(file == "META-INF/signatures.xml")
7363
{
74-
if(!dataFiles().empty())
75-
THROW("Can not add document to ASiC-S container which already contains a document.");
76-
addDataFile(dataStream(file, z), file, "application/octet-stream");
64+
if(!signatures().empty())
65+
THROW("Can not add signature to ASiC-S container which already contains a signature.");
66+
auto data = z.extract<stringstream>(file);
67+
auto signatures = make_shared<Signatures>(data, mediaType());
68+
for(auto s = signatures->signature(); s; s++)
69+
addSignature(make_unique<SignatureXAdES_LTA>(signatures, s, this));
7770
}
71+
else if(file == "META-INF/ASiCArchiveManifest.xml")
72+
THROW("ASiCArchiveManifest are not supported.");
73+
else if(starts_with(file, "META-INF/"))
74+
continue;
75+
else if(const auto directory = File::directory(file);
76+
!directory.empty() && directory != "/" && directory != "./")
77+
THROW("Subfolders are not supported %s", directory.c_str());
78+
else if(!dataFiles().empty())
79+
THROW("Can not add document to ASiC-S container which already contains a document.");
80+
else
81+
addDataFile(dataStream(file, z), file, "application/octet-stream");
7882
}
7983

8084
if(dataFiles().empty())
@@ -83,11 +87,6 @@ ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S)
8387
THROW("ASiC-S container does not contain any signatures.");
8488
}
8589

86-
void ASiC_S::save(const string & /*path*/)
87-
{
88-
THROW("Not implemented.");
89-
}
90-
9190
unique_ptr<Container> ASiC_S::createInternal(const string & /*path*/)
9291
{
9392
return {};
@@ -98,6 +97,12 @@ void ASiC_S::addAdESSignature(istream & /*signature*/)
9897
THROW("Not implemented.");
9998
}
10099

100+
void ASiC_S::canSave()
101+
{
102+
if(auto list = signatures(); !list.empty() && list.front()->profile() != ASIC_TST_PROFILE)
103+
THROW("ASiC-S container supports only saving TimeStampToken signatures.");
104+
}
105+
101106
unique_ptr<Container> ASiC_S::openInternal(const string &path, ContainerOpenCB * /*cb*/)
102107
{
103108
if (!isContainerSimpleFormat(path))
@@ -111,6 +116,12 @@ Signature* ASiC_S::prepareSignature(Signer * /*signer*/)
111116
THROW("Not implemented.");
112117
}
113118

119+
void ASiC_S::save(const ZipSerialize &s)
120+
{
121+
if(auto list = signatures(); !list.empty())
122+
s.addFile("META-INF/timestamp.tst", zproperty("META-INF/timestamp.tst"))(static_cast<SignatureTST*>(list.front())->save());
123+
}
124+
114125
Signature *ASiC_S::sign(Signer * /*signer*/)
115126
{
116127
THROW("Not implemented.");

src/ASiC_S.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,8 @@ namespace digidoc
3030
*/
3131
class ASiC_S : public ASiContainer
3232
{
33-
3433
public:
35-
void save(const std::string &path = {}) override;
34+
static constexpr std::string_view ASIC_TST_PROFILE = "TimeStampToken";
3635

3736
void addAdESSignature(std::istream &sigdata) override;
3837
Signature* prepareSignature(Signer *signer) override;
@@ -46,6 +45,9 @@ namespace digidoc
4645
ASiC_S(const std::string &path);
4746
DISABLE_COPY(ASiC_S);
4847

48+
void canSave() final;
49+
void save(const ZipSerialize &s) final;
50+
4951
static bool isContainerSimpleFormat(const std::string &path);
5052
};
5153
}

src/ASiContainer.cpp

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "util/log.h"
2626

2727
#include <algorithm>
28+
#include <array>
2829
#include <ctime>
2930
#include <fstream>
3031
#include <map>
@@ -51,7 +52,7 @@ class ASiContainer::Private
5152
ASiContainer::ASiContainer(string_view mimetype)
5253
: d(make_unique<Private>())
5354
{
54-
d->mimetype = mimetype;
55+
d->mimetype = string(mimetype);
5556
}
5657

5758
/**
@@ -166,15 +167,15 @@ void ASiContainer::addDataFile(const string &path, const string &mediaType)
166167
is = std::move(data);
167168
}
168169
d->properties[fileName] = { appInfo(), File::modifiedTime(path), size };
169-
addDataFilePrivate(std::move(is), std::move(fileName), mediaType);
170+
d->documents.push_back(new DataFilePrivate(std::move(is), std::move(fileName), mediaType));
170171
}
171172

172173
void ASiContainer::addDataFile(unique_ptr<istream> is, const string &fileName, const string &mediaType)
173174
{
174175
addDataFileChecks(fileName, mediaType);
175176
if(fileName.find_last_of("/\\") != string::npos)
176177
THROW("Document file '%s' cannot contain directory path.", fileName.c_str());
177-
addDataFilePrivate(std::move(is), fileName, mediaType);
178+
d->documents.push_back(new DataFilePrivate(std::move(is), fileName, mediaType));
178179
}
179180

180181
void ASiContainer::addDataFileChecks(const string &fileName, const string &mediaType)
@@ -241,6 +242,34 @@ void ASiContainer::deleteSignature(Signature* s)
241242
delete s;
242243
}
243244

245+
void ASiContainer::save(const string &path)
246+
{
247+
if(dataFiles().empty())
248+
THROW("Can not save, container is empty.");
249+
canSave();
250+
if(!path.empty())
251+
zpath(path);
252+
ZipSerialize s(zpath(), true);
253+
s.addFile("mimetype", zproperty("mimetype"), false)(mediaType());
254+
255+
array<char,10240> buf{};
256+
for(const DataFile *file: dataFiles())
257+
{
258+
auto f = s.addFile(file->fileName(), zproperty(file->fileName()));
259+
const auto &is = static_cast<const DataFilePrivate*>(file)->m_is;
260+
is->clear();
261+
is->seekg(0);
262+
while(*is)
263+
{
264+
is->read(buf.data(), buf.size());
265+
if(auto size = is->gcount(); size > 0)
266+
f(buf.data(), size_t(size));
267+
}
268+
}
269+
270+
save(s);
271+
}
272+
244273
void ASiContainer::zpath(const string &file)
245274
{
246275
d->path = file;
@@ -251,11 +280,11 @@ string ASiContainer::zpath() const
251280
return d->path;
252281
}
253282

254-
const ZipSerialize::Properties& ASiContainer::zproperty(const string &file) const
283+
const ZipSerialize::Properties& ASiContainer::zproperty(string_view file) const
255284
{
256285
if(auto i = d->properties.find(file); i != d->properties.cend())
257286
return i->second;
258-
return d->properties[file] = { appInfo(), time(nullptr), 0 };
287+
return d->properties[string(file)] = { appInfo(), time(nullptr), 0 };
259288
}
260289

261290
/**

src/ASiContainer.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,28 +51,30 @@ namespace digidoc
5151
std::vector<DataFile*> dataFiles() const override;
5252
void removeDataFile(unsigned int id) override;
5353
void removeSignature(unsigned int id) override;
54+
void save(const std::string &path) override;
5455
std::vector<Signature*> signatures() const override;
5556

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

5859
protected:
5960
ASiContainer(std::string_view mimetype);
6061

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

6771
void zpath(const std::string &file);
6872
std::string zpath() const;
69-
const ZipSerialize::Properties &zproperty(const std::string &file) const;
73+
const ZipSerialize::Properties& zproperty(std::string_view file) const;
7074

7175
private:
7276
DISABLE_COPY(ASiContainer);
7377

74-
void addDataFileChecks(const std::string &path, const std::string &mediaType);
75-
7678
class Private;
7779
std::unique_ptr<Private> d;
7880
};

src/SiVaContainer.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,8 @@ unique_ptr<istream> SiVaContainer::parseDDoc(bool useHashCode)
365365
dataFile = std::string_view{};
366366
}
367367
auto result = make_unique<stringstream>();
368-
doc.save(*result);
368+
if(!doc.save([&result](const char *data, size_t size) { result->write(data, streamsize(size)); }))
369+
THROW("Failed to save DDoc");
369370
return result;
370371
}
371372
catch(const Exception &)

src/SignatureTST.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,9 @@ void SignatureTST::validate() const
8383
}
8484
try
8585
{
86-
const string digestMethod = timestampToken->digestMethod();
87-
timestampToken->verify(asicSDoc->dataFiles().front()->calcDigest(digestMethod));
88-
89-
if(!Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) &&
86+
timestampToken->verify(dataToSign());
87+
if(auto digestMethod = signatureMethod();
88+
!Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) &&
9089
Digest::isWeakDigest(digestMethod))
9190
{
9291
Exception e(EXCEPTION_PARAMS("TimeStamp '%s' digest weak", digestMethod.c_str()));
@@ -105,7 +104,12 @@ void SignatureTST::validate() const
105104

106105
std::vector<unsigned char> SignatureTST::dataToSign() const
107106
{
108-
THROW("Not implemented.");
107+
return asicSDoc->dataFiles().front()->calcDigest(signatureMethod());
108+
}
109+
110+
vector<unsigned char> SignatureTST::messageImprint() const
111+
{
112+
return timestampToken->messageImprint();
109113
}
110114

111115
void SignatureTST::setSignatureValue(const std::vector<unsigned char> & /*signatureValue*/)
@@ -116,5 +120,10 @@ void SignatureTST::setSignatureValue(const std::vector<unsigned char> & /*signat
116120
// Xades properties
117121
string SignatureTST::profile() const
118122
{
119-
return "TimeStampToken";
123+
return string(ASiC_S::ASIC_TST_PROFILE);
124+
}
125+
126+
std::vector<unsigned char> SignatureTST::save() const
127+
{
128+
return *timestampToken;
120129
}

0 commit comments

Comments
 (0)