Skip to content

Commit 4b8cc6d

Browse files
committed
Validate ASiC-S manifest containers
IB-8180, IB-8330 Signed-off-by: Raul Metsma <[email protected]>
1 parent 83fcde4 commit 4b8cc6d

File tree

10 files changed

+162
-105
lines changed

10 files changed

+162
-105
lines changed

src/ASiC_S.cpp

Lines changed: 15 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,6 @@ using namespace digidoc;
3333
using namespace digidoc::util;
3434
using namespace std;
3535

36-
struct ASiC_S::Data {
37-
std::string name, mime, data;
38-
39-
Digest digest(Digest digest = {}) const
40-
{
41-
digest.update((const unsigned char*)data.data(), data.size());
42-
return digest;
43-
}
44-
};
45-
46-
47-
4836
/**
4937
* Initialize ASiCS container.
5038
*/
@@ -59,17 +47,19 @@ ASiC_S::ASiC_S(const string &path)
5947
: ASiContainer(MIMETYPE_ASIC_S)
6048
{
6149
auto z = load(path, false, {mediaType()});
50+
bool foundTimestamp = false;
6251
for(const string &file: z.list())
6352
{
6453
if(file == "mimetype")
6554
continue;
6655
if(file == "META-INF/timestamp.tst")
56+
foundTimestamp = true;
57+
if(file == "META-INF/ASiCArchiveManifest.xml")
6758
{
6859
if(!signatures().empty())
6960
THROW("Can not add signature to ASiC-S container which already contains a signature.");
70-
string tst = z.extract<stringstream>(file).str();
71-
addSignature(make_unique<SignatureTST>(tst, this));
72-
metadata.push_back({file, "application/vnd.etsi.timestamp-token", std::move(tst)});
61+
addSignature(make_unique<SignatureTST>(true, z, this));
62+
foundTimestamp = false; // Manifest contains timestamp
7363
}
7464
else if(file == "META-INF/signatures.xml")
7565
{
@@ -80,28 +70,6 @@ ASiC_S::ASiC_S(const string &path)
8070
for(auto s = signatures->signature(); s; s++)
8171
addSignature(make_unique<SignatureXAdES_LTA>(signatures, s, this));
8272
}
83-
else if(file == "META-INF/ASiCArchiveManifest.xml")
84-
{
85-
function<void(const string &, string_view)> add = [this, &add, &z](const string &file, string_view mime) {
86-
auto xml = z.extract<stringstream>(file);
87-
XMLDocument doc = XMLDocument::openStream(xml, {"ASiCManifest", ASIC_NS});
88-
doc.validateSchema(util::File::path(Conf::instance()->xsdPath(), "en_31916201v010101.xsd"));
89-
90-
for(auto ref = doc/"DataObjectReference"; ref; ref++)
91-
{
92-
if(ref["Rootfile"] == "true")
93-
add(util::File::fromUriPath(ref["URI"]), ref["MimeType"]);
94-
}
95-
96-
auto ref = doc/"SigReference";
97-
string uri = util::File::fromUriPath(ref["URI"]);
98-
string tst = z.extract<stringstream>(uri).str();
99-
addSignature(make_unique<SignatureTST>(file, ::move(doc), tst, this));
100-
metadata.push_back({file, string(mime), xml.str()});
101-
metadata.push_back({uri, string(ref["MimeType"]), std::move(tst)});
102-
};
103-
add(file, "text/xml");
104-
}
10573
else if(starts_with(file, "META-INF/"))
10674
continue;
10775
else if(const auto directory = File::directory(file);
@@ -112,6 +80,12 @@ ASiC_S::ASiC_S(const string &path)
11280
else
11381
addDataFile(dataStream(file, z), file, "application/octet-stream");
11482
}
83+
if(foundTimestamp)
84+
{
85+
if(!signatures().empty())
86+
THROW("Can not add signature to ASiC-S container which already contains a signature.");
87+
addSignature(make_unique<SignatureTST>(false, z, this));
88+
}
11589

11690
if(dataFiles().empty())
11791
THROW("ASiC-S container does not contain any data objects.");
@@ -147,14 +121,6 @@ void ASiC_S::canSave()
147121
THROW("ASiC-S container supports only saving TimeStampToken signatures.");
148122
}
149123

150-
Digest ASiC_S::fileDigest(const string &file, string_view method) const
151-
{
152-
if(auto i = find_if(metadata.cbegin(), metadata.cend(), [&file](const auto &d) { return d.name == file; });
153-
i != metadata.cend())
154-
return i->digest(method);
155-
THROW("File not found %s.", file.c_str());
156-
}
157-
158124
unique_ptr<Container> ASiC_S::openInternal(const string &path, ContainerOpenCB * /*cb*/)
159125
{
160126
if (!isContainerSimpleFormat(path))
@@ -170,10 +136,11 @@ Signature* ASiC_S::prepareSignature(Signer * /*signer*/)
170136

171137
void ASiC_S::save(const ZipSerialize &s)
172138
{
173-
if(zproperty("META-INF/manifest.xml").size && !createManifest().save(s.addFile("META-INF/manifest.xml", zproperty("META-INF/manifest.xml")), true))
139+
if(const auto &prop = zproperty("META-INF/manifest.xml");
140+
prop.size && !createManifest().save(s.addFile("META-INF/manifest.xml", prop), true))
174141
THROW("Failed to create manifest XML");
175-
for(const auto &[name, mime, data]: metadata)
176-
s.addFile(name, zproperty(name))(data);
142+
for(Signature *sig: signatures())
143+
static_cast<SignatureTST*>(sig)->save(s);
177144
}
178145

179146
Signature *ASiC_S::sign(Signer *signer)

src/ASiC_S.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ namespace digidoc
3939
Signature* prepareSignature(Signer *signer) override;
4040
Signature* sign(Signer* signer) override;
4141

42-
Digest fileDigest(const std::string &file, std::string_view method = {}) const;
43-
4442
static std::unique_ptr<Container> createInternal(const std::string &path);
4543
static std::unique_ptr<Container> openInternal(const std::string &path, ContainerOpenCB *cb);
4644

@@ -54,8 +52,6 @@ namespace digidoc
5452
void save(const ZipSerialize &s) final;
5553

5654
static bool isContainerSimpleFormat(const std::string &path);
57-
58-
struct Data;
59-
std::vector<Data> metadata;
55+
friend class SignatureTST;
6056
};
6157
}

src/ASiContainer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ namespace digidoc
4545
//https://signa.mitsoft.lt/static/signa-web/webResources/docs/ADOC_specification_approved20090907_EN.pdf
4646
static constexpr std::string_view MIMETYPE_ADOC = "application/vnd.lt.archyvai.adoc-2008";
4747
static constexpr std::string_view MANIFEST_NS = "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0";
48+
static constexpr std::string_view ASIC_NS = "http://uri.etsi.org/02918/v1.2.1#";
4849

4950
~ASiContainer() override;
5051
std::string mediaType() const override;

src/Container.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class DIGIDOCPP_EXPORT Container
5858
virtual std::vector<DataFile*> dataFiles() const = 0;
5959
virtual void removeDataFile(unsigned int index) = 0;
6060

61-
void addAdESSignature(const std::vector<unsigned char> &signature);
61+
DIGIDOCPP_DEPRECATED void addAdESSignature(const std::vector<unsigned char> &signature);
6262
virtual void addAdESSignature(std::istream &signature) = 0;
6363
virtual Signature* prepareSignature(Signer *signer) = 0;
6464
virtual std::vector<Signature*> signatures() const = 0;

src/SignatureTST.cpp

Lines changed: 89 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,62 @@
2020
#include "SignatureTST.h"
2121

2222
#include "ASiC_S.h"
23+
#include "Conf.h"
2324
#include "DataFile_p.h"
25+
#include "XMLDocument.h"
2426
#include "crypto/Digest.h"
2527
#include "crypto/Signer.h"
2628
#include "crypto/TS.h"
2729
#include "crypto/X509Cert.h"
30+
#include "util/algorithm.h"
2831
#include "util/DateTime.h"
2932
#include "util/File.h"
3033
#include "util/log.h"
3134

35+
#include <functional>
36+
#include <sstream>
37+
3238
using namespace digidoc;
3339
using namespace std;
3440

35-
constexpr std::string_view DSIG_NS {"http://www.w3.org/2000/09/xmldsig#"};
36-
constexpr XMLName DigestMethod {"DigestMethod", DSIG_NS};
37-
constexpr XMLName DigestValue {"DigestValue", DSIG_NS};
41+
struct SignatureTST::Data {
42+
std::string name, mime, data;
43+
bool root = false;
3844

39-
SignatureTST::SignatureTST(const string &data, ASiC_S *asicSDoc)
40-
: asicSDoc(asicSDoc)
41-
, timestampToken(make_unique<TS>((const unsigned char*)data.data(), data.size()))
42-
{}
45+
Digest digest(Digest digest = {}) const
46+
{
47+
digest.update((const unsigned char*)data.data(), data.size());
48+
return digest;
49+
}
50+
};
4351

44-
SignatureTST::SignatureTST(string current, XMLDocument &&xml, const string &data, ASiC_S *asicSDoc)
45-
: SignatureTST(data, asicSDoc)
52+
SignatureTST::SignatureTST(bool manifest, const ZipSerialize &z, ASiC_S *asicSDoc)
53+
: asicSDoc(asicSDoc)
4654
{
47-
file = std::move(current);
48-
doc = std::move(xml);
55+
auto data = z.extract<stringstream>("META-INF/timestamp.tst").str();
56+
timestampToken = make_unique<TS>((const unsigned char*)data.data(), data.size());
57+
metadata.push_back({"META-INF/timestamp.tst", "application/vnd.etsi.timestamp-token", std::move(data)});
58+
if(!manifest)
59+
return;
60+
XMLSchema schema(util::File::path(Conf::instance()->xsdPath(), "en_31916201v010101.xsd"));
61+
function<void(const string &, string_view)> add = [this, &schema, &add, &z](const string &file, string_view mime) {
62+
auto xml = z.extract<stringstream>(file);
63+
XMLDocument doc = XMLDocument::openStream(xml, {"ASiCManifest", ASiContainer::ASIC_NS});
64+
schema.validate(doc);
65+
66+
for(auto ref = doc/"DataObjectReference"; ref; ref++)
67+
{
68+
if(ref["Rootfile"] == "true")
69+
add(util::File::fromUriPath(ref["URI"]), ref["MimeType"]);
70+
}
71+
72+
auto ref = doc/"SigReference";
73+
string uri = util::File::fromUriPath(ref["URI"]);
74+
string tst = z.extract<stringstream>(uri).str();
75+
metadata.push_back({file, string(mime), xml.str()});
76+
metadata.push_back({uri, string(ref["MimeType"]), std::move(tst)});
77+
};
78+
add("META-INF/ASiCArchiveManifest.xml", "text/xml");
4979
}
5080

5181
SignatureTST::SignatureTST(ASiC_S *asicSDoc, Signer *signer)
@@ -55,10 +85,25 @@ SignatureTST::SignatureTST(ASiC_S *asicSDoc, Signer *signer)
5585
Digest digest;
5686
dataFile->digest(digest);
5787
timestampToken = make_unique<TS>(digest, signer->userAgent());
88+
vector<unsigned char> der = *timestampToken;
89+
metadata.push_back({"META-INF/timestamp.tst", "application/vnd.etsi.timestamp-token", {der.cbegin(), der.cend()}});
5890
}
5991

6092
SignatureTST::~SignatureTST() = default;
6193

94+
std::vector<SignatureTST::TSAInfo> SignatureTST::ArchiveTimeStamps() const
95+
{
96+
std::vector<TSAInfo> result;
97+
for(auto i = metadata.cbegin() + 1; i != metadata.cend(); ++i)
98+
{
99+
if(i->mime != "application/vnd.etsi.timestamp-token")
100+
continue;
101+
TS ts((const unsigned char*)i->data.data(), i->data.size());
102+
result.push_back({ts.cert(), util::date::to_string(ts.time())});
103+
}
104+
return result;
105+
}
106+
62107
X509Cert SignatureTST::TimeStampCertificate() const
63108
{
64109
return timestampToken->cert();
@@ -106,28 +151,48 @@ void SignatureTST::validate() const
106151
}
107152
try
108153
{
109-
timestampToken->verify(dataToSign());
110-
if(auto digestMethod = signatureMethod();
111-
!Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) &&
154+
auto digestMethod = signatureMethod();
155+
DataFile *file = asicSDoc->dataFiles().front();
156+
timestampToken->verify(file->calcDigest(digestMethod));
157+
if(!Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) &&
112158
Digest::isWeakDigest(digestMethod))
113159
{
114160
Exception e(EXCEPTION_PARAMS("TimeStamp '%s' digest weak", digestMethod.c_str()));
115161
e.setCode(Exception::ReferenceDigestWeak);
116162
exception.addCause(e);
117163
}
118-
if(doc)
164+
vector<string> list {file->fileName()};
165+
for(const auto &manifest: metadata)
119166
{
120-
DataFile *file = asicSDoc->dataFiles().front();
167+
if(manifest.mime != "text/xml")
168+
continue;
169+
istringstream is(manifest.data);
170+
XMLDocument doc = XMLDocument::openStream(is, {"ASiCManifest", ASiContainer::ASIC_NS});
171+
vector<string> add;
121172
for(auto ref = doc/"DataObjectReference"; ref; ref++)
122173
{
123174
string_view method = (ref/DigestMethod)["Algorithm"];
124-
auto uri = util::File::fromUriPath(ref["URI"]);
125-
vector<unsigned char> digest = file->fileName() == uri ?
126-
dynamic_cast<const DataFilePrivate*>(file)->calcDigest(string(method)) :
127-
asicSDoc->fileDigest(uri, method).result();
175+
const auto &uri = add.emplace_back(util::File::fromUriPath(ref["URI"]));
176+
vector<unsigned char> digest;
177+
if(file->fileName() == uri)
178+
digest = file->calcDigest(string(method));
179+
else
180+
{
181+
auto i = find_if(metadata.cbegin(), metadata.cend(), [&uri](const auto &d) { return d.name == uri; });
182+
if(i == metadata.cend())
183+
THROW("File not found '%s'.", uri.c_str());
184+
digest = i->digest(method).result();
185+
}
128186
if(vector<unsigned char> digestValue = ref/DigestValue; digest != digestValue)
129-
THROW("Reference %s digest does not match", uri.c_str());
187+
THROW("Reference '%s' digest does not match", uri.c_str());
188+
}
189+
// Check if all files in previous scope are present
190+
for(const string &uri: list)
191+
{
192+
if(!contains(add, uri))
193+
THROW("Reference '%s' not found in manifest", uri.c_str());
130194
}
195+
list = std::move(add);
131196
}
132197
}
133198
catch (const Exception& e)
@@ -141,8 +206,6 @@ void SignatureTST::validate() const
141206

142207
std::vector<unsigned char> SignatureTST::dataToSign() const
143208
{
144-
if(!file.empty())
145-
return asicSDoc->fileDigest(file, signatureMethod()).result();
146209
return asicSDoc->dataFiles().front()->calcDigest(signatureMethod());
147210
}
148211

@@ -162,7 +225,8 @@ string SignatureTST::profile() const
162225
return string(ASiC_S::ASIC_TST_PROFILE);
163226
}
164227

165-
std::vector<unsigned char> SignatureTST::save() const
228+
void SignatureTST::save(const ZipSerialize &z) const
166229
{
167-
return *timestampToken;
230+
for(const auto &[name, mime, data, root]: metadata)
231+
z.addFile(name, asicSDoc->zproperty(name))(data);
168232
}

src/SignatureTST.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,18 @@
2323

2424
#include "XMLDocument.h"
2525

26+
#include <crypto/X509Cert.h>
27+
2628
namespace digidoc
2729
{
2830
class ASiC_S;
2931
class TS;
32+
class ZipSerialize;
3033

3134
class SignatureTST final: public Signature
3235
{
3336
public:
34-
SignatureTST(const std::string &data, ASiC_S *asicSDoc);
35-
SignatureTST(std::string current, XMLDocument &&xml, const std::string &data, ASiC_S *asicSDoc);
37+
SignatureTST(bool manifest, const ZipSerialize &z, ASiC_S *asicSDoc);
3638
SignatureTST(ASiC_S *asicSDoc, Signer *signer);
3739
~SignatureTST();
3840

@@ -54,14 +56,20 @@ class SignatureTST final: public Signature
5456
// Xades properties
5557
std::string profile() const final;
5658

57-
std::vector<unsigned char> save() const;
59+
//TSA profile properties
60+
struct TSAInfo { X509Cert cert; std::string time; };
61+
std::vector<TSAInfo> ArchiveTimeStamps() const;
62+
63+
void save(const ZipSerialize &s) const;
5864

5965
private:
6066
DISABLE_COPY(SignatureTST);
6167
ASiC_S *asicSDoc {};
6268
std::string file;
6369
XMLDocument doc;
6470
std::unique_ptr<TS> timestampToken;
71+
struct Data;
72+
std::vector<Data> metadata;
6573
};
6674

6775
}

src/SignatureXAdES_B.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,6 @@ const map<string_view,SignatureXAdES_B::Policy> SignatureXAdES_B::policylist{
8686
namespace digidoc
8787
{
8888

89-
constexpr XMLName DigestMethod {"DigestMethod", DSIG_NS};
90-
constexpr XMLName DigestValue {"DigestValue", DSIG_NS};
9189
constexpr XMLName X509IssuerName {"X509IssuerName", DSIG_NS};
9290
constexpr XMLName X509SerialNumber {"X509SerialNumber", DSIG_NS};
9391

src/SignatureXAdES_B.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ namespace digidoc
2929
{
3030
constexpr std::string_view ASIC_NS {"http://uri.etsi.org/02918/v1.2.1#"};
3131
constexpr std::string_view OPENDOCUMENT_NS {"urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0"};
32-
constexpr std::string_view DSIG_NS {"http://www.w3.org/2000/09/xmldsig#"};
33-
constexpr std::string_view XADES_NS {"http://uri.etsi.org/01903/v1.3.2#"};
3432
constexpr std::string_view XADESv141_NS {"http://uri.etsi.org/01903/v1.4.1#"};
3533
constexpr std::string_view REF_TYPE {"http://uri.etsi.org/01903#SignedProperties"};
3634

0 commit comments

Comments
 (0)