@@ -32,6 +32,11 @@ using namespace libcdoc;
3232
3333#define RET_ERROR (F ) if (auto rv = F; rv < 0 ) return rv
3434
35+ constexpr XMLWriter::NS DENC{ " denc" , " http://www.w3.org/2001/04/xmlenc#" };
36+ constexpr XMLWriter::NS DS{ " ds" , " http://www.w3.org/2000/09/xmldsig#" };
37+ constexpr XMLWriter::NS XENC11{ " xenc11" , " http://www.w3.org/2009/xmlenc11#" };
38+ constexpr XMLWriter::NS DSIG11{ " dsig11" , " http://www.w3.org/2009/xmldsig11#" };
39+
3540struct FileEntry {
3641 std::string name;
3742 size_t size;
@@ -45,8 +50,6 @@ struct FileEntry {
4550
4651struct CDoc1Writer ::Private final : public XMLWriter
4752{
48- static const XMLWriter::NS DENC, DS, XENC11, DSIG11;
49-
5053 Private (DataConsumer &dst, std::string &last_error)
5154 : XMLWriter(dst)
5255 , lastError(last_error)
@@ -57,57 +60,60 @@ struct CDoc1Writer::Private final: public XMLWriter
5760 std::string &lastError;
5861 std::vector<FileEntry> files;
5962
60- int64_t writeEncryptionProperties (bool use_ddoc);
61- int64_t writeKeyInfo (bool use_ddoc, const std::vector<Recipient> &rcpts, const Crypto::Key& transportKey);
63+ int64_t writeDocument (bool use_ddoc, const std::vector<Recipient> &rcpts, const std::function<int64_t (DataConsumer&)> &f);
6264 int64_t writeRecipient (const std::vector<uint8_t > &recipient, const Crypto::Key& transportKey);
6365};
6466
65- const XMLWriter::NS CDoc1Writer::Private::DENC{ " denc" , " http://www.w3.org/2001/04/xmlenc#" };
66- const XMLWriter::NS CDoc1Writer::Private::DS{ " ds" , " http://www.w3.org/2000/09/xmldsig#" };
67- const XMLWriter::NS CDoc1Writer::Private::XENC11{ " xenc11" , " http://www.w3.org/2009/xmlenc11#" };
68- const XMLWriter::NS CDoc1Writer::Private::DSIG11{ " dsig11" , " http://www.w3.org/2009/xmldsig11#" };
69-
7067CDoc1Writer::CDoc1Writer (DataConsumer *dst, bool take_ownership)
7168 : CDocWriter(1 , dst, take_ownership)
7269{}
7370
7471CDoc1Writer::~CDoc1Writer () noexcept = default ;
7572
76- int64_t CDoc1Writer::Private::writeEncryptionProperties (bool use_ddoc)
73+ int64_t CDoc1Writer::Private::writeDocument (bool use_ddoc, const std::vector<Recipient> &rcpts, const std::function< int64_t (DataConsumer&)> &f )
7774{
78- RET_ERROR ( writeElement (DENC, " EncryptionProperties " , [&]() -> int64_t {
79- RET_ERROR ( writeTextElement (Private::DENC, " EncryptionProperty " , {{" Name " , " LibraryVersion " }}, " cdoc|0.0.1 " ));
80- RET_ERROR (writeTextElement (Private:: DENC, " EncryptionProperty " , {{" Name " , " DocumentFormat " }}, documentFormat ));
81- RET_ERROR ( writeTextElement (Private::DENC, " EncryptionProperty " , {{ " Name " , " Filename " }}, use_ddoc ? " tmp.ddoc " : files. at ( 0 ). name ) );
82- for ( const FileEntry &file: files)
83- {
84- RET_ERROR ( writeTextElement (Private::DENC, " EncryptionProperty " , {{ " Name " , " orig_file " }},
85- file. name + " | " + std::to_string (file. size ) + " | " + " application/octet-stream " + " |D0 " )) ;
75+ return writeElement (DENC, " EncryptedData " ,
76+ {{" MimeType " , use_ddoc ? " http://www.sk.ee/DigiDoc/v1.3.0/digidoc.xsd " : " application/octet-stream " }}, [&] -> int64_t {
77+ RET_ERROR (writeElement ( DENC, " EncryptionMethod " , {{" Algorithm " , method}} ));
78+ libcdoc::Crypto::Key transportKey = libcdoc::Crypto::generateKey (method );
79+ if (transportKey. key . empty ()) {
80+ lastError = " Failed to generate transport key " ;
81+ LOG_ERROR ( " {} " , lastError);
82+ return CRYPTO_ERROR ;
8683 }
87- return OK;
88- }));
89- return writeEndElement (Private::DENC); // EncryptedData
90- }
91-
92- int64_t CDoc1Writer::Private::writeKeyInfo (bool use_ddoc, const std::vector<Recipient> &rcpts, const Crypto::Key& transportKey)
93- {
94- RET_ERROR (writeStartElement (Private::DENC, " EncryptedData" ,
95- {{" MimeType" , use_ddoc ? " http://www.sk.ee/DigiDoc/v1.3.0/digidoc.xsd" : " application/octet-stream" }}));
96- RET_ERROR (writeElement (Private::DENC, " EncryptionMethod" , {{" Algorithm" , method}}));
97- return writeElement (Private::DS, " KeyInfo" , {}, [&]() -> int64_t {
98- for (const Recipient& key : rcpts) {
99- if (!key.isCertificate ()) {
100- lastError = " Invalid recipient type" ;
101- LOG_ERROR (" {}" , lastError);
102- return UNSPECIFIED_ERROR;
84+ RET_ERROR (writeElement (DS, " KeyInfo" , {}, [&] -> int64_t {
85+ for (const Recipient& key : rcpts) {
86+ if (!key.isCertificate ()) {
87+ lastError = " Invalid recipient type" ;
88+ LOG_ERROR (" {}" , lastError);
89+ return UNSPECIFIED_ERROR;
90+ }
91+ if (auto rv = writeRecipient (key.cert , transportKey); rv < 0 ) {
92+ lastError = " Failed to write Recipient info" ;
93+ LOG_ERROR (" {}" , lastError);
94+ return rv;
95+ }
10396 }
104- if (auto rv = writeRecipient (key.cert , transportKey); rv < 0 ) {
105- lastError = " Failed to write Recipient info" ;
106- LOG_ERROR (" {}" , lastError);
107- return rv;
97+ return OK;
98+ }));
99+ RET_ERROR (writeElement (DENC, " CipherData" , [&] {
100+ return writeBase64Element (DENC, " CipherValue" , [&](DataConsumer &dst) -> int64_t {
101+ EncryptionConsumer enc (dst, method, transportKey);
102+ RET_ERROR (f (enc));
103+ return enc.close ();
104+ });
105+ }));
106+ return writeElement (DENC, " EncryptionProperties" , [&] -> int64_t {
107+ RET_ERROR (writeTextElement (DENC, " EncryptionProperty" , {{" Name" , " LibraryVersion" }}, " cdoc|0.0.1" ));
108+ RET_ERROR (writeTextElement (DENC, " EncryptionProperty" , {{" Name" , " DocumentFormat" }}, documentFormat));
109+ RET_ERROR (writeTextElement (DENC, " EncryptionProperty" , {{" Name" , " Filename" }}, use_ddoc ? " tmp.ddoc" : files.at (0 ).name ));
110+ for (const FileEntry &file: files)
111+ {
112+ RET_ERROR (writeTextElement (DENC, " EncryptionProperty" , {{" Name" , " orig_file" }},
113+ file.name + " |" + std::to_string (file.size ) + " |" + " application/octet-stream" + " |D0" ));
108114 }
109- }
110- return OK ;
115+ return OK;
116+ }) ;
111117 });
112118}
113119
@@ -134,18 +140,18 @@ int64_t CDoc1Writer::Private::writeRecipient(const std::vector<uint8_t> &recipie
134140 OPENSSL_free (data);
135141 return cn;
136142 }();
137- return writeElement (Private:: DENC, " EncryptedKey" , {{" Recipient" , cn}}, [&]() -> int64_t {
143+ return writeElement (DENC, " EncryptedKey" , {{" Recipient" , cn}}, [&] -> int64_t {
138144 std::vector<uint8_t > encryptedData;
139145 auto *peerPKey = X509_get0_pubkey (peerCert.get ());
140146 switch (EVP_PKEY_base_id (peerPKey))
141147 {
142148 case EVP_PKEY_RSA:
143149 {
144150 encryptedData = Crypto::encrypt (peerPKey, RSA_PKCS1_PADDING, transportKey.key );
145- RET_ERROR (writeElement (Private:: DENC, " EncryptionMethod" , {{" Algorithm" , Crypto::RSA_MTH}}));
146- RET_ERROR (writeElement (Private:: DS, " KeyInfo" , [&] {
147- return writeElement (Private:: DS, " X509Data" , [&] {
148- return writeBase64Element (Private:: DS, " X509Certificate" , recipient);
151+ RET_ERROR (writeElement (DENC, " EncryptionMethod" , {{" Algorithm" , Crypto::RSA_MTH}}));
152+ RET_ERROR (writeElement (DS, " KeyInfo" , [&] {
153+ return writeElement (DS, " X509Data" , [&] {
154+ return writeBase64Element (DS, " X509Certificate" , recipient);
149155 });
150156 }));
151157 break ;
@@ -183,28 +189,28 @@ int64_t CDoc1Writer::Private::writeRecipient(const std::vector<uint8_t> &recipie
183189 LOG_TRACE_KEY (" iv {}" , transportKey.iv );
184190 LOG_TRACE_KEY (" transport {}" , transportKey.key );
185191
186- RET_ERROR (writeElement (Private:: DENC, " EncryptionMethod" , {{" Algorithm" , encryptionMethod}}));
187- RET_ERROR (writeElement (Private:: DS, " KeyInfo" , [&] {
188- return writeElement (Private:: DENC, " AgreementMethod" , {{" Algorithm" , Crypto::AGREEMENT_MTH}}, [&] {
189- RET_ERROR (writeElement (Private:: XENC11, " KeyDerivationMethod" , {{" Algorithm" , Crypto::CONCATKDF_MTH}}, [&] {
190- return writeElement (Private:: XENC11, " ConcatKDFParams" , {
192+ RET_ERROR (writeElement (DENC, " EncryptionMethod" , {{" Algorithm" , encryptionMethod}}));
193+ RET_ERROR (writeElement (DS, " KeyInfo" , [&] {
194+ return writeElement (DENC, " AgreementMethod" , {{" Algorithm" , Crypto::AGREEMENT_MTH}}, [&] {
195+ RET_ERROR (writeElement (XENC11, " KeyDerivationMethod" , {{" Algorithm" , Crypto::CONCATKDF_MTH}}, [&] {
196+ return writeElement (XENC11, " ConcatKDFParams" , {
191197 {" AlgorithmID" , " 00" + toHex (AlgorithmID)},
192198 {" PartyUInfo" , " 00" + toHex (SsDer)},
193199 {" PartyVInfo" , " 00" + toHex (recipient)}}, [&] {
194- return writeElement (Private:: DS, " DigestMethod" , {{" Algorithm" , concatDigest}});
200+ return writeElement (DS, " DigestMethod" , {{" Algorithm" , concatDigest}});
195201 });
196202 }));
197- RET_ERROR (writeElement (Private:: DENC, " OriginatorKeyInfo" , [&] {
198- return writeElement (Private:: DS, " KeyValue" , [&] {
199- return writeElement (Private:: DSIG11, " ECKeyValue" , [&] {
200- RET_ERROR (writeElement (Private:: DSIG11, " NamedCurve" , {{" URI" , " urn:oid:" + oid}}));
201- return writeBase64Element (Private:: DSIG11, " PublicKey" , SsDer);
203+ RET_ERROR (writeElement (DENC, " OriginatorKeyInfo" , [&] {
204+ return writeElement (DS, " KeyValue" , [&] {
205+ return writeElement (DSIG11, " ECKeyValue" , [&] {
206+ RET_ERROR (writeElement (DSIG11, " NamedCurve" , {{" URI" , " urn:oid:" + oid}}));
207+ return writeBase64Element (DSIG11, " PublicKey" , SsDer);
202208 });
203209 });
204210 }));
205- return writeElement (Private:: DENC, " RecipientKeyInfo" , [&] {
206- return writeElement (Private:: DS, " X509Data" , [&] {
207- return writeBase64Element (Private:: DS, " X509Certificate" , recipient);
211+ return writeElement (DENC, " RecipientKeyInfo" , [&] {
212+ return writeElement (DS, " X509Data" , [&] {
213+ return writeBase64Element (DS, " X509Certificate" , recipient);
208214 });
209215 });
210216 });
@@ -217,8 +223,8 @@ int64_t CDoc1Writer::Private::writeRecipient(const std::vector<uint8_t> &recipie
217223
218224 if (encryptedData.empty ())
219225 return UNSPECIFIED_ERROR;
220- return writeElement (Private:: DENC, " CipherData" , [&] {
221- return writeBase64Element (Private:: DENC, " CipherValue" , encryptedData);
226+ return writeElement (DENC, " CipherData" , [&] {
227+ return writeBase64Element (DENC, " CipherValue" , encryptedData);
222228 });
223229 });
224230}
@@ -231,38 +237,31 @@ CDoc1Writer::encrypt(libcdoc::MultiDataSource& src, const std::vector<libcdoc::R
231237{
232238 if (keys.empty ())
233239 return WORKFLOW_ERROR;
234- RET_ERROR (beginEncryption ());
235240 rcpts = keys;
236- Crypto::Key transportKey = Crypto::generateKey (d-> method );
241+ RET_ERROR ( beginEncryption () );
237242 int n_components = src.getNumComponents ();
238243 bool use_ddoc = (n_components > 1 ) || (n_components == libcdoc::NOT_IMPLEMENTED);
239-
240- RET_ERROR (d->writeKeyInfo (use_ddoc, keys, transportKey));
241- RET_ERROR (d->writeElement (Private::DENC, " CipherData" , [&] {
242- return d->writeBase64Element (Private::DENC, " CipherValue" , [&](DataConsumer &dst) -> int64_t {
243- EncryptionConsumer enc (dst, d->method , transportKey);
244- std::string name;
245- int64_t size;
246- if (use_ddoc) {
247- DDOCWriter ddoc (enc);
248- result_t result;
249- for (result = src.next (name, size); result == OK; result = src.next (name, size)) {
250- RET_ERROR (ddoc.addFile (name, " application/octet-stream" , size, src));
251- d->files .push_back ({name, size_t (size)});
252- }
253- if (result != END_OF_STREAM)
254- return result;
255- } else {
256- RET_ERROR (src.next (name, size));
257- if (auto rv = src.readAll (enc); rv >= 0 )
258- d->files .push_back ({std::move (name), size_t (rv)});
259- else
260- return rv;
244+ RET_ERROR (d->writeDocument (use_ddoc, keys, [&](DataConsumer &dst) -> int64_t {
245+ std::string name;
246+ int64_t size;
247+ if (use_ddoc) {
248+ DDOCWriter ddoc (dst);
249+ result_t result;
250+ for (result = src.next (name, size); result == OK; result = src.next (name, size)) {
251+ RET_ERROR (ddoc.addFile (name, " application/octet-stream" , size, src));
252+ d->files .push_back ({name, size_t (size)});
261253 }
262- return enc.close ();
263- });
254+ if (result != END_OF_STREAM)
255+ return result;
256+ } else {
257+ RET_ERROR (src.next (name, size));
258+ if (auto rv = src.readAll (dst); rv >= 0 )
259+ d->files .push_back ({std::move (name), size_t (rv)});
260+ else
261+ return rv;
262+ }
263+ return OK;
264264 }));
265- RET_ERROR (d->writeEncryptionProperties (use_ddoc));
266265 d.reset ();
267266 if (owned) return dst->close ();
268267 return OK;
@@ -271,8 +270,16 @@ CDoc1Writer::encrypt(libcdoc::MultiDataSource& src, const std::vector<libcdoc::R
271270libcdoc::result_t
272271CDoc1Writer::beginEncryption ()
273272{
274- if (d)
273+ if (rcpts.empty ()) {
274+ setLastError (" No recipients added" );
275+ LOG_ERROR (" {}" , last_error);
276+ return WORKFLOW_ERROR;
277+ }
278+ if (d) {
279+ setLastError (" Encryption already started" );
280+ LOG_ERROR (" {}" , last_error);
275281 return WORKFLOW_ERROR;
282+ }
276283 d = std::make_unique<Private>(*dst, last_error);
277284 return libcdoc::OK;
278285}
@@ -308,26 +315,16 @@ CDoc1Writer::writeData(const uint8_t *src, size_t size)
308315libcdoc::result_t
309316CDoc1Writer::finishEncryption ()
310317{
311- if (!d || rcpts. empty () || d->files .empty ())
318+ if (!d || d->files .empty ())
312319 return WORKFLOW_ERROR;
313320 bool use_ddoc = d->files .size () > 1 ;
314- libcdoc::Crypto::Key transportKey = libcdoc::Crypto::generateKey (d->method );
315-
316- RET_ERROR (d->writeKeyInfo (use_ddoc, rcpts, transportKey));
317- RET_ERROR (d->writeElement (Private::DENC, " CipherData" , [&] {
318- return d->writeBase64Element (Private::DENC, " CipherValue" , [&](DataConsumer &dst) -> int64_t {
319- EncryptionConsumer enc (dst, d->method , transportKey);
320- if (use_ddoc)
321- {
322- for (DDOCWriter ddoc (enc); const FileEntry& file : d->files )
323- RET_ERROR (ddoc.addFile (file.name , " application/octet-stream" , file.data ));
324- }
325- else
326- RET_ERROR (VectorSource (d->files .back ().data ).readAll (enc));
327- return enc.close ();
328- });
321+ RET_ERROR (d->writeDocument (use_ddoc, rcpts, [&, this ](DataConsumer &dst) -> int64_t {
322+ if (!use_ddoc)
323+ return VectorSource (d->files .back ().data ).readAll (dst);
324+ for (DDOCWriter ddoc (dst); const FileEntry& file : d->files )
325+ RET_ERROR (ddoc.addFile (file.name , " application/octet-stream" , file.data ));
326+ return OK;
329327 }));
330- RET_ERROR (d->writeEncryptionProperties (use_ddoc));
331328 d.reset ();
332329 if (owned) return dst->close ();
333330 return libcdoc::OK;
0 commit comments