Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 103 additions & 106 deletions cdoc/CDoc1Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ using namespace libcdoc;

#define RET_ERROR(F) if (auto rv = F; rv < 0) return rv

constexpr XMLWriter::NS DENC{ "denc", "http://www.w3.org/2001/04/xmlenc#" };
constexpr XMLWriter::NS DS{ "ds", "http://www.w3.org/2000/09/xmldsig#" };
constexpr XMLWriter::NS XENC11{ "xenc11", "http://www.w3.org/2009/xmlenc11#" };
constexpr XMLWriter::NS DSIG11{ "dsig11", "http://www.w3.org/2009/xmldsig11#" };

struct FileEntry {
std::string name;
size_t size;
Expand All @@ -45,8 +50,6 @@ struct FileEntry {

struct CDoc1Writer::Private final: public XMLWriter
{
static const XMLWriter::NS DENC, DS, XENC11, DSIG11;

Private(DataConsumer &dst, std::string &last_error)
: XMLWriter(dst)
, lastError(last_error)
Expand All @@ -57,57 +60,60 @@ struct CDoc1Writer::Private final: public XMLWriter
std::string &lastError;
std::vector<FileEntry> files;

int64_t writeEncryptionProperties(bool use_ddoc);
int64_t writeKeyInfo(bool use_ddoc, const std::vector<Recipient> &rcpts, const Crypto::Key& transportKey);
int64_t writeDocument(bool use_ddoc, const std::vector<Recipient> &rcpts, const std::function<int64_t(DataConsumer&)> &f);
int64_t writeRecipient(const std::vector<uint8_t> &recipient, const Crypto::Key& transportKey);
};

const XMLWriter::NS CDoc1Writer::Private::DENC{ "denc", "http://www.w3.org/2001/04/xmlenc#" };
const XMLWriter::NS CDoc1Writer::Private::DS{ "ds", "http://www.w3.org/2000/09/xmldsig#" };
const XMLWriter::NS CDoc1Writer::Private::XENC11{ "xenc11", "http://www.w3.org/2009/xmlenc11#" };
const XMLWriter::NS CDoc1Writer::Private::DSIG11{ "dsig11", "http://www.w3.org/2009/xmldsig11#" };

CDoc1Writer::CDoc1Writer(DataConsumer *dst, bool take_ownership)
: CDocWriter(1, dst, take_ownership)
{}

CDoc1Writer::~CDoc1Writer() noexcept = default;

int64_t CDoc1Writer::Private::writeEncryptionProperties(bool use_ddoc)
int64_t CDoc1Writer::Private::writeDocument(bool use_ddoc, const std::vector<Recipient> &rcpts, const std::function<int64_t(DataConsumer&)> &f)
{
RET_ERROR(writeElement(DENC, "EncryptionProperties", [&]() -> int64_t {
RET_ERROR(writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "LibraryVersion"}}, "cdoc|0.0.1"));
RET_ERROR(writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "DocumentFormat"}}, documentFormat));
RET_ERROR(writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "Filename"}}, use_ddoc ? "tmp.ddoc" : files.at(0).name));
for(const FileEntry &file: files)
{
RET_ERROR(writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "orig_file"}},
file.name + "|" + std::to_string(file.size) + "|" + "application/octet-stream" + "|D0"));
return writeElement(DENC, "EncryptedData",
{{"MimeType", use_ddoc ? "http://www.sk.ee/DigiDoc/v1.3.0/digidoc.xsd" : "application/octet-stream"}}, [&] -> int64_t {
RET_ERROR(writeElement(DENC, "EncryptionMethod", {{"Algorithm", method}}));
libcdoc::Crypto::Key transportKey = libcdoc::Crypto::generateKey(method);
if (transportKey.key.empty()) {
lastError = "Failed to generate transport key";
LOG_ERROR("{}", lastError);
return CRYPTO_ERROR;
}
return OK;
}));
return writeEndElement(Private::DENC); // EncryptedData
}

int64_t CDoc1Writer::Private::writeKeyInfo(bool use_ddoc, const std::vector<Recipient> &rcpts, const Crypto::Key& transportKey)
{
RET_ERROR(writeStartElement(Private::DENC, "EncryptedData",
{{"MimeType", use_ddoc ? "http://www.sk.ee/DigiDoc/v1.3.0/digidoc.xsd" : "application/octet-stream"}}));
RET_ERROR(writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", method}}));
return writeElement(Private::DS, "KeyInfo", {}, [&]() -> int64_t {
for (const Recipient& key : rcpts) {
if (!key.isCertificate()) {
lastError = "Invalid recipient type";
LOG_ERROR("{}", lastError);
return UNSPECIFIED_ERROR;
RET_ERROR(writeElement(DS, "KeyInfo", {}, [&] -> int64_t {
for (const Recipient& key : rcpts) {
if (!key.isCertificate()) {
lastError = "Invalid recipient type";
LOG_ERROR("{}", lastError);
return UNSPECIFIED_ERROR;
}
if(auto rv = writeRecipient(key.cert, transportKey); rv < 0) {
lastError = "Failed to write Recipient info";
LOG_ERROR("{}", lastError);
return rv;
}
}
if(auto rv = writeRecipient(key.cert, transportKey); rv < 0) {
lastError = "Failed to write Recipient info";
LOG_ERROR("{}", lastError);
return rv;
return OK;
}));
RET_ERROR(writeElement(DENC, "CipherData", [&] {
return writeBase64Element(DENC, "CipherValue", [&](DataConsumer &dst) -> int64_t {
EncryptionConsumer enc(dst, method, transportKey);
RET_ERROR(f(enc));
return enc.close();
});
}));
return writeElement(DENC, "EncryptionProperties", [&] -> int64_t {
RET_ERROR(writeTextElement(DENC, "EncryptionProperty", {{"Name", "LibraryVersion"}}, "cdoc|0.0.1"));
RET_ERROR(writeTextElement(DENC, "EncryptionProperty", {{"Name", "DocumentFormat"}}, documentFormat));
RET_ERROR(writeTextElement(DENC, "EncryptionProperty", {{"Name", "Filename"}}, use_ddoc ? "tmp.ddoc" : files.at(0).name));
for(const FileEntry &file: files)
{
RET_ERROR(writeTextElement(DENC, "EncryptionProperty", {{"Name", "orig_file"}},
file.name + "|" + std::to_string(file.size) + "|" + "application/octet-stream" + "|D0"));
}
}
return OK;
return OK;
});
});
}

Expand All @@ -134,18 +140,18 @@ int64_t CDoc1Writer::Private::writeRecipient(const std::vector<uint8_t> &recipie
OPENSSL_free(data);
return cn;
}();
return writeElement(Private::DENC, "EncryptedKey", {{"Recipient", cn}}, [&]() -> int64_t {
return writeElement(DENC, "EncryptedKey", {{"Recipient", cn}}, [&] -> int64_t {
std::vector<uint8_t> encryptedData;
auto *peerPKey = X509_get0_pubkey(peerCert.get());
switch(EVP_PKEY_base_id(peerPKey))
{
case EVP_PKEY_RSA:
{
encryptedData = Crypto::encrypt(peerPKey, RSA_PKCS1_PADDING, transportKey.key);
RET_ERROR(writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", Crypto::RSA_MTH}}));
RET_ERROR(writeElement(Private::DS, "KeyInfo", [&] {
return writeElement(Private::DS, "X509Data", [&] {
return writeBase64Element(Private::DS, "X509Certificate", recipient);
RET_ERROR(writeElement(DENC, "EncryptionMethod", {{"Algorithm", Crypto::RSA_MTH}}));
RET_ERROR(writeElement(DS, "KeyInfo", [&] {
return writeElement(DS, "X509Data", [&] {
return writeBase64Element(DS, "X509Certificate", recipient);
});
}));
break;
Expand Down Expand Up @@ -183,28 +189,28 @@ int64_t CDoc1Writer::Private::writeRecipient(const std::vector<uint8_t> &recipie
LOG_TRACE_KEY("iv {}", transportKey.iv);
LOG_TRACE_KEY("transport {}", transportKey.key);

RET_ERROR(writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", encryptionMethod}}));
RET_ERROR(writeElement(Private::DS, "KeyInfo", [&] {
return writeElement(Private::DENC, "AgreementMethod", {{"Algorithm", Crypto::AGREEMENT_MTH}}, [&] {
RET_ERROR(writeElement(Private::XENC11, "KeyDerivationMethod", {{"Algorithm", Crypto::CONCATKDF_MTH}}, [&] {
return writeElement(Private::XENC11, "ConcatKDFParams", {
RET_ERROR(writeElement(DENC, "EncryptionMethod", {{"Algorithm", encryptionMethod}}));
RET_ERROR(writeElement(DS, "KeyInfo", [&] {
return writeElement(DENC, "AgreementMethod", {{"Algorithm", Crypto::AGREEMENT_MTH}}, [&] {
RET_ERROR(writeElement(XENC11, "KeyDerivationMethod", {{"Algorithm", Crypto::CONCATKDF_MTH}}, [&] {
return writeElement(XENC11, "ConcatKDFParams", {
{"AlgorithmID", "00" + toHex(AlgorithmID)},
{"PartyUInfo", "00" + toHex(SsDer)},
{"PartyVInfo", "00" + toHex(recipient)}}, [&] {
return writeElement(Private::DS, "DigestMethod", {{"Algorithm", concatDigest}});
return writeElement(DS, "DigestMethod", {{"Algorithm", concatDigest}});
});
}));
RET_ERROR(writeElement(Private::DENC, "OriginatorKeyInfo", [&] {
return writeElement(Private::DS, "KeyValue", [&] {
return writeElement(Private::DSIG11, "ECKeyValue", [&] {
RET_ERROR(writeElement(Private::DSIG11, "NamedCurve", {{"URI", "urn:oid:" + oid}}));
return writeBase64Element(Private::DSIG11, "PublicKey", SsDer);
RET_ERROR(writeElement(DENC, "OriginatorKeyInfo", [&] {
return writeElement(DS, "KeyValue", [&] {
return writeElement(DSIG11, "ECKeyValue", [&] {
RET_ERROR(writeElement(DSIG11, "NamedCurve", {{"URI", "urn:oid:" + oid}}));
return writeBase64Element(DSIG11, "PublicKey", SsDer);
});
});
}));
return writeElement(Private::DENC, "RecipientKeyInfo", [&] {
return writeElement(Private::DS, "X509Data", [&] {
return writeBase64Element(Private::DS, "X509Certificate", recipient);
return writeElement(DENC, "RecipientKeyInfo", [&] {
return writeElement(DS, "X509Data", [&] {
return writeBase64Element(DS, "X509Certificate", recipient);
});
});
});
Expand All @@ -217,8 +223,8 @@ int64_t CDoc1Writer::Private::writeRecipient(const std::vector<uint8_t> &recipie

if (encryptedData.empty())
return UNSPECIFIED_ERROR;
return writeElement(Private::DENC, "CipherData", [&] {
return writeBase64Element(Private::DENC, "CipherValue", encryptedData);
return writeElement(DENC, "CipherData", [&] {
return writeBase64Element(DENC, "CipherValue", encryptedData);
});
});
}
Expand All @@ -231,38 +237,31 @@ CDoc1Writer::encrypt(libcdoc::MultiDataSource& src, const std::vector<libcdoc::R
{
if(keys.empty())
return WORKFLOW_ERROR;
RET_ERROR(beginEncryption());
rcpts = keys;
Crypto::Key transportKey = Crypto::generateKey(d->method);
RET_ERROR(beginEncryption());
int n_components = src.getNumComponents();
bool use_ddoc = (n_components > 1) || (n_components == libcdoc::NOT_IMPLEMENTED);

RET_ERROR(d->writeKeyInfo(use_ddoc, keys, transportKey));
RET_ERROR(d->writeElement(Private::DENC, "CipherData", [&] {
return d->writeBase64Element(Private::DENC, "CipherValue", [&](DataConsumer &dst) -> int64_t {
EncryptionConsumer enc(dst, d->method, transportKey);
std::string name;
int64_t size;
if(use_ddoc) {
DDOCWriter ddoc(enc);
result_t result;
for (result = src.next(name, size); result == OK; result = src.next(name, size)) {
RET_ERROR(ddoc.addFile(name, "application/octet-stream", size, src));
d->files.push_back({name, size_t(size)});
}
if(result != END_OF_STREAM)
return result;
} else {
RET_ERROR(src.next(name, size));
if(auto rv = src.readAll(enc); rv >= 0)
d->files.push_back({std::move(name), size_t(rv)});
else
return rv;
RET_ERROR(d->writeDocument(use_ddoc, keys, [&](DataConsumer &dst) -> int64_t {
std::string name;
int64_t size;
if(use_ddoc) {
DDOCWriter ddoc(dst);
result_t result;
for (result = src.next(name, size); result == OK; result = src.next(name, size)) {
RET_ERROR(ddoc.addFile(name, "application/octet-stream", size, src));
d->files.push_back({name, size_t(size)});
}
return enc.close();
});
if(result != END_OF_STREAM)
return result;
} else {
RET_ERROR(src.next(name, size));
if(auto rv = src.readAll(dst); rv >= 0)
d->files.push_back({std::move(name), size_t(rv)});
else
return rv;
}
return OK;
}));
RET_ERROR(d->writeEncryptionProperties(use_ddoc));
d.reset();
if (owned) return dst->close();
return OK;
Expand All @@ -271,8 +270,16 @@ CDoc1Writer::encrypt(libcdoc::MultiDataSource& src, const std::vector<libcdoc::R
libcdoc::result_t
CDoc1Writer::beginEncryption()
{
if(d)
if(rcpts.empty()) {
setLastError("No recipients added");
LOG_ERROR("{}", last_error);
return WORKFLOW_ERROR;
}
if(d) {
setLastError("Encryption already started");
LOG_ERROR("{}", last_error);
return WORKFLOW_ERROR;
}
d = std::make_unique<Private>(*dst, last_error);
return libcdoc::OK;
}
Expand Down Expand Up @@ -308,26 +315,16 @@ CDoc1Writer::writeData(const uint8_t *src, size_t size)
libcdoc::result_t
CDoc1Writer::finishEncryption()
{
if(!d || rcpts.empty() || d->files.empty())
if(!d || d->files.empty())
return WORKFLOW_ERROR;
bool use_ddoc = d->files.size() > 1;
libcdoc::Crypto::Key transportKey = libcdoc::Crypto::generateKey(d->method);

RET_ERROR(d->writeKeyInfo(use_ddoc, rcpts, transportKey));
RET_ERROR(d->writeElement(Private::DENC, "CipherData", [&] {
return d->writeBase64Element(Private::DENC, "CipherValue", [&](DataConsumer &dst) -> int64_t {
EncryptionConsumer enc(dst, d->method, transportKey);
if(use_ddoc)
{
for(DDOCWriter ddoc(enc); const FileEntry& file : d->files)
RET_ERROR(ddoc.addFile(file.name, "application/octet-stream", file.data));
}
else
RET_ERROR(VectorSource(d->files.back().data).readAll(enc));
return enc.close();
});
RET_ERROR(d->writeDocument(use_ddoc, rcpts, [&, this](DataConsumer &dst) -> int64_t {
if(!use_ddoc)
return VectorSource(d->files.back().data).readAll(dst);
for(DDOCWriter ddoc(dst); const FileEntry& file : d->files)
RET_ERROR(ddoc.addFile(file.name, "application/octet-stream", file.data));
return OK;
}));
RET_ERROR(d->writeEncryptionProperties(use_ddoc));
d.reset();
if (owned) return dst->close();
return libcdoc::OK;
Expand Down
2 changes: 1 addition & 1 deletion cdoc/DDocWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ using namespace libcdoc;
* @brief DDOCWriter is used for storing multiple files.
*/

const XMLWriter::NS DDOCWriter::DDOC{ "", "http://www.sk.ee/DigiDoc/v1.3.0#" };
constexpr XMLWriter::NS DDOC{ nullptr, "http://www.sk.ee/DigiDoc/v1.3.0#" };

DDOCWriter::DDOCWriter(DataConsumer &dst)
: XMLWriter(dst)
Expand Down
2 changes: 0 additions & 2 deletions cdoc/DDocWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ class DDOCWriter final: public XMLWriter
DDOCWriter(const DDOCWriter &) = delete;
DDOCWriter &operator=(const DDOCWriter &) = delete;
int fileCount = 0;

static const NS DDOC;
};

}
Loading