diff --git a/cdoc/CDoc2Writer.cpp b/cdoc/CDoc2Writer.cpp index 6934379f..f75c850c 100644 --- a/cdoc/CDoc2Writer.cpp +++ b/cdoc/CDoc2Writer.cpp @@ -105,7 +105,7 @@ createRSACapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::Recipie return cdoc20::header::CreateRecipientRecord(builder, cdoc20::header::Capsule::recipients_RSAPublicKeyCapsule, capsule.Union(), - builder.CreateString(rcpt.getLabel({})), + builder.CreateString(rcpt.getLabel({{"x-expiry-time", rcpt.expiry_ts == 0 ? std::string() : std::to_string(rcpt.expiry_ts)}})), 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({})), + builder.CreateString(rcpt.getLabel({{"x-expiry-time", rcpt.expiry_ts == 0 ? std::string() : std::to_string(rcpt.expiry_ts)}})), builder.CreateVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); } diff --git a/cdoc/Certificate.cpp b/cdoc/Certificate.cpp index 3574ef4c..03e66360 100644 --- a/cdoc/Certificate.cpp +++ b/cdoc/Certificate.cpp @@ -74,6 +74,21 @@ Certificate::getSerialNumber() const return getName(cert, NID_serialNumber); } +time_t +Certificate::getNotAfter() const +{ + if(!cert) + return 0; + tm tm{}; + if(ASN1_TIME_to_tm(X509_get0_notAfter(cert.get()), &tm) != 1) + return 0; +#ifdef _WIN32 + return _mkgmtime(&tm); +#else + return timegm(&tm); +#endif +} + std::vector @@ -85,8 +100,8 @@ Certificate::policies() const if(!cert) return list; - auto p = static_cast(X509_get_ext_d2i(cert.get(), NID_certificate_policies, nullptr, nullptr)); - auto cp = std::unique_ptr(p,CERTIFICATEPOLICIES_free); + auto cp = make_unique_cast(X509_get_ext_d2i( + cert.get(), NID_certificate_policies, nullptr, nullptr)); if(!cp) return list; @@ -122,7 +137,7 @@ Certificate::getAlgorithm() const return (alg == EVP_PKEY_RSA) ? Algorithm::RSA : Algorithm::ECC; } -std::vector Certificate::getDigest() +std::vector Certificate::getDigest() const { if(!cert) return {}; diff --git a/cdoc/Certificate.h b/cdoc/Certificate.h index 61182490..830f7e26 100644 --- a/cdoc/Certificate.h +++ b/cdoc/Certificate.h @@ -50,8 +50,9 @@ class Certificate { std::vector getPublicKey() const; Algorithm getAlgorithm() const; + time_t getNotAfter() const; - std::vector getDigest(); + std::vector getDigest() const; }; } // Namespace diff --git a/cdoc/Io.h b/cdoc/Io.h index 89b9eece..a1c76de5 100644 --- a/cdoc/Io.h +++ b/cdoc/Io.h @@ -26,7 +26,7 @@ namespace libcdoc { -class DataSource; +struct DataSource; /** * @brief The DataConsumer class diff --git a/cdoc/Recipient.cpp b/cdoc/Recipient.cpp index a7380279..fe604f5e 100644 --- a/cdoc/Recipient.cpp +++ b/cdoc/Recipient.cpp @@ -49,20 +49,20 @@ static constexpr std::string_view eid_strs[] = { }; Recipient -Recipient::makeSymmetric(const std::string& label, int32_t kdf_iter) +Recipient::makeSymmetric(std::string label, int32_t kdf_iter) { Recipient rcpt(Type::SYMMETRIC_KEY); - rcpt.label = label; + rcpt.label = std::move(label); rcpt.kdf_iter = kdf_iter; return rcpt; } Recipient -Recipient::makePublicKey(const std::string& label, const std::vector& public_key, PKType pk_type) +Recipient::makePublicKey(std::string label, const std::vector& public_key, PKType pk_type) { if (public_key.empty()) return Recipient(Type::NONE); Recipient rcpt(Type::PUBLIC_KEY); - rcpt.label = label; + rcpt.label = std::move(label); rcpt.pk_type = pk_type; if (pk_type == PKType::ECC && public_key[0] == 0x30) { // 0x30 identifies SEQUENCE tag in ASN.1 encoding @@ -77,13 +77,14 @@ Recipient::makePublicKey(const std::string& label, const std::vector& p Recipient Recipient::makeCertificate(std::string label, std::vector cert) { - Recipient rcpt(Type::PUBLIC_KEY); - rcpt.label = std::move(label); + Recipient rcpt(Type::PUBLIC_KEY); + rcpt.label = std::move(label); rcpt.cert = std::move(cert); - Certificate ssl(rcpt.cert); - rcpt.rcpt_key = ssl.getPublicKey(); - rcpt.pk_type = (ssl.getAlgorithm() == libcdoc::Certificate::RSA) ? PKType::RSA : PKType::ECC; - return rcpt; + Certificate x509(rcpt.cert); + rcpt.rcpt_key = x509.getPublicKey(); + rcpt.pk_type = (x509.getAlgorithm() == libcdoc::Certificate::RSA) ? PKType::RSA : PKType::ECC; + rcpt.expiry_ts = x509.getNotAfter(); + return rcpt; } Recipient @@ -138,26 +139,10 @@ Recipient::isTheSameRecipient(const std::vector& public_key) const return rcpt_key == public_key; } -static std::string -buildLabel(std::vector> components) -{ - std::ostringstream ofs; - ofs << LABELPREFIX; - bool first = true; - for (auto& [key, value] : components) { - if (!value.empty()) { - if (!first) ofs << '&'; - ofs << libcdoc::urlEncode(key) << '=' << libcdoc::urlEncode(value); - first = false; - } - } - return ofs.str(); -} - static Recipient::EIDType getEIDType(const std::vector& policies) { - for (std::vector::const_reference policy : policies) + for (const auto& policy : policies) { if (policy.starts_with("1.3.6.1.4.1.51361.1.1.3") || policy.starts_with("1.3.6.1.4.1.51361.1.2.3")) { @@ -181,113 +166,103 @@ getEIDType(const std::vector& policies) return Recipient::EIDType::Unknown; } -static std::string -BuildLabelEID(const std::vector& cert) +static void +buildLabel(std::ostream& ofs, std::string_view type, const std::initializer_list> &components) { - Certificate x509(cert); - Recipient::EIDType type = getEIDType(x509.policies()); - std::string cn = x509.getCommonName(); - std::string sn = x509.getSerialNumber(); - std::string gn = x509.getGivenName(); - if (!gn.empty()) { - return buildLabel({ - {"v", std::to_string(CDoc2::KEYLABELVERSION)}, - {"type", eid_strs[type]}, - {"cn", cn}, - {"serial_number", sn} - }); - } else { - return buildLabel({ - {"v", std::to_string(CDoc2::KEYLABELVERSION)}, - {"type", eid_strs[type]}, - {"cn", cn}, - {"serial_number", sn}, - {"last_name", x509.getSurname()}, - {"first_name", gn} - }); + ofs << LABELPREFIX; + ofs << "v" << '=' << std::to_string(CDoc2::KEYLABELVERSION) << '&' + << "type" << '=' << type; + for (auto& [key, value] : components) { + if (value.empty()) + continue; + ofs << '&'; + ofs << urlEncode(key) << '=' << urlEncode(value); } } -static std::string -BuildLabelCertificate(std::string_view file, const std::vector& cert) +static void +BuildLabelEID(std::ostream& ofs, Recipient::EIDType type, const Certificate& x509) { - Certificate x509(cert); - return buildLabel({ - {"v", std::to_string(CDoc2::KEYLABELVERSION)}, - {"type", "cert"}, + buildLabel(ofs, eid_strs[type], { + {"cn", x509.getCommonName()}, + {"serial_number", x509.getSerialNumber()}, + {"last_name", x509.getSurname()}, + {"first_name", x509.getGivenName()}, + }); +} + +static void +BuildLabelCertificate(std::ostream &ofs, std::string_view file, const Certificate& x509) +{ + buildLabel(ofs, "cert", { {"file", file}, {"cn", x509.getCommonName()}, {"cert_sha1", toHex(x509.getDigest())} }); } -static std::string -BuildLabelPublicKey(int version, const std::string file) +static void +BuildLabelPublicKey(std::ostream &ofs, const std::string file) { - return buildLabel({ - {"v", std::to_string(version)}, - {"type", "pub_key"}, + buildLabel(ofs, "pub_key", { {"file", file} }); } -static std::string -BuildLabelSymmetricKey(int version, const std::string& label, const std::string file) +static void +BuildLabelSymmetricKey(std::ostream &ofs, const std::string& label, const std::string file) { - return buildLabel({ - {"v", std::to_string(version)}, - {"type", "secret"}, + buildLabel(ofs, "secret", { {"label", label}, {"file", file} }); } -static std::string -BuildLabelPassword(int version, const std::string& label) +static void +BuildLabelPassword(std::ostream &ofs, const std::string& label) { - return buildLabel({ - {"v", std::to_string(version)}, - {"type", "pw"}, + buildLabel(ofs, "pw", { {"label", label} }); } std::string -Recipient::getLabel(std::vector> extra) const +Recipient::getLabel(const std::vector> &extra) const { LOG_DBG("Generating label"); if (!label.empty()) return label; std::ostringstream ofs; switch(type) { - case NONE: + case NONE: LOG_DBG("The recipient is not initialized"); break; case SYMMETRIC_KEY: if (kdf_iter > 0) { - ofs << BuildLabelPassword(CDoc2::KEYLABELVERSION, key_name); + BuildLabelPassword(ofs, key_name); } else { - ofs << BuildLabelSymmetricKey(CDoc2::KEYLABELVERSION, key_name, file_name); + BuildLabelSymmetricKey(ofs, key_name, file_name); } + break; case PUBLIC_KEY: if (!cert.empty()) { Certificate x509(cert); - EIDType eid_type = getEIDType(x509.policies()); - if (eid_type != EIDType::Unknown) { - ofs << BuildLabelEID(cert); + if (auto type = getEIDType(x509.policies()); type != EIDType::Unknown) { + BuildLabelEID(ofs, type, x509); } else { - ofs << BuildLabelCertificate(file_name, cert); + BuildLabelCertificate(ofs, file_name, x509); } } else { - ofs << BuildLabelPublicKey(CDoc2::KEYLABELVERSION, file_name); + BuildLabelPublicKey(ofs, file_name); } + break; case KEYSHARE: break; } for (auto& [key, value] : extra) { - if (!value.empty()) { - ofs << '&'; - ofs << libcdoc::urlEncode(key) << '=' << libcdoc::urlEncode(value); - } + if (value.empty()) + continue; + ofs << '&'; + ofs << urlEncode(key) << '=' << urlEncode(value); } LOG_DBG("Generated label: {}", ofs.str()); return ofs.str(); diff --git a/cdoc/Recipient.h b/cdoc/Recipient.h index 284927e8..9acbd875 100644 --- a/cdoc/Recipient.h +++ b/cdoc/Recipient.h @@ -80,20 +80,6 @@ struct CDOC_EXPORT Recipient { DigiID_EResident }; - /** - * @brief Extra parameters for automatic label generation - */ - enum Params : unsigned char { - /** - * @brief Name of symmetric key/password ('label') - */ - LABEL, - /** - * @brief Public key or certificate filename ('file') - */ - FILE - }; - Recipient() = default; /** @@ -199,7 +185,7 @@ struct CDOC_EXPORT Recipient { * @param kdf_iter the number of PBKDF iterations (0 if full key is provided) * @return a new Recipient structure */ - static Recipient makeSymmetric(const std::string& label, int32_t kdf_iter); + static Recipient makeSymmetric(std::string label, int32_t kdf_iter); /** * @brief Create a new public key based Recipient * @param label the label text @@ -207,7 +193,7 @@ struct CDOC_EXPORT Recipient { * @param pk_type the algorithm type (either ECC or RSA) * @return a new Recipient structure */ - static Recipient makePublicKey(const std::string& label, const std::vector& public_key, PKType pk_type); + static Recipient makePublicKey(std::string label, const std::vector& public_key, PKType pk_type); /** * @brief Create a new certificate based Recipient * @param label the label text @@ -257,7 +243,7 @@ struct CDOC_EXPORT Recipient { * @param extra additional parameter values to use * @return a label value */ - std::string getLabel(std::vector> extra) const; + std::string getLabel(const std::vector> &extra) const; /** * @brief parse machine-readable CDoc2 label diff --git a/cdoc/Utils.cpp b/cdoc/Utils.cpp index 9ad09c9a..081c6ed4 100644 --- a/cdoc/Utils.cpp +++ b/cdoc/Utils.cpp @@ -118,14 +118,13 @@ buildURL(const std::string& host, int port) return std::string("https://") + host + ":" + std::to_string(port) + "/"; } -std::string -urlEncode(std::string_view src) +std::ostream& +operator<<(std::ostream& escaped, urlEncode src) { - std::ostringstream escaped; escaped.fill('0'); escaped << std::hex; - for (auto c : src) { + for (auto c : src.src) { // Keep alphanumeric and other accepted characters intact if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { escaped << c; @@ -136,7 +135,7 @@ urlEncode(std::string_view src) escaped << '%' << std::setw(2) << int((unsigned char) c); escaped << std::nouppercase; } - return escaped.str(); + return escaped; } std::string diff --git a/cdoc/Utils.h b/cdoc/Utils.h index db126a90..112c2161 100644 --- a/cdoc/Utils.h +++ b/cdoc/Utils.h @@ -118,7 +118,11 @@ readAllBytes(std::string_view filename) int parseURL(const std::string& url, std::string& host, int& port, std::string& path, bool end_with_slash = false); std::string buildURL(const std::string& host, int port); -std::string urlEncode(std::string_view src); +struct urlEncode { + std::string_view src; + friend std::ostream& operator<<(std::ostream& escaped, urlEncode src); +}; + std::string urlDecode(const std::string &src); } // namespace libcdoc