diff --git a/cdoc/CDoc2Writer.cpp b/cdoc/CDoc2Writer.cpp index fdae2055..6934379f 100644 --- a/cdoc/CDoc2Writer.cpp +++ b/cdoc/CDoc2Writer.cpp @@ -105,13 +105,13 @@ createRSACapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::Recipie return cdoc20::header::CreateRecipientRecord(builder, cdoc20::header::Capsule::recipients_RSAPublicKeyCapsule, capsule.Union(), - builder.CreateString(rcpt.label), + builder.CreateString(rcpt.getLabel({})), builder.CreateVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); } static flatbuffers::Offset -createRSAServerCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::Recipient& rcpt, const std::string& transaction_id, const std::vector& xor_key) +createRSAServerCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::Recipient& rcpt, const std::string& transaction_id, uint64_t expiry_time, const std::vector& xor_key) { auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, builder.CreateVector(rcpt.rcpt_key)); @@ -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.label), + builder.CreateString(rcpt.getLabel({{"x-expiry-time", std::to_string(expiry_time)}})), builder.CreateVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); } @@ -138,13 +138,13 @@ createECCCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::Recipie return cdoc20::header::CreateRecipientRecord(builder, cdoc20::header::Capsule::recipients_ECCPublicKeyCapsule, capsule.Union(), - builder.CreateString(rcpt.label), + builder.CreateString(rcpt.getLabel({})), builder.CreateVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); } static flatbuffers::Offset -createECCServerCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::Recipient& rcpt, const std::string& transaction_id, const std::vector& xor_key) +createECCServerCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::Recipient& rcpt, const std::string& transaction_id, uint64_t expiry_time, const std::vector& xor_key) { auto eccKeyServer = cdoc20::recipients::CreateEccKeyDetails(builder, cdoc20::recipients::EllipticCurve::secp384r1, @@ -157,7 +157,7 @@ createECCServerCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::R return cdoc20::header::CreateRecipientRecord(builder, cdoc20::header::Capsule::recipients_KeyServerCapsule, capsule.Union(), - builder.CreateString(rcpt.label), + builder.CreateString(rcpt.getLabel({{"x-expiry-time", std::to_string(expiry_time)}})), builder.CreateVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); } @@ -170,7 +170,7 @@ createSymmetricKeyCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc return cdoc20::header::CreateRecipientRecord(builder, cdoc20::header::Capsule::recipients_SymmetricKeyCapsule, capsule.Union(), - builder.CreateString(rcpt.label), + builder.CreateString(rcpt.getLabel({})), builder.CreateVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); } @@ -186,7 +186,7 @@ createPasswordCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::Re return cdoc20::header::CreateRecipientRecord(builder, cdoc20::header::Capsule::recipients_PBKDF2Capsule, capsule.Union(), - builder.CreateString(rcpt.label), + builder.CreateString(rcpt.getLabel({})), builder.CreateVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); } @@ -272,7 +272,7 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vectorsendKey(cinfo, send_url, rcpt.rcpt_key, key_material, "RSA"); + int result = network->sendKey(cinfo, send_url, rcpt.rcpt_key, key_material, "RSA", rcpt.expiry_ts); if (result < 0) { setLastError(network->getLastErrorStr(result)); LOG_ERROR("{}", last_error); @@ -282,7 +282,7 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector& header, const std::vectorsendKey(cinfo, send_url, rcpt.rcpt_key, key_material, "ecc_secp384r1"); + int result = network->sendKey(cinfo, send_url, rcpt.rcpt_key, key_material, "ecc_secp384r1", rcpt.expiry_ts); if (result < 0) { setLastError(network->getLastErrorStr(result)); LOG_ERROR("{}", last_error); @@ -317,7 +317,7 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector& header, const std::vector kek_pm(libcdoc::CDoc2::KEY_LEN); std::vector salt; int64_t result = crypto->random(salt, libcdoc::CDoc2::KEY_LEN); diff --git a/cdoc/CDocCipher.cpp b/cdoc/CDocCipher.cpp index 04df61a3..7d0fe9c7 100644 --- a/cdoc/CDocCipher.cpp +++ b/cdoc/CDocCipher.cpp @@ -268,64 +268,17 @@ fill_recipients_from_rcpt_info(ToolConf& conf, ToolCrypto& crypto, std::vector cert_bytes; - ToolPKCS11* p11 = dynamic_cast(crypto.p11.get()); - int result = p11->getCertificate(cert_bytes, isRsa, rcpt.slot, rcpt.secret, rcpt.key_id, rcpt.key_label); - if (result != libcdoc::OK) - { - LOG_ERROR("Certificate reading from SC card failed. Key label: {}", rcpt.key_label); - return 1; - } - LOG_DBG("Got certificate from P11 module"); - label = Recipient::BuildLabelEID(cert_bytes); - break; - } - - case RcptInfo::Type::CERT: - { - label = Recipient::BuildLabelCertificate(rcpt.key_file_name, rcpt.cert); - break; - } case RcptInfo::Type::P11_SYMMETRIC: - // TODO: what label should be generated in this case? - break; - - default: - LOG_ERROR("Unhandled recipient type {} for generating the lock's label", static_cast(rcpt.type)); - break; - } -#ifndef NDEBUG - LOG_DBG("Generated label: {}", label); -#endif - } else { - label = rcpt.label; - } - - if (label.empty()) { - LOG_ERROR("No lock label"); - return 1; - } + if (!conf.gen_label) label = rcpt.label; crypto_rcpts[idx++] = rcpt; Recipient key; if (rcpt.type == RcptInfo::Type::CERT) { - key = libcdoc::Recipient::makeCertificate(label, rcpt.cert); + if (!conf.servers.empty()) { + key = libcdoc::Recipient::makeServer(label, rcpt.cert, conf.servers[0].ID); + } else { + key = libcdoc::Recipient::makeCertificate(label, rcpt.cert); + } } else if (rcpt.type == RcptInfo::Type::SKEY) { key = libcdoc::Recipient::makeSymmetric(label, 0); LOG_DBG("Creating symmetric key:"); @@ -766,7 +719,7 @@ void CDocCipher::Locks(const char* file) const } // Output the fields with their values - cout << lock_id << ":" << endl; + cout << lock_id << ":" << lock.label << endl; for (map::const_reference pair : parsed_label) { cout << " " << setw(maxFieldLength + 1) << left << pair.first << ": " << pair.second << endl; } diff --git a/cdoc/ILogger.h b/cdoc/ILogger.h index ce2f89de..3bf1c72c 100644 --- a/cdoc/ILogger.h +++ b/cdoc/ILogger.h @@ -134,6 +134,8 @@ class CDOC_EXPORT ILogger */ static ILogger* getLogger(); + static void setLogger(ILogger *logger); + protected: /** * @brief Minimum level of log messages to log. diff --git a/cdoc/LogEngine.cpp b/cdoc/LogEngine.cpp index 2b565ecf..e6bc69cc 100644 --- a/cdoc/LogEngine.cpp +++ b/cdoc/LogEngine.cpp @@ -57,6 +57,15 @@ struct LogEngine final : public ILogger return tmp; } + void setLogger(ILogger *logger) { + lock_guard guard(loggers_protector); + while (!loggers.empty()) { + delete loggers.begin()->second; + loggers.erase(loggers.begin()->first); + } + loggers[0] = logger; + } + private: // Current Cookie value int currentLoggerCookie = 0; @@ -91,4 +100,11 @@ ILogger::getLogger() return &defaultLogEngine; } +void +ILogger::setLogger(ILogger *logger) +{ + defaultLogEngine.setLogger(logger); +} + + } diff --git a/cdoc/NetworkBackend.cpp b/cdoc/NetworkBackend.cpp index 202a8f45..c5723130 100644 --- a/cdoc/NetworkBackend.cpp +++ b/cdoc/NetworkBackend.cpp @@ -224,11 +224,11 @@ setProxy(httplib::SSLClient& cli, libcdoc::NetworkBackend *network) // Post request and fetch response // static libcdoc::result_t -post(httplib::SSLClient& cli, const std::string& path, const std::string& req, httplib::Response& rsp) +post(httplib::SSLClient& cli, const std::string& path, httplib::Headers& hdrs, const std::string& req, httplib::Response& rsp) { // Capture TLS and HTTP errors libcdoc::LOG_DBG("POST: {} {}", path, req); - httplib::Result res = cli.Post(path, req, "application/json"); + httplib::Result res = cli.Post(path, hdrs, req, "application/json"); if (!res) { error = FORMAT("Cannot connect to https://{}:{}{}", cli.host(), cli.port(), path); return libcdoc::NetworkBackend::NETWORK_ERROR; @@ -267,8 +267,9 @@ get(httplib::SSLClient& cli, httplib::Headers& hdrs, const std::string& path, pi } libcdoc::result_t -libcdoc::NetworkBackend::sendKey (CapsuleInfo& dst, const std::string& url, const std::vector& rcpt_key, const std::vector &key_material, const std::string& type) +libcdoc::NetworkBackend::sendKey (CapsuleInfo& dst, const std::string& url, const std::vector& rcpt_key, const std::vector &key_material, const std::string& type, uint64_t expiry_ts) { + LOG_DBG("Sendkey"); picojson::object obj = { {"recipient_id", picojson::value(libcdoc::toBase64(rcpt_key))}, {"ephemeral_key_material", picojson::value(libcdoc::toBase64(key_material))}, @@ -288,8 +289,14 @@ libcdoc::NetworkBackend::sendKey (CapsuleInfo& dst, const std::string& url, cons if (result = setProxy(cli, this); result != OK) return result; std::string full = path + "/key-capsules"; + httplib::Headers hdrs; + if (expiry_ts) { + std::string expiry_str = timeToISO(expiry_ts); + LOG_DBG("Expiry time: {}", expiry_str); + hdrs.emplace(std::make_pair("x-expiry-time", expiry_str)); + } httplib::Response rsp; - result = post(cli, full, req_str, rsp); + result = post(cli, full, hdrs, req_str, rsp); if (result != libcdoc::OK) return result; std::string location = rsp.get_header_value("Location"); @@ -298,28 +305,13 @@ libcdoc::NetworkBackend::sendKey (CapsuleInfo& dst, const std::string& url, cons return NETWORK_ERROR; } error = {}; - /* Remove /key-capsules/ */ dst.transaction_id = location.substr(14); - // Calculate expiry time - auto now = std::chrono::system_clock::now(); - // Get a days-precision chrono::time_point - auto sd = floor(now); - // Record the time of day - auto time_of_day = now - sd; - // Convert to a y/m/d calendar data structure - std::chrono::year_month_day ymd = sd; - // Add the months - ymd += std::chrono::months{6}; - // Add some policy for overflowing the day-of-month if desired - if (!ymd.ok()) - ymd = ymd.year()/ymd.month()/std::chrono::last; - // Convert back to system_clock::time_point - std::chrono::system_clock::time_point later = std::chrono::sys_days{ymd} + time_of_day; - auto ttt = std::chrono::system_clock::to_time_t(later); - - dst.expiry_time = ttt; + std::string expiry_str = rsp.get_header_value("x-expiry-time"); + LOG_DBG("Server expiry: {}", expiry_str); + dst.expiry_time = uint64_t(timeFromISO(expiry_str)); + LOG_DBG("Server expiry timestamp: {}", dst.expiry_time); return OK; } @@ -348,8 +340,9 @@ libcdoc::NetworkBackend::sendShare(std::vector& dst, const std::string& if (result = setProxy(cli, this); result != OK) return result; std::string full = path + "/key-shares"; + httplib::Headers hdrs; httplib::Response rsp; - result = post(cli, full, req_str, rsp); + result = post(cli, full, hdrs, req_str, rsp); if (result != libcdoc::OK) return result; std::string location = rsp.get_header_value("Location"); @@ -421,8 +414,9 @@ libcdoc::NetworkBackend::fetchNonce(std::vector& dst, const std::string if (result = setProxy(cli, this); result != OK) return result; std::string full = path + "/key-shares/" + share_id + "/nonce"; + httplib::Headers hdrs; httplib::Response rsp; - result = post(cli, full, "", rsp); + result = post(cli, full, hdrs, "", rsp); if (result != libcdoc::OK) return result; LOG_DBG("Response: {}", rsp.body); @@ -710,8 +704,9 @@ libcdoc::NetworkBackend::signSID(std::vector& dst, std::vector // std::string full = path + "/certificatechoice/" + rcpt_id; LOG_DBG("SmartID path: {}", full); + httplib::Headers hdrs; httplib::Response rsp; - result = post(cli, full, query.serialize(), rsp); + result = post(cli, full, hdrs, query.serialize(), rsp); if (result != libcdoc::OK) return result; @@ -773,7 +768,7 @@ libcdoc::NetworkBackend::signSID(std::vector& dst, std::vector // full = path + "/authentication/" + rcpt_id; LOG_DBG("SmartID path: {}", full); - result = post(cli, full, query.serialize(), rsp); + result = post(cli, full, hdrs, query.serialize(), rsp); if (result != libcdoc::OK) return result; LOG_DBG("Response: {}", rsp.body); picojson::parse(v, rsp.body); @@ -857,8 +852,9 @@ libcdoc::NetworkBackend::signMID(std::vector& dst, std::vector // std::string full = path + "/authentication"; LOG_DBG("Mobile ID path: {}", full); + httplib::Headers hdrs; httplib::Response rsp; - result = post(cli, full, query.serialize(), rsp); + result = post(cli, full, hdrs, query.serialize(), rsp); if (result != libcdoc::OK) return result; LOG_DBG("Response: {}", rsp.body); diff --git a/cdoc/NetworkBackend.h b/cdoc/NetworkBackend.h index d2122bef..6c082240 100644 --- a/cdoc/NetworkBackend.h +++ b/cdoc/NetworkBackend.h @@ -142,9 +142,10 @@ struct CDOC_EXPORT NetworkBackend { * @param rcpt_key recipient's public key * @param key_material encrypted KEK or ECDH public Key used to derive shared secret * @param type algorithm type, currently either "rsa" or "ecc_secp384r1" + * @param expiry_ts the requested capsule expiry timestamp, 0 - use server default * @return error code or OK */ - virtual result_t sendKey (CapsuleInfo& dst, const std::string& url, const std::vector& rcpt_key, const std::vector &key_material, const std::string& type); + virtual result_t sendKey (CapsuleInfo& dst, const std::string& url, const std::vector& rcpt_key, const std::vector &key_material, const std::string& type, uint64_t expiry_ts); /** * @brief send key share to server * diff --git a/cdoc/Recipient.cpp b/cdoc/Recipient.cpp index 58fb9b38..a7380279 100644 --- a/cdoc/Recipient.cpp +++ b/cdoc/Recipient.cpp @@ -38,6 +38,16 @@ constexpr string_view LABELPREFIX{"data:"}; */ constexpr string_view LABELBASE64IND{";base64,"}; +/** + * @brief EID type values for machine-readable label + */ +static constexpr std::string_view eid_strs[] = { + "Unknown", + "ID-card", + "Digi-ID", + "Digi-ID E-RESIDENT" +}; + Recipient Recipient::makeSymmetric(const std::string& label, int32_t kdf_iter) { @@ -67,7 +77,7 @@ Recipient::makePublicKey(const std::string& label, const std::vector& p Recipient Recipient::makeCertificate(std::string label, std::vector cert) { - Recipient rcpt(Type::CERTIFICATE); + Recipient rcpt(Type::PUBLIC_KEY); rcpt.label = std::move(label); rcpt.cert = std::move(cert); Certificate ssl(rcpt.cert); @@ -76,17 +86,10 @@ Recipient::makeCertificate(std::string label, std::vector cert) return rcpt; } -Recipient -Recipient::makeEID(std::vector cert) -{ - auto label = BuildLabelEID(cert); - return makeCertificate(std::move(label), std::move(cert)); -} - Recipient Recipient::makeServer(std::string label, std::vector public_key, PKType pk_type, std::string server_id) { - Recipient rcpt(Type::SERVER); + Recipient rcpt(Type::PUBLIC_KEY); rcpt.label = std::move(label); rcpt.pk_type = pk_type; if (pk_type == PKType::ECC && public_key[0] == 0x30) { @@ -101,12 +104,12 @@ Recipient::makeServer(std::string label, std::vector public_key, PKType } Recipient -Recipient::makeEIDServer(std::vector cert, std::string server_id) +Recipient::makeServer(std::string label, std::vector cert, std::string server_id) { Certificate x509(cert); - auto label = BuildLabelEID(cert); - return makeServer(std::move(label), - x509.getPublicKey(), x509.getAlgorithm() == Certificate::Algorithm::RSA ? RSA : ECC, std::move(server_id)); + Recipient rcpt = makeServer(std::move(label), x509.getPublicKey(), x509.getAlgorithm() == Certificate::Algorithm::RSA ? RSA : ECC, std::move(server_id)); + rcpt.cert = cert; + return std::move(rcpt); } Recipient @@ -135,15 +138,8 @@ Recipient::isTheSameRecipient(const std::vector& public_key) const return rcpt_key == public_key; } -static constexpr std::string_view type_strs[] = { - "Unknown", - "ID-card", - "Digi-ID", - "Digi-ID E-RESIDENT" -}; - -std::string -Recipient::buildLabel(std::vector> components) +static std::string +buildLabel(std::vector> components) { std::ostringstream ofs; ofs << LABELPREFIX; @@ -158,57 +154,75 @@ Recipient::buildLabel(std::vector> return ofs.str(); } -std::string -Recipient::BuildLabelEID(int version, EIDType type, std::string_view cn, std::string_view serial_number, std::string_view last_name, std::string_view first_name) +static Recipient::EIDType +getEIDType(const std::vector& policies) { - // In case of cards issued to an organization the first name (and last name) are missing. We ommit these parts. - if (first_name.empty()) { + for (std::vector::const_reference 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")) { + return Recipient::EIDType::DigiID; + } + + if (policy.starts_with("1.3.6.1.4.1.51361.1.1.4") || + policy.starts_with("1.3.6.1.4.1.51361.1.2.4")) { + return Recipient::EIDType::DigiID_EResident; + } + + if (policy.starts_with("1.3.6.1.4.1.51361.1.1") || + policy.starts_with("1.3.6.1.4.1.51455.1.1") || + policy.starts_with("1.3.6.1.4.1.51361.1.2") || + policy.starts_with("1.3.6.1.4.1.51455.1.2")) { + return Recipient::EIDType::IDCard; + } + } + + // If the execution reaches so far then EID type determination failed. + return Recipient::EIDType::Unknown; +} + +static std::string +BuildLabelEID(const std::vector& cert) +{ + 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(version)}, - {"type", type_strs[type]}, + {"v", std::to_string(CDoc2::KEYLABELVERSION)}, + {"type", eid_strs[type]}, {"cn", cn}, - {"serial_number", serial_number} + {"serial_number", sn} }); } else { return buildLabel({ - {"v", std::to_string(version)}, - {"type", type_strs[type]}, + {"v", std::to_string(CDoc2::KEYLABELVERSION)}, + {"type", eid_strs[type]}, {"cn", cn}, - {"serial_number", serial_number}, - {"last_name", last_name}, - {"first_name", first_name} + {"serial_number", sn}, + {"last_name", x509.getSurname()}, + {"first_name", gn} }); } } -std::string -Recipient::BuildLabelEID(const std::vector& cert) +static std::string +BuildLabelCertificate(std::string_view file, const std::vector& cert) { Certificate x509(cert); - return BuildLabelEID(CDoc2::KEYLABELVERSION, getEIDType(x509.policies()), x509.getCommonName(), x509.getSerialNumber(), x509.getSurname(), x509.getGivenName()); -} - -std::string -Recipient::BuildLabelCertificate(int version, std::string_view file, std::string_view cn, const std::vector& cert_sha1) -{ return buildLabel({ - {"v", std::to_string(version)}, + {"v", std::to_string(CDoc2::KEYLABELVERSION)}, {"type", "cert"}, {"file", file}, - {"cn", cn}, - {"cert_sha1", toHex(cert_sha1)} + {"cn", x509.getCommonName()}, + {"cert_sha1", toHex(x509.getDigest())} }); } -std::string -Recipient::BuildLabelCertificate(std::string_view file, const std::vector& cert) -{ - Certificate x509(cert); - return BuildLabelCertificate(CDoc2::KEYLABELVERSION, file, x509.getCommonName(), x509.getDigest()); -} - -std::string -Recipient::BuildLabelPublicKey(int version, const std::string file) +static std::string +BuildLabelPublicKey(int version, const std::string file) { return buildLabel({ {"v", std::to_string(version)}, @@ -217,8 +231,8 @@ Recipient::BuildLabelPublicKey(int version, const std::string file) }); } -std::string -Recipient::BuildLabelSymmetricKey(int version, const std::string& label, const std::string file) +static std::string +BuildLabelSymmetricKey(int version, const std::string& label, const std::string file) { return buildLabel({ {"v", std::to_string(version)}, @@ -228,8 +242,8 @@ Recipient::BuildLabelSymmetricKey(int version, const std::string& label, const s }); } -std::string -Recipient::BuildLabelPassword(int version, const std::string& label) +static std::string +BuildLabelPassword(int version, const std::string& label) { return buildLabel({ {"v", std::to_string(version)}, @@ -238,30 +252,45 @@ Recipient::BuildLabelPassword(int version, const std::string& label) }); } -Recipient::EIDType Recipient::getEIDType(const std::vector& policies) +std::string +Recipient::getLabel(std::vector> extra) const { - for (std::vector::const_reference 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")) { - return Recipient::EIDType::DigiID; - } - - if (policy.starts_with("1.3.6.1.4.1.51361.1.1.4") || - policy.starts_with("1.3.6.1.4.1.51361.1.2.4")) { - return Recipient::EIDType::DigiID_EResident; - } - - if (policy.starts_with("1.3.6.1.4.1.51361.1.1") || - policy.starts_with("1.3.6.1.4.1.51455.1.1") || - policy.starts_with("1.3.6.1.4.1.51361.1.2") || - policy.starts_with("1.3.6.1.4.1.51455.1.2")) { - return Recipient::EIDType::IDCard; + LOG_DBG("Generating label"); + if (!label.empty()) return label; + std::ostringstream ofs; + switch(type) { + case NONE: + LOG_DBG("The recipient is not initialized"); + break; + case SYMMETRIC_KEY: + if (kdf_iter > 0) { + ofs << BuildLabelPassword(CDoc2::KEYLABELVERSION, key_name); + } else { + ofs << BuildLabelSymmetricKey(CDoc2::KEYLABELVERSION, key_name, file_name); + } + case PUBLIC_KEY: + if (!cert.empty()) { + Certificate x509(cert); + EIDType eid_type = getEIDType(x509.policies()); + if (eid_type != EIDType::Unknown) { + ofs << BuildLabelEID(cert); + } else { + ofs << BuildLabelCertificate(file_name, cert); + } + } else { + ofs << BuildLabelPublicKey(CDoc2::KEYLABELVERSION, file_name); + } + case KEYSHARE: + break; + } + for (auto& [key, value] : extra) { + if (!value.empty()) { + ofs << '&'; + ofs << libcdoc::urlEncode(key) << '=' << libcdoc::urlEncode(value); } } - - // If the execution reaches so far then EID type determination failed. - return Recipient::EIDType::Unknown; + LOG_DBG("Generated label: {}", ofs.str()); + return ofs.str(); } map Recipient::parseLabel(const string& label) diff --git a/cdoc/Recipient.h b/cdoc/Recipient.h index 544e0c9b..284927e8 100644 --- a/cdoc/Recipient.h +++ b/cdoc/Recipient.h @@ -50,14 +50,6 @@ struct CDOC_EXPORT Recipient { * @brief Public key */ PUBLIC_KEY, - /** - * @brief Full certificate - */ - CERTIFICATE, - /** - * @brief public key on keyserver - */ - SERVER, /** * @brief n of n shared symmetric key */ @@ -81,13 +73,27 @@ struct CDOC_EXPORT Recipient { /** * @brief The EID type */ - enum EIDType { + enum EIDType : unsigned char { Unknown, IDCard, DigiID, 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; /** @@ -103,7 +109,7 @@ struct CDOC_EXPORT Recipient { */ int32_t kdf_iter = 0; /** - * @brief The recipient's label + * @brief The recipient's label (if empty the lock label will be autogenerated) */ std::string label; /** @@ -122,6 +128,21 @@ struct CDOC_EXPORT Recipient { * @brief The keyserver or share server list id (if present) */ std::string server_id; + /** + * @brief The requested capsule expiry timestamp (0 - use server default) + * + */ + uint64_t expiry_ts = 0; + /** + * @brief key/certificate filename for machine-readable label + * + */ + std::string file_name; + /** + * @brief public key/password name for machine-readable label + * + */ + std::string key_name; /** * @brief test whether the Recipient structure is initialized @@ -137,17 +158,17 @@ struct CDOC_EXPORT Recipient { * @brief check whether Recipient is based on public key * @return true if type is CERTIFICATE, PUBLIC_KEY or SERVER */ - bool isPKI() const { return (type == Type::CERTIFICATE) || (type == Type::PUBLIC_KEY) || (type == Type::SERVER); } + bool isPKI() const { return (type == Type::PUBLIC_KEY); } /** * @brief check whether Recipient is based on certificate * @return true if type is CERTIFICATE */ - bool isCertificate() const { return (type == Type::CERTIFICATE); } + bool isCertificate() const { return (type == Type::PUBLIC_KEY) && !cert.empty(); } /** * @brief check whether Recipient is keyserver * @return true if type is SERVER */ - bool isKeyServer() const { return (type == Type::SERVER); } + bool isKeyServer() const { return (type == Type::PUBLIC_KEY) && !server_id.empty(); } /** * @brief check whether Recipient is keyshare * @return true if type is KEYSHARE @@ -194,15 +215,11 @@ struct CDOC_EXPORT Recipient { * @return a new Recipient structure */ static Recipient makeCertificate(std::string label, std::vector cert); + /** - * @brief Create a new certificate based Recipient filling label from certificate - * @see makeCertificate, BuildLabelEID - * @param cert the certificate value (der-encoded) - * @return a new Recipient structure - */ - static Recipient makeEID(std::vector cert); - /** - * @brief Create new server based Recipient + * @brief Create a new capsule server based Recipient + * If the label is empty, a machine-readable label text (public key version) is automatically generated according to CDoc2 specification. + * * @param label the label text * @param public_key the public key value * @param pk_type the algorithm type (either ECC or RSA) @@ -210,14 +227,18 @@ struct CDOC_EXPORT Recipient { * @return a new Recipient structure */ static Recipient makeServer(std::string label, std::vector public_key, PKType pk_type, std::string server_id); + /** - * @brief Create new server based Recipient filling label from certificate - * @see makeServer, BuildLabelEID - * @param cert the certificate value (der-encoded) + * @brief Create a new capsule server based Recipient + * If the label is empty, a machine-readable label text (either eID or certificate version) is automatically generated according to CDoc2 specification. + * + * @param label the label text + * @param cert the recipient's certificate (der-encoded) * @param server_id the keyserver id * @return a new Recipient structure */ - static Recipient makeEIDServer(std::vector cert, std::string server_id); + static Recipient makeServer(std::string label, std::vector cert, std::string server_id); + /** * @brief Create new keyshare recipient * @@ -229,75 +250,14 @@ struct CDOC_EXPORT Recipient { static Recipient makeShare(const std::string& label, const std::string& server_id, const std::string& recipient_id); /** - * @brief build machine-readable CDoc2 label - * @param components a list of string pairs - * @return a composed label - */ - static std::string buildLabel(std::vector> components); - /** - * @brief build machine-readable CDoc2 label for EID recipient - * @param version the label version - * @param type EID type - * @param cn the common name - * @param serial_number the serial number - * @param last_name the last name - * @param first_name the first name - * @return a composed label - */ - static std::string BuildLabelEID(int version, EIDType type, std::string_view cn, std::string_view serial_number, std::string_view last_name, std::string_view first_name); - /** - * @brief build machine-readable CDoc2 label for EID recipient filling info from certificate - * @see BuildLabelEID - * @param cert the certificate value (der-encoded) - * @return a composed label - */ - static std::string BuildLabelEID(const std::vector &cert); - /** - * @brief build machine-readable CDoc2 label for certificate-based recipient - * @param version the label version - * @param file the name of certificate file - * @param cn the common name - * @param cert_sha1 the certificate SHA1 hash - * @return a composed label - */ - static std::string BuildLabelCertificate(int version, std::string_view file, std::string_view cn, const std::vector& cert_sha1); - /** - * @brief build machine-readable CDoc2 label for certificate-based recipient filling info from certificate - * @see BuildLabelCertificate - * @param file the name of certificate file - * @param cert the certificate value (der-encoded) - * @return a composed label - */ - static std::string BuildLabelCertificate(std::string_view file, const std::vector &cert); - /** - * @brief build machine-readable CDoc2 label for public key based recipient - * @param version the label version - * @param file the name of public key file - * @return a composed label - */ - static std::string BuildLabelPublicKey(int version, const std::string file); - /** - * @brief build machine-readable CDoc2 label for symmetric key based recipient - * @param version the label version - * @param label the key label - * @param file the name of key file - * @return a composed label - */ - static std::string BuildLabelSymmetricKey(int version, const std::string& label, const std::string file); - /** - * @brief build machine-readable CDoc2 label for password key based recipient - * @param version the label version - * @param label the password label - * @return a composed label - */ - static std::string BuildLabelPassword(int version, const std::string& label); - - /** - * @brief get EID type from policies list - * @param policies the list of policies - * @return EID type + * @brief Get the label for this recipient + * + * Either returns user-specified label or generate machine-readable if empty + * + * @param extra additional parameter values to use + * @return a label value */ - static EIDType getEIDType(const std::vector& policies); + std::string getLabel(std::vector> extra) const; /** * @brief parse machine-readable CDoc2 label @@ -309,7 +269,6 @@ struct CDOC_EXPORT Recipient { bool operator== (const Recipient& other) const = default; protected: Recipient(Type _type) : type(_type) {}; -private: }; } // namespace libcdoc diff --git a/cdoc/Utils.cpp b/cdoc/Utils.cpp index 2909fad1..9ad09c9a 100644 --- a/cdoc/Utils.cpp +++ b/cdoc/Utils.cpp @@ -55,6 +55,33 @@ getTime() return std::chrono::duration(std::chrono::system_clock::now().time_since_epoch()).count(); } +#if defined(_WIN32) || defined(_WIN64) +#define timegm _mkgmtime +#endif + +double +timeFromISO(std::string_view iso) +{ + std::istringstream in{std::string(iso.data(), iso.size())}; + std::tm t = {}; + in >> std::get_time(&t, "%Y-%m-%dT%TZ"); + return timegm(&t); +} + +std::string +timeToISO(double time) +{ +#ifdef __cpp_lib_format + auto expiry_tp = std::chrono::system_clock::time_point{std::chrono::seconds{time_t(time)}}; + return std::format("{:%FT%TZ}", expiry_tp); +#else + char buf[sizeof "2011-10-08T07:07:09Z"]; + time_t tstamp = (time_t) time; + strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&tstamp)); + return std::string(buf); +#endif +} + int parseURL(const std::string& url, std::string& host, int& port, std::string& path, bool end_with_slash) { diff --git a/cdoc/Utils.h b/cdoc/Utils.h index 8518605b..db126a90 100644 --- a/cdoc/Utils.h +++ b/cdoc/Utils.h @@ -83,6 +83,8 @@ std::vector JsonToStringArray(std::string_view json); // Get time in seconds since the Epoch double getTime(); +double timeFromISO(std::string_view iso); +std::string timeToISO(double time); static std::vector readAllBytes(std::istream& ifs) diff --git a/cdoc/cdoc-tool.cpp b/cdoc/cdoc-tool.cpp index 5b7236f7..c5deef62 100644 --- a/cdoc/cdoc-tool.cpp +++ b/cdoc/cdoc-tool.cpp @@ -41,9 +41,9 @@ static void print_usage(ostream& ofs) ofs << " Encrypt files for one or more recipients" << endl; ofs << " RECIPIENT has to be one of the following:" << endl; ofs << " [label]:cert:CERTIFICATE_HEX - public key from certificate" << endl; - ofs << " [label]:skey:SECRET_KEY_HEX - AES key" << endl; ofs << " [label]:pkey:SECRET_KEY_HEX - public key" << endl; ofs << " [label]:pfkey:PUB_KEY_FILE - path to DER file with EC (secp384r1 curve) public key" << endl; + ofs << " [label]:skey:SECRET_KEY_HEX - AES key" << endl; ofs << " [label]:pw:PASSWORD - Derive key using PWBKDF" << endl; ofs << " [label]:p11sk:SLOT:[PIN]:[PKCS11 ID]:[PKCS11 LABEL] - use AES key from PKCS11 module" << endl; ofs << " [label]:p11pk:SLOT:[PIN]:[PKCS11 ID]:[PKCS11 LABEL] - use public key from PKCS11 module" << endl; @@ -283,6 +283,9 @@ static int ParseAndEncrypt(int argc, char *argv[]) ToolConf conf; RecipientInfoVector rcpts; + // + // Parse all arguments into ToolConf structure + // int arg_idx = 0; while (arg_idx < argc) { int result = parse_common(conf, arg_idx, argc, argv); diff --git a/libcdoc.i b/libcdoc.i index f1a7f9d1..fb1b1068 100644 --- a/libcdoc.i +++ b/libcdoc.i @@ -446,7 +446,6 @@ static std::vector SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *jen %ignore libcdoc::Recipient::rcpt_key; %ignore libcdoc::Recipient::cert; -%ignore libcdoc::Recipient::buildLabel(std::vector> components); %extend libcdoc::Recipient { std::vector getRcptKey() { return $self->rcpt_key; @@ -460,13 +459,6 @@ static std::vector SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *jen void setCert(const std::vector& value) { $self->cert = value; } - static std::string buildLabel(const std::vector& values) { - std::vector> vec; - for (size_t i = 0; (i + 1) < values.size(); i += 2) { - vec.push_back({values[i], values[i + 1]}); - } - return libcdoc::Recipient::buildLabel(vec); - } }; //