Skip to content

Commit 3279ff8

Browse files
committed
CDoc1 XmlWriter error handling
Signed-off-by: Raul Metsma <raul@metsma.ee>
1 parent 8844528 commit 3279ff8

File tree

3 files changed

+117
-129
lines changed

3 files changed

+117
-129
lines changed

cdoc/CDoc1Writer.cpp

Lines changed: 109 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
using namespace libcdoc;
3434

35+
#define RET_ERROR(F) if (auto rv = F; rv != OK) return rv
36+
3537
struct FileEntry {
3638
std::string name;
3739
size_t size;
@@ -53,7 +55,9 @@ class CDoc1Writer::Private
5355
static const XMLWriter::NS DENC, DS, XENC11, DSIG11;
5456
std::string method, documentFormat = "ENCDOC-XML|1.1", lastError;
5557

56-
bool writeRecipient(XMLWriter *xmlw, const std::vector<uint8_t> &recipient, const libcdoc::Crypto::Key& transportKey);
58+
uint64_t writeEncryptionProperties(bool use_ddoc);
59+
uint64_t writeKeyInfo(bool use_ddoc, const libcdoc::Crypto::Key& transportKey);
60+
uint64_t writeRecipient(const std::vector<uint8_t> &recipient, const libcdoc::Crypto::Key& transportKey);
5761
};
5862

5963
const XMLWriter::NS CDoc1Writer::Private::DENC{ "denc", "http://www.w3.org/2001/04/xmlenc#" };
@@ -72,11 +76,48 @@ CDoc1Writer::~CDoc1Writer()
7276
delete d;
7377
}
7478

75-
bool CDoc1Writer::Private::writeRecipient(XMLWriter *xmlw, const std::vector<uint8_t> &recipient, const libcdoc::Crypto::Key& transportKey)
79+
uint64_t CDoc1Writer::Private::writeEncryptionProperties(bool use_ddoc)
80+
{
81+
RET_ERROR(_xml->writeElement(DENC, "EncryptionProperties", [&]() -> uint64_t {
82+
RET_ERROR(_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "LibraryVersion"}}, "cdoc|0.0.1"));
83+
RET_ERROR(_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "DocumentFormat"}}, documentFormat));
84+
RET_ERROR(_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "Filename"}}, use_ddoc ? "tmp.ddoc" : files.at(0).name));
85+
for(const FileEntry &file: files)
86+
{
87+
RET_ERROR(_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "orig_file"}},
88+
file.name + "|" + std::to_string(file.size) + "|" + "application/octet-stream" + "|D0"));
89+
}
90+
return OK;
91+
}));
92+
return _xml->writeEndElement(Private::DENC); // EncryptedData
93+
}
94+
95+
uint64_t CDoc1Writer::Private::writeKeyInfo(bool use_ddoc, const libcdoc::Crypto::Key& transportKey)
96+
{
97+
RET_ERROR(_xml->writeStartElement(Private::DENC, "EncryptedData", {{"MimeType", use_ddoc ? "http://www.sk.ee/DigiDoc/v1.3.0/digidoc.xsd" : "application/octet-stream"}}));
98+
RET_ERROR(_xml->writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", method}}));
99+
return _xml->writeElement(Private::DS, "KeyInfo", {}, [&]() -> uint64_t {
100+
for (const libcdoc::Recipient& key : rcpts) {
101+
if (!key.isCertificate()) {
102+
lastError = "Invalid recipient type";
103+
LOG_ERROR("{}", lastError);
104+
return libcdoc::UNSPECIFIED_ERROR;
105+
}
106+
if(auto rv = writeRecipient(key.cert, transportKey); rv != OK) {
107+
lastError = "Failed to write Recipient info";
108+
LOG_ERROR("{}", lastError);
109+
return rv;
110+
}
111+
}
112+
return OK;
113+
});
114+
}
115+
116+
uint64_t CDoc1Writer::Private::writeRecipient(const std::vector<uint8_t> &recipient, const libcdoc::Crypto::Key& transportKey)
76117
{
77118
auto peerCert = libcdoc::Crypto::toX509(recipient);
78119
if(!peerCert)
79-
return false;
120+
return UNSPECIFIED_ERROR;
80121
std::string cn = [&]{
81122
std::string cn;
82123
X509_NAME *name = X509_get_subject_name(peerCert.get());
@@ -95,7 +136,7 @@ bool CDoc1Writer::Private::writeRecipient(XMLWriter *xmlw, const std::vector<uin
95136
OPENSSL_free(data);
96137
return cn;
97138
}();
98-
xmlw->writeElement(Private::DENC, "EncryptedKey", {{"Recipient", cn}}, [&]{
139+
return _xml->writeElement(Private::DENC, "EncryptedKey", {{"Recipient", cn}}, [&]() -> uint64_t {
99140
std::vector<uint8_t> encryptedData;
100141
auto *peerPKey = X509_get0_pubkey(peerCert.get());
101142
switch(EVP_PKEY_base_id(peerPKey))
@@ -106,12 +147,12 @@ bool CDoc1Writer::Private::writeRecipient(XMLWriter *xmlw, const std::vector<uin
106147
encryptedData.resize(size_t(RSA_size(rsa.get())));
107148
RSA_public_encrypt(int(transportKey.key.size()), transportKey.key.data(),
108149
encryptedData.data(), rsa.get(), RSA_PKCS1_PADDING);
109-
xmlw->writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", libcdoc::Crypto::RSA_MTH}});
110-
xmlw->writeElement(Private::DS, "KeyInfo", [&]{
111-
xmlw->writeElement(Private::DS, "X509Data", [&]{
112-
xmlw->writeBase64Element(Private::DS, "X509Certificate", recipient);
150+
RET_ERROR(_xml->writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", libcdoc::Crypto::RSA_MTH}}));
151+
RET_ERROR(_xml->writeElement(Private::DS, "KeyInfo", [&]{
152+
return _xml->writeElement(Private::DS, "X509Data", [&]{
153+
return _xml->writeBase64Element(Private::DS, "X509Certificate", recipient);
113154
});
114-
});
155+
}));
115156
break;
116157
}
117158
case EVP_PKEY_EC:
@@ -147,41 +188,44 @@ bool CDoc1Writer::Private::writeRecipient(XMLWriter *xmlw, const std::vector<uin
147188
LOG_TRACE_KEY("iv {}", transportKey.iv);
148189
LOG_TRACE_KEY("transport {}", transportKey.key);
149190

150-
xmlw->writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", encryptionMethod}});
151-
xmlw->writeElement(Private::DS, "KeyInfo", [&]{
152-
xmlw->writeElement(Private::DENC, "AgreementMethod", {{"Algorithm", libcdoc::Crypto::AGREEMENT_MTH}}, [&]{
153-
xmlw->writeElement(Private::XENC11, "KeyDerivationMethod", {{"Algorithm", libcdoc::Crypto::CONCATKDF_MTH}}, [&]{
154-
xmlw->writeElement(Private::XENC11, "ConcatKDFParams", {
191+
RET_ERROR(_xml->writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", encryptionMethod}}));
192+
RET_ERROR(_xml->writeElement(Private::DS, "KeyInfo", [&]{
193+
return _xml->writeElement(Private::DENC, "AgreementMethod", {{"Algorithm", libcdoc::Crypto::AGREEMENT_MTH}}, [&]{
194+
RET_ERROR(_xml->writeElement(Private::XENC11, "KeyDerivationMethod", {{"Algorithm", libcdoc::Crypto::CONCATKDF_MTH}}, [&]{
195+
return _xml->writeElement(Private::XENC11, "ConcatKDFParams", {
155196
{"AlgorithmID", "00" + libcdoc::toHex(AlgorithmID)},
156197
{"PartyUInfo", "00" + libcdoc::toHex(SsDer)},
157198
{"PartyVInfo", "00" + libcdoc::toHex(recipient)}}, [&]{
158-
xmlw->writeElement(Private::DS, "DigestMethod", {{"Algorithm", concatDigest}});
199+
return _xml->writeElement(Private::DS, "DigestMethod", {{"Algorithm", concatDigest}});
159200
});
160-
});
161-
xmlw->writeElement(Private::DENC, "OriginatorKeyInfo", [&]{
162-
xmlw->writeElement(Private::DS, "KeyValue", [&]{
163-
xmlw->writeElement(Private::DSIG11, "ECKeyValue", [&]{
164-
xmlw->writeElement(Private::DSIG11, "NamedCurve", {{"URI", "urn:oid:" + oid}});
165-
xmlw->writeBase64Element(Private::DSIG11, "PublicKey", SsDer);
201+
}));
202+
RET_ERROR(_xml->writeElement(Private::DENC, "OriginatorKeyInfo", [&]{
203+
return _xml->writeElement(Private::DS, "KeyValue", [&]{
204+
return _xml->writeElement(Private::DSIG11, "ECKeyValue", [&]{
205+
RET_ERROR(_xml->writeElement(Private::DSIG11, "NamedCurve", {{"URI", "urn:oid:" + oid}}));
206+
return _xml->writeBase64Element(Private::DSIG11, "PublicKey", SsDer);
166207
});
167208
});
168-
});
169-
xmlw->writeElement(Private::DENC, "RecipientKeyInfo", [&]{
170-
xmlw->writeElement(Private::DS, "X509Data", [&]{
171-
xmlw->writeBase64Element(Private::DS, "X509Certificate", recipient);
209+
}));
210+
return _xml->writeElement(Private::DENC, "RecipientKeyInfo", [&]{
211+
return _xml->writeElement(Private::DS, "X509Data", [&]{
212+
return _xml->writeBase64Element(Private::DS, "X509Certificate", recipient);
172213
});
173214
});
174215
});
175-
});
216+
}));
176217
break;
177218
}
178-
default: break;
179-
}
180-
xmlw->writeElement(Private::DENC, "CipherData", [&]{
181-
xmlw->writeBase64Element(Private::DENC, "CipherValue", encryptedData);
219+
default:
220+
return UNSPECIFIED_ERROR;
221+
}
222+
223+
if (encryptedData.empty())
224+
return UNSPECIFIED_ERROR;
225+
return _xml->writeElement(Private::DENC, "CipherData", [&]{
226+
return _xml->writeBase64Element(Private::DENC, "CipherValue", encryptedData);
182227
});
183228
});
184-
return true;
185229
}
186230

187231
/**
@@ -194,72 +238,44 @@ CDoc1Writer::encrypt(libcdoc::MultiDataSource& src, const std::vector<libcdoc::R
194238

195239
int n_components = src.getNumComponents();
196240
bool use_ddoc = (n_components > 1) || (n_components == libcdoc::NOT_IMPLEMENTED);
241+
d->rcpts = keys;
197242

198243
d->_xml = std::make_unique<XMLWriter>(dst);
199-
d->_xml->writeStartElement(Private::DENC, "EncryptedData", {{"MimeType", use_ddoc ? "http://www.sk.ee/DigiDoc/v1.3.0/digidoc.xsd" : "application/octet-stream"}});
200-
d->_xml->writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", d->method}});
201-
d->_xml->writeStartElement(Private::DS, "KeyInfo", {});
202-
for (const libcdoc::Recipient& key : keys) {
203-
if (!key.isCertificate()) {
204-
d->lastError = "Invalid recipient type";
205-
LOG_ERROR("{}", d->lastError);
206-
return libcdoc::UNSPECIFIED_ERROR;
207-
}
208-
if(!d->writeRecipient(d->_xml.get(), key.cert, transportKey)) {
209-
d->lastError = "Failed to write Recipient info";
210-
LOG_ERROR("{}", d->lastError);
211-
return libcdoc::IO_ERROR;
212-
}
213-
}
214-
d->_xml->writeEndElement(Private::DS); // KeyInfo
244+
RET_ERROR(d->writeKeyInfo(use_ddoc, transportKey));
215245

216-
std::vector<FileEntry> files;
217-
int64_t result = libcdoc::OK;
218-
d->_xml->writeElement(Private::DENC, "CipherData", [&]() -> void {
246+
RET_ERROR(d->_xml->writeElement(Private::DENC, "CipherData", [&]() -> uint64_t {
219247
std::vector<uint8_t> data;
220248
if(use_ddoc) {
221249
data.reserve(16384);
222250
DDOCWriter ddoc(data);
223251
std::string name;
224252
int64_t size;
225-
result = src.next(name, size);
226-
while (result == libcdoc::OK) {
253+
for (auto result = src.next(name, size); result == libcdoc::OK; result = src.next(name, size)) {
227254
std::vector<uint8_t> contents;
228255
libcdoc::VectorConsumer vcons(contents);
229-
result = src.readAll(vcons);
230-
if (result < 0) return;
231-
files.push_back({name, (size_t) result});
232-
ddoc.addFile(name, "application/octet-stream", contents);
233-
result = src.next(name, size);
256+
if (src.readAll(vcons) < 0)
257+
return IO_ERROR;
258+
d->files.push_back({name, (size_t) result});
259+
if(auto rv = ddoc.addFile(name, "application/octet-stream", contents); rv != OK)
260+
return rv;
234261
}
235262
} else {
236263
std::string name;
237264
int64_t size;
238-
result = src.next(name, size);
239-
if (result < 0) return;
265+
if (src.next(name, size) < 0)
266+
return IO_ERROR;
240267
libcdoc::VectorConsumer vcons(data);
241-
result = src.readAll(vcons);
242-
if (result < 0) return;
243-
files.push_back({std::move(name), (size_t) result});
268+
auto result = src.readAll(vcons);
269+
if (result < 0)
270+
return IO_ERROR;
271+
d->files.push_back({std::move(name), (size_t) result});
244272
}
245-
d->_xml->writeBase64Element(Private::DENC, "CipherValue", libcdoc::Crypto::encrypt(d->method, transportKey, data));
246-
});
247-
if (result < 0) return result;
248-
d->_xml->writeElement(Private::DENC, "EncryptionProperties", [&]{
249-
d->_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "LibraryVersion"}}, "cdoc|0.0.1");
250-
d->_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "DocumentFormat"}}, d->documentFormat);
251-
d->_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "Filename"}}, files.size() == 1 ? files.at(0).name : "tmp.ddoc");
252-
for(const FileEntry &file: files)
253-
{
254-
d->_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "orig_file"}},
255-
file.name + "|" + std::to_string(file.size) + "|" + "application/octet-stream" + "|D0");
256-
}
257-
});
258-
d->_xml->writeEndElement(Private::DENC); // EncryptedData
273+
return d->_xml->writeBase64Element(Private::DENC, "CipherValue", libcdoc::Crypto::encrypt(d->method, transportKey, data));
274+
}));
275+
RET_ERROR(d->writeEncryptionProperties(use_ddoc));
259276
d->_xml.reset();
260-
result = libcdoc::OK;
261-
if (owned) result = dst->close();
262-
return result;
277+
if (owned) return dst->close();
278+
return OK;
263279
}
264280

265281
libcdoc::result_t
@@ -301,48 +317,20 @@ CDoc1Writer::finishEncryption()
301317

302318
libcdoc::Crypto::Key transportKey = libcdoc::Crypto::generateKey(d->method);
303319

304-
d->_xml->writeStartElement(Private::DENC, "EncryptedData", {{"MimeType", use_ddoc ? "http://www.sk.ee/DigiDoc/v1.3.0/digidoc.xsd" : "application/octet-stream"}});
305-
d->_xml->writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", d->method}});
306-
d->_xml->writeStartElement(Private::DS, "KeyInfo", {});
307-
for (const libcdoc::Recipient& key : d->rcpts) {
308-
if (!key.isCertificate()) {
309-
d->lastError = "Invalid recipient type";
310-
LOG_ERROR("{}", d->lastError);
311-
return libcdoc::UNSPECIFIED_ERROR;
312-
}
313-
if(!d->writeRecipient(d->_xml.get(), key.cert, transportKey)) {
314-
d->lastError = "Failed to write Recipient info";
315-
LOG_ERROR("{}", d->lastError);
316-
return libcdoc::IO_ERROR;
317-
}
318-
}
319-
d->_xml->writeEndElement(Private::DS); // KeyInfo
320-
321-
d->_xml->writeElement(Private::DENC, "CipherData", [&]{
322-
if(use_ddoc) {
323-
std::vector<uint8_t> data;
324-
data.reserve(4096);
325-
for (DDOCWriter ddoc(data); const FileEntry& file : d->files) {
326-
std::vector<uint8_t> contents;
327-
ddoc.addFile(file.name, "application/octet-stream", file.data);
328-
}
329-
d->_xml->writeBase64Element(Private::DENC, "CipherValue", libcdoc::Crypto::encrypt(d->method, transportKey, data));
330-
} else {
331-
d->_xml->writeBase64Element(Private::DENC, "CipherValue", libcdoc::Crypto::encrypt(d->method, transportKey, d->files.back().data));
332-
}
333-
});
334-
d->_xml->writeElement(Private::DENC, "EncryptionProperties", [&]{
335-
d->_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "LibraryVersion"}}, "cdoc|0.0.1");
336-
d->_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "DocumentFormat"}}, d->documentFormat);
337-
d->_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "Filename"}}, use_ddoc ? "tmp.ddoc" : d->files.at(0).name);
338-
for(const FileEntry &file: d->files)
339-
{
340-
d->_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "orig_file"}},
341-
file.name + "|" + std::to_string(file.size) + "|" + "application/octet-stream" + "|D0");
342-
}
343-
});
344-
d->_xml->writeEndElement(Private::DENC); // EncryptedData
320+
RET_ERROR(d->writeKeyInfo(use_ddoc, transportKey));
321+
RET_ERROR(d->_xml->writeElement(Private::DENC, "CipherData", [&]{
322+
if(!use_ddoc)
323+
return d->_xml->writeBase64Element(Private::DENC, "CipherValue", libcdoc::Crypto::encrypt(d->method, transportKey, d->files.back().data));
324+
std::vector<uint8_t> data;
325+
data.reserve(4096);
326+
for (DDOCWriter ddoc(data); const FileEntry& file : d->files) {
327+
std::vector<uint8_t> contents;
328+
ddoc.addFile(file.name, "application/octet-stream", file.data);
329+
}
330+
return d->_xml->writeBase64Element(Private::DENC, "CipherValue", libcdoc::Crypto::encrypt(d->method, transportKey, data));
331+
}));
332+
RET_ERROR(d->writeEncryptionProperties(use_ddoc));
345333
d->_xml.reset();
346-
if (owned) dst->close();
334+
if (owned) return dst->close();
347335
return libcdoc::OK;
348336
}

cdoc/XmlWriter.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,21 +113,21 @@ int64_t XMLWriter::writeEndElement(const NS &ns)
113113
return OK;
114114
}
115115

116-
int64_t XMLWriter::writeElement(const NS &ns, const std::string &name, const std::function<void()> &f)
116+
int64_t XMLWriter::writeElement(const NS &ns, const std::string &name, const std::function<uint64_t()> &f)
117117
{
118118
if(auto rv = writeStartElement(ns, name, {}); rv != OK)
119119
return rv;
120-
if(f)
121-
f();
120+
if(uint64_t rv = OK; f && (rv = f()) != OK)
121+
return rv;
122122
return writeEndElement(ns);
123123
}
124124

125-
int64_t XMLWriter::writeElement(const NS &ns, const std::string &name, const std::map<std::string, std::string> &attr, const std::function<void()> &f)
125+
int64_t XMLWriter::writeElement(const NS &ns, const std::string &name, const std::map<std::string, std::string> &attr, const std::function<uint64_t()> &f)
126126
{
127127
if(auto rv = writeStartElement(ns, name, attr); rv != OK)
128128
return rv;
129-
if(f)
130-
f();
129+
if(uint64_t rv = OK; f && (rv = f()) != OK)
130+
return rv;
131131
return writeEndElement(ns);
132132
}
133133

cdoc/XmlWriter.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ class XMLWriter
4141

4242
int64_t writeStartElement(const NS &ns, const std::string &name, const std::map<std::string, std::string> &attr);
4343
int64_t writeEndElement(const NS &ns);
44-
int64_t writeElement(const NS &ns, const std::string &name, const std::function<void()> &f = nullptr);
45-
int64_t writeElement(const NS &ns, const std::string &name, const std::map<std::string, std::string> &attr, const std::function<void()> &f = nullptr);
44+
int64_t writeElement(const NS &ns, const std::string &name, const std::function<uint64_t()> &f = nullptr);
45+
int64_t writeElement(const NS &ns, const std::string &name, const std::map<std::string, std::string> &attr, const std::function<uint64_t()> &f = nullptr);
4646
int64_t writeBase64Element(const NS &ns, const std::string &name, const std::vector<unsigned char> &data, const std::map<std::string, std::string> &attr = {});
4747
int64_t writeTextElement(const NS &ns, const std::string &name, const std::map<std::string, std::string> &attr, const std::string &data);
4848

0 commit comments

Comments
 (0)