diff --git a/cdoc/CDoc.h b/cdoc/CDoc.h index 2249c79f..ec5cd7b4 100644 --- a/cdoc/CDoc.h +++ b/cdoc/CDoc.h @@ -31,6 +31,21 @@ namespace libcdoc { */ using result_t = int64_t; + +/** + * @brief The public key type + */ +enum class PKType : uint8_t { + /** + * Elliptic curve + */ + ECC, + /** + * RSA + */ + RSA +}; + enum { /** * @brief Operation completed successfully @@ -215,6 +230,7 @@ namespace Label { static constexpr std::string_view LAST_NAME = "last_name"; static constexpr std::string_view FIRST_NAME = "first_name"; static constexpr std::string_view CERT_SHA1 = "cert_sha1"; + static constexpr const char* EXPIRY = "server_exp"; } } diff --git a/cdoc/CDoc1Reader.cpp b/cdoc/CDoc1Reader.cpp index 676877de..fa9b365a 100644 --- a/cdoc/CDoc1Reader.cpp +++ b/cdoc/CDoc1Reader.cpp @@ -83,12 +83,12 @@ CDoc1Reader::getLockForCert(const std::vector& cert) ll.encrypted_fmk.empty()) continue; switch(cc.getAlgorithm()) { - case libcdoc::Certificate::RSA: + case libcdoc::PKType::RSA: if (ll.getString(Lock::Params::METHOD) == libcdoc::Crypto::RSA_MTH) { return i; } break; - case libcdoc::Certificate::ECC: + case libcdoc::PKType::ECC: if(!ll.getBytes(Lock::Params::KEY_MATERIAL).empty() && std::find(SUPPORTED_KWAES.cbegin(), SUPPORTED_KWAES.cend(), ll.getString(Lock::Params::METHOD)) != SUPPORTED_KWAES.cend()) { return i; @@ -310,7 +310,7 @@ CDoc1Reader::CDoc1Reader(libcdoc::DataSource *src, bool delete_on_close) Certificate ssl(cert); key.setBytes(Lock::CERT, std::move(cert)); key.setBytes(Lock::RCPT_KEY, ssl.getPublicKey()); - key.pk_type = (ssl.getAlgorithm() == libcdoc::Certificate::RSA) ? Lock::RSA : Lock::ECC; + key.pk_type = ssl.getAlgorithm(); } // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/CipherData/CipherValue else if(reader.isElement("CipherValue")) diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index 4e9b1199..114baaf0 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -511,7 +511,7 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor if(const auto *key = recipient.capsule_as_recipients_ECCPublicKeyCapsule()) { if(key->curve() == EllipticCurve::secp384r1) { lock.type = Lock::Type::PUBLIC_KEY; - lock.pk_type = Lock::PKType::ECC; + lock.pk_type = PKType::ECC; lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(key->recipient_public_key())); lock.setBytes(Lock::Params::KEY_MATERIAL, toUint8Vector(key->sender_public_key())); LOG_DBG("Load PK: {}", toHex(lock.getBytes(Lock::Params::RCPT_KEY))); @@ -524,7 +524,7 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor if(const auto *key = recipient.capsule_as_recipients_RSAPublicKeyCapsule()) { lock.type = Lock::Type::PUBLIC_KEY; - lock.pk_type = Lock::PKType::RSA; + lock.pk_type = PKType::RSA; lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(key->recipient_public_key())); lock.setBytes(Lock::Params::KEY_MATERIAL, toUint8Vector(key->encrypted_kek())); } @@ -539,13 +539,13 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor LOG_ERROR("Unsupported elliptic curve key type"); return; } - lock.pk_type = Lock::PKType::ECC; + lock.pk_type = PKType::ECC; lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(eccDetails->recipient_public_key())); } break; case KeyDetailsUnion::RsaKeyDetails: if(const RsaKeyDetails *rsaDetails = server->recipient_key_details_as_RsaKeyDetails()) { - lock.pk_type = Lock::PKType::RSA; + lock.pk_type = PKType::RSA; lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(rsaDetails->recipient_public_key())); } break; diff --git a/cdoc/CDoc2Writer.cpp b/cdoc/CDoc2Writer.cpp index eceeeffe..7e7c39d5 100644 --- a/cdoc/CDoc2Writer.cpp +++ b/cdoc/CDoc2Writer.cpp @@ -104,7 +104,7 @@ createRSACapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::Recipie return cdoc20::header::CreateRecipientRecord(builder, cdoc20::header::Capsule::recipients_RSAPublicKeyCapsule, capsule.Union(), - builder.CreateString(rcpt.getLabel({{"server_exp", rcpt.expiry_ts == 0 ? std::string() : std::to_string(rcpt.expiry_ts)}})), + builder.CreateString(rcpt.getLabel({{CDoc2::Label::EXPIRY, rcpt.expiry_ts == 0 ? std::string() : std::to_string(rcpt.expiry_ts)}})), builder.CreateVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); } @@ -123,7 +123,7 @@ createRSAServerCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::R return cdoc20::header::CreateRecipientRecord(builder, cdoc20::header::Capsule::recipients_KeyServerCapsule, capsule.Union(), - builder.CreateString(rcpt.getLabel({{"server_exp", std::to_string(expiry_time)}})), + builder.CreateString(rcpt.getLabel({{CDoc2::Label::EXPIRY, std::to_string(expiry_time)}})), builder.CreateVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); } @@ -138,7 +138,7 @@ createECCCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::Recipie return cdoc20::header::CreateRecipientRecord(builder, cdoc20::header::Capsule::recipients_ECCPublicKeyCapsule, capsule.Union(), - builder.CreateString(rcpt.getLabel({{"server_exp", rcpt.expiry_ts == 0 ? std::string() : std::to_string(rcpt.expiry_ts)}})), + builder.CreateString(rcpt.getLabel({{CDoc2::Label::EXPIRY, rcpt.expiry_ts == 0 ? std::string() : std::to_string(rcpt.expiry_ts)}})), builder.CreateVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); } @@ -158,7 +158,7 @@ createECCServerCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::R return cdoc20::header::CreateRecipientRecord(builder, cdoc20::header::Capsule::recipients_KeyServerCapsule, capsule.Union(), - builder.CreateString(rcpt.getLabel({{"server_exp", std::to_string(expiry_time)}})), + builder.CreateString(rcpt.getLabel({{CDoc2::Label::EXPIRY, std::to_string(expiry_time)}})), builder.CreateVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); } @@ -222,7 +222,7 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vectorrandom(kek, libcdoc::CDoc2::KEY_LEN); if (libcdoc::Crypto::xor_data(xor_key, fmk, kek) != libcdoc::OK) { setLastError("Internal error"); diff --git a/cdoc/CDocCipher.cpp b/cdoc/CDocCipher.cpp index e0cedb24..31c728e8 100644 --- a/cdoc/CDocCipher.cpp +++ b/cdoc/CDocCipher.cpp @@ -280,7 +280,7 @@ fill_recipients_from_rcpt_info(ToolConf& conf, ToolCrypto& crypto, std::vector Certificate::getDigest() const diff --git a/cdoc/Certificate.h b/cdoc/Certificate.h index 4ac87832..45b537d2 100644 --- a/cdoc/Certificate.h +++ b/cdoc/Certificate.h @@ -19,6 +19,7 @@ #ifndef SSLCERTIFICATE_H #define SSLCERTIFICATE_H +#include "CDoc.h" #include "utils/memory.h" #include @@ -30,11 +31,6 @@ namespace libcdoc { class Certificate { public: - enum Algorithm : unsigned char { - RSA, - ECC - }; - enum EIDType : unsigned char { Unknown, IDCard, @@ -53,7 +49,7 @@ class Certificate { EIDType getEIDType() const; std::vector getPublicKey() const; - Algorithm getAlgorithm() const; + PKType getAlgorithm() const; time_t getNotAfter() const; std::vector getDigest() const; diff --git a/cdoc/Lock.cpp b/cdoc/Lock.cpp index d5dd3614..ce9f9079 100644 --- a/cdoc/Lock.cpp +++ b/cdoc/Lock.cpp @@ -23,13 +23,15 @@ #include "json/base.h" +#include + namespace libcdoc { std::string Lock::getString(Params key) const { - const std::vector& bytes = params.at(key); - return std::string((const char *) bytes.data(), bytes.size()); + const std::vector& bytes = params.at(key); + return {(const char *) bytes.data(), bytes.size()}; } int32_t @@ -59,50 +61,47 @@ Lock::parseLabel(const std::string& label) { std::map parsed_label; // Check if provided label starts with the machine generated label prefix. - if (!label.starts_with(CDoc2::LABELPREFIX)) - { + if (!label.starts_with(CDoc2::LABELPREFIX)) { return parsed_label; } - std::string label_wo_prefix(label.substr(CDoc2::LABELPREFIX.size())); + auto label_wo_prefix = std::string_view(label).substr(CDoc2::LABELPREFIX.size()); // Label to be processed - std::string label_to_prcss; + std::string decodedBase64; // Strong ref + std::string_view label_to_prcss; // We ignore mediatype part // Check, if the label is Base64 encoded - auto base64IndPos = label_wo_prefix.find(CDoc2::LABELBASE64IND); - if (base64IndPos == std::string::npos) - { - if (label_wo_prefix.starts_with(",")) { - label_to_prcss = label_wo_prefix.substr(1); - } else { - label_to_prcss = std::move(label_wo_prefix); - } - } - else + if (auto base64IndPos = label_wo_prefix.find(CDoc2::LABELBASE64IND); + base64IndPos != std::string::npos) { std::string base64_label(label_wo_prefix.substr(base64IndPos + CDoc2::LABELBASE64IND.size())); - label_to_prcss = jwt::base::decode(base64_label); + decodedBase64 = jwt::base::decode(base64_label); + label_to_prcss = decodedBase64; + } else if (label_wo_prefix.starts_with(",")) { + label_to_prcss = label_wo_prefix.substr(1); + } else { + label_to_prcss = label_wo_prefix; } - auto label_parts(split(label_to_prcss, '&')); - for (auto& part : label_parts) + auto range_to_sv = [](auto range) constexpr { + return std::string_view(&*range.begin(), std::ranges::distance(range)); + }; + for (const auto &part : std::ranges::split_view(label_to_prcss, '&')) { - auto label_data_parts(split(part, '=')); - if (label_data_parts.size() != 2) - { - // Invalid label data. We just ignore them. + auto label_data_parts = std::ranges::split_view(part, '='); + if (label_data_parts.empty()) { LOG_ERROR("The label '{}' is invalid", label); + continue; } - else - { - std::string key = urlDecode(label_data_parts[0]); - std::string value = urlDecode(label_data_parts[1]); - std::transform(key.begin(), key.end(), key.begin(), [](unsigned char c){ return std::tolower(c); }); - parsed_label[key] = value; - } + auto it = label_data_parts.begin(); + std::string key = urlDecode(range_to_sv(*it)); + std::ranges::transform(key, key.begin(), [](unsigned char c){ return std::tolower(c); }); + ++it; + std::string value = urlDecode(range_to_sv(*it)); + parsed_label[std::move(key)] = std::move(value); } return parsed_label; diff --git a/cdoc/Lock.h b/cdoc/Lock.h index 07225e8d..2b2db333 100644 --- a/cdoc/Lock.h +++ b/cdoc/Lock.h @@ -19,7 +19,7 @@ #ifndef __LOCK_H__ #define __LOCK_H__ -#include +#include "CDoc.h" #include #include @@ -74,20 +74,6 @@ struct CDOC_EXPORT Lock SHARE_SERVER }; - /** - * @brief The public key type - */ - enum PKType : unsigned char { - /** - * Elliptic curve - */ - ECC, - /** - * RSA - */ - RSA - }; - /** * @brief Extra parameters depending on key type */ diff --git a/cdoc/Recipient.cpp b/cdoc/Recipient.cpp index 1e70fa78..7772e87f 100644 --- a/cdoc/Recipient.cpp +++ b/cdoc/Recipient.cpp @@ -21,6 +21,7 @@ #include "CDoc2.h" #include "Certificate.h" #include "Crypto.h" +#include "Lock.h" #include "Utils.h" #include @@ -33,10 +34,11 @@ namespace libcdoc { Recipient Recipient::makeSymmetric(std::string label, int32_t kdf_iter) { - Recipient rcpt(Type::SYMMETRIC_KEY); - rcpt.label = std::move(label); - rcpt.kdf_iter = kdf_iter; - return rcpt; + Recipient rcpt(Type::SYMMETRIC_KEY); + rcpt.label = std::move(label); + rcpt.lbl_parts[std::string(CDoc2::Label::TYPE)] = kdf_iter ? CDoc2::Label::TYPE_PASSWORD : CDoc2::Label::TYPE_SYMMETRIC; + rcpt.kdf_iter = kdf_iter; + return rcpt; } Recipient @@ -46,6 +48,7 @@ Recipient::makePublicKey(std::string label, std::vector public_key, PKT return {Type::NONE}; Recipient rcpt(Type::PUBLIC_KEY); rcpt.label = std::move(label); + rcpt.lbl_parts[std::string(CDoc2::Label::TYPE)] = CDoc2::Label::TYPE_PUBLIC_KEY; rcpt.pk_type = pk_type; if (pk_type == PKType::ECC && public_key[0] == 0x30) { // 0x30 identifies SEQUENCE tag in ASN.1 encoding @@ -54,7 +57,30 @@ Recipient::makePublicKey(std::string label, std::vector public_key, PKT } else { rcpt.rcpt_key = std::move(public_key); } - return rcpt; + return rcpt; +} + +Recipient +Recipient::makePublicKey(const Lock &lock) +{ + auto params = Lock::parseLabel(lock.label); + Recipient rcpt(Type::PUBLIC_KEY); + rcpt.pk_type = lock.pk_type; + rcpt.rcpt_key = lock.getBytes(Lock::RCPT_KEY); + if (rcpt.rcpt_key.empty()) + return {Type::NONE}; + if (lock.isCDoc1()) + rcpt.cert = lock.getBytes(Lock::CERT); + if (params.empty()) + rcpt.label = lock.label; + if (params.contains(CDoc2::Label::EXPIRY)) + { + const auto &val = params[CDoc2::Label::EXPIRY]; + if(std::from_chars(val.data(), val.data() + val.size(), rcpt.expiry_ts).ec == std::errc{}) + params.erase(CDoc2::Label::EXPIRY); + } + rcpt.lbl_parts = std::move(params); + return rcpt; } Recipient @@ -67,8 +93,23 @@ Recipient::makeCertificate(std::string label, std::vector cert) rcpt.label = std::move(label); rcpt.cert = std::move(cert); rcpt.rcpt_key = x509.getPublicKey(); - rcpt.pk_type = (x509.getAlgorithm() == libcdoc::Certificate::RSA) ? PKType::RSA : PKType::ECC; + rcpt.pk_type = x509.getAlgorithm(); rcpt.expiry_ts = x509.getNotAfter(); + if (auto eid = x509.getEIDType(); eid != Certificate::Unknown) { + rcpt.lbl_parts = { + {std::string(CDoc2::Label::TYPE), std::string(CDoc2::eid_strs[eid])}, + {std::string(CDoc2::Label::CN), x509.getCommonName()}, + {std::string(CDoc2::Label::SERIAL_NUMBER), x509.getSerialNumber()}, + {std::string(CDoc2::Label::LAST_NAME), x509.getSurname()}, + {std::string(CDoc2::Label::FIRST_NAME), x509.getGivenName()}, + }; + } else { + rcpt.lbl_parts = { + {std::string(CDoc2::Label::TYPE), std::string(CDoc2::Label::TYPE_CERTIFICATE)}, + {std::string(CDoc2::Label::CN), x509.getCommonName()}, + {std::string(CDoc2::Label::CERT_SHA1), toHex(x509.getDigest())}, + }; + } return rcpt; } @@ -94,6 +135,14 @@ Recipient::makeServer(std::string label, std::vector cert, std::string return rcpt; } +Recipient +Recipient::makeServer(const Lock &lock, std::string server_id) +{ + auto rcpt = makePublicKey(lock); + rcpt.server_id = std::move(server_id); + return rcpt; +} + Recipient Recipient::makeShare(std::string label, std::string server_id, std::string recipient_id) { @@ -107,105 +156,53 @@ Recipient::makeShare(std::string label, std::string server_id, std::string recip bool Recipient::isTheSameRecipient(const Recipient& other) const { - if (!isPKI()) return false; - if (!other.isPKI()) return false; + if (!isPKI()) return false; + if (!other.isPKI()) return false; return rcpt_key == other.rcpt_key; } bool Recipient::isTheSameRecipient(const std::vector& public_key) const { - if (!isPKI()) return false; + if (!isPKI()) return false; if (rcpt_key.empty() || public_key.empty()) return false; return rcpt_key == public_key; } -static void -buildLabel(std::ostream& ofs, std::string_view type, const std::map lbl_parts, std::initializer_list> extra) -{ - auto parts = lbl_parts; - if (parts.contains("v")) - parts.erase("v"); - if (parts.contains("type")) - parts.erase("type"); - for (const auto& [key, value] : extra) { - if (!value.empty()) - parts[key] = value; - } - ofs << CDoc2::LABELPREFIX; - ofs << CDoc2::Label::VERSION << '=' << std::to_string(CDoc2::KEYLABELVERSION) << '&' - << CDoc2::Label::TYPE << '=' << type; - for (const auto& [key, value] : parts) { - if (!value.empty()) - ofs << '&' << urlEncode(key) << '=' << urlEncode(value); - } -} - -static void -BuildLabelEID(std::ostream& ofs, Certificate::EIDType type, const Certificate& x509, const std::map& lbl_parts) -{ - - buildLabel(ofs, CDoc2::eid_strs[type], lbl_parts, { - {CDoc2::Label::CN, x509.getCommonName()}, - {CDoc2::Label::SERIAL_NUMBER, x509.getSerialNumber()}, - {CDoc2::Label::LAST_NAME, x509.getSurname()}, - {CDoc2::Label::FIRST_NAME, x509.getGivenName()}, - }); -} - -static void -BuildLabelCertificate(std::ostream &ofs, const Certificate& x509, const std::map& lbl_parts) -{ - buildLabel(ofs, CDoc2::Label::TYPE_CERTIFICATE, lbl_parts, { - {CDoc2::Label::CN, x509.getCommonName()}, - {CDoc2::Label::CERT_SHA1, toHex(x509.getDigest())} - }); -} - std::string -Recipient::getLabel(const std::vector> &extra) const +Recipient::getLabel(std::map extra) const { LOG_DBG("Generating label"); if (!label.empty()) return label; - std::map parts; - for (const auto& [key, value] : lbl_parts) { - if (!value.empty()) - parts[key] = value; - } - for (const auto& [key, value] : extra) { - if (!value.empty()) - parts[key] = value; - } std::ostringstream ofs; switch(type) { - case NONE: - LOG_DBG("The recipient is not initialized"); - break; - case SYMMETRIC_KEY: - if (kdf_iter > 0) { - buildLabel(ofs, CDoc2::Label::TYPE_PASSWORD, parts, {}); - } else { - buildLabel(ofs, CDoc2::Label::TYPE_SYMMETRIC, parts, {}); - } - break; - case PUBLIC_KEY: - if (!cert.empty()) { - Certificate x509(cert); - if (auto eid = x509.getEIDType(); eid != Certificate::Unknown) { - BuildLabelEID(ofs, eid, x509, parts); - } else { - BuildLabelCertificate(ofs, x509, parts); - } + case NONE: + LOG_DBG("The recipient is not initialized"); + break; + case SYMMETRIC_KEY: + case PUBLIC_KEY: + ofs << CDoc2::LABELPREFIX + << CDoc2::Label::VERSION << '=' << std::to_string(CDoc2::KEYLABELVERSION); + for (const auto& [key, value] : lbl_parts) { + if (key == "v") + continue; + if (auto it = extra.find(key); it != extra.end()) { + ofs << '&' << urlEncode(key) << '=' << urlEncode(it->second); + extra.erase(it); } else { - buildLabel(ofs, CDoc2::Label::TYPE_PUBLIC_KEY, parts, {}); + ofs << '&' << urlEncode(key) << '=' << urlEncode(value); } - break; - case KEYSHARE: - break; + } + for (const auto& [key, value] : extra) { + if (!value.empty()) + ofs << '&' << urlEncode(key) << '=' << urlEncode(value); + } + break; + case KEYSHARE: + break; } LOG_DBG("Generated label: {}", ofs.str()); return ofs.str(); } } // namespace libcdoc - diff --git a/cdoc/Recipient.h b/cdoc/Recipient.h index bc50d944..fb752862 100644 --- a/cdoc/Recipient.h +++ b/cdoc/Recipient.h @@ -19,7 +19,7 @@ #ifndef __RECIPIENT_H__ #define __RECIPIENT_H__ -#include +#include "CDoc.h" #include #include @@ -28,6 +28,8 @@ namespace libcdoc { +struct Lock; + /** * @brief A descriptor of encryption method and key to be used in container * @@ -56,20 +58,6 @@ struct CDOC_EXPORT Recipient { KEYSHARE }; - /** - * @brief The public key type - */ - enum PKType : uint8_t { - /** - * Elliptic curve - */ - ECC, - /** - * RSA - */ - RSA - }; - Recipient() = default; /** @@ -174,6 +162,12 @@ struct CDOC_EXPORT Recipient { * @return a new Recipient structure */ static Recipient makePublicKey(std::string label, std::vector public_key, PKType pk_type); + /** + * @brief Create a new public key based Recipient + * @param lock Lock to derive parameters from + * @return a new Recipient structure + */ + static Recipient makePublicKey(const Lock &lock); /** * @brief Create a new certificate based Recipient * @param label the label text @@ -205,6 +199,15 @@ struct CDOC_EXPORT Recipient { */ static Recipient makeServer(std::string label, std::vector cert, std::string server_id); + /** + * @brief Create a new capsule server based Recipient + * + * @param lock Lock to derive parameters from + * @param server_id the keyserver id + * @return a new Recipient structure + */ + static Recipient makeServer(const Lock &lock, std::string server_id); + /** * @brief Create new keyshare recipient * @@ -223,7 +226,7 @@ struct CDOC_EXPORT Recipient { * @param extra additional parameter values to use * @return a label value */ - std::string getLabel(const std::vector> &extra) const; + std::string getLabel(std::map extra) const; /** * @brief Set a property for automatic label generation @@ -232,11 +235,7 @@ struct CDOC_EXPORT Recipient { * @param value the property value */ void setLabelValue(std::string_view key, std::string_view value) { - if (!value.empty()) { - lbl_parts[std::string(key)] = value; - } else { - lbl_parts.erase(std::string(key)); - } + lbl_parts[std::string(key)] = value; } bool operator== (const Recipient& other) const = default; diff --git a/test/libcdoc_boost.cpp b/test/libcdoc_boost.cpp index 763768e5..5ce21e9e 100644 --- a/test/libcdoc_boost.cpp +++ b/test/libcdoc_boost.cpp @@ -790,6 +790,27 @@ BOOST_AUTO_TEST_CASE(Base64LabelParsingWithMediaType) } } } + +BOOST_AUTO_TEST_CASE(LabelParsingEmptyLabel) +{ + const string label("data:v=1&type=pw&label="); + + auto result = libcdoc::Lock::parseLabel(label); + for (const auto& [key, value] : { + pair {"v", "1"}, + pair {"type", "pw"}, + pair {"label", ""}, + }) + { + auto result_pair = result.find(key); + BOOST_TEST((result_pair != result.cend()), "Field " << key << " presented"); + if (result_pair != result.end()) + { + BOOST_CHECK_EQUAL(result_pair->second, value); + } + } +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(StreamingDecryption)