diff --git a/cdoc/CDoc.h b/cdoc/CDoc.h index 960f4727..01ac043b 100644 --- a/cdoc/CDoc.h +++ b/cdoc/CDoc.h @@ -19,7 +19,7 @@ #ifndef __CDOC_H__ #define __CDOC_H__ -#include "Exports.h" +#include #include #include @@ -129,6 +129,31 @@ CDOC_EXPORT std::string getErrorStr(int64_t code); CDOC_EXPORT std::string getVersion(); +/** + * @brief The public key algorithm + */ +enum Algorithm : uint8_t { + UNKNOWN_ALGORITHM, + /** + * Elliptic curve + */ + ECC, + /** + * RSA + */ + RSA +}; + +/** + * @brief The EC curve used + */ +enum Curve : uint8_t { + UNKNOWN_CURVE, + SECP_384_R1, + SECP_256_R1 +}; + + // Logging interface /** diff --git a/cdoc/CDoc1Reader.cpp b/cdoc/CDoc1Reader.cpp index 9d293b51..5c45da88 100644 --- a/cdoc/CDoc1Reader.cpp +++ b/cdoc/CDoc1Reader.cpp @@ -89,12 +89,12 @@ CDoc1Reader::getLockForCert(const std::vector& cert) ll.encrypted_fmk.empty()) continue; switch(cc.getAlgorithm()) { - case libcdoc::Certificate::RSA: + case libcdoc::RSA: if (ll.getString(Lock::Params::METHOD) == libcdoc::Crypto::RSA_MTH) { return i; } break; - case libcdoc::Certificate::ECC: + case libcdoc::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; @@ -331,7 +331,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 478e51ab..81ae1e49 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -508,44 +508,52 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor switch(recipient.capsule_type()) { case Capsule::recipients_ECCPublicKeyCapsule: - 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.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))); + if(const auto *capsule = recipient.capsule_as_recipients_ECCPublicKeyCapsule()) { + lock.type = Lock::Type::PUBLIC_KEY; + lock.pk_type = Algorithm::ECC; + if(capsule->curve() == EllipticCurve::secp384r1) { + lock.ec_type = Curve::SECP_384_R1; + } else if (capsule->curve() == EllipticCurve::secp256r1) { + lock.ec_type = Curve::SECP_256_R1; } else { - LOG_ERROR("Unsupported ECC curve: skipping"); + LOG_WARN("Unknown ECC curve: {}", (int) capsule->curve()); + lock.ec_type = Curve::UNKNOWN_CURVE; } + lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(capsule->recipient_public_key())); + lock.setBytes(Lock::Params::KEY_MATERIAL, toUint8Vector(capsule->sender_public_key())); + LOG_DBG("Load PK: {}", toHex(lock.getBytes(Lock::Params::RCPT_KEY))); } return; case Capsule::recipients_RSAPublicKeyCapsule: if(const auto *key = recipient.capsule_as_recipients_RSAPublicKeyCapsule()) { lock.type = Lock::Type::PUBLIC_KEY; - lock.pk_type = Lock::PKType::RSA; + lock.pk_type = Algorithm::RSA; lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(key->recipient_public_key())); lock.setBytes(Lock::Params::KEY_MATERIAL, toUint8Vector(key->encrypted_kek())); } return; case Capsule::recipients_KeyServerCapsule: - if (const KeyServerCapsule *server = recipient.capsule_as_recipients_KeyServerCapsule()) { - KeyDetailsUnion details = server->recipient_key_details_type(); + if (const KeyServerCapsule *capsule = recipient.capsule_as_recipients_KeyServerCapsule()) { + KeyDetailsUnion details = capsule->recipient_key_details_type(); switch (details) { case KeyDetailsUnion::EccKeyDetails: - if(const EccKeyDetails *eccDetails = server->recipient_key_details_as_EccKeyDetails()) { - if(eccDetails->curve() != EllipticCurve::secp384r1) { - LOG_ERROR("Unsupported elliptic curve key type"); - return; - } - lock.pk_type = Lock::PKType::ECC; + if(const EccKeyDetails *eccDetails = capsule->recipient_key_details_as_EccKeyDetails()) { + lock.pk_type = Algorithm::ECC; lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(eccDetails->recipient_public_key())); + if(eccDetails->curve() == EllipticCurve::secp384r1) { + lock.ec_type = Curve::SECP_384_R1; + } else if (eccDetails->curve() == EllipticCurve::secp256r1) { + lock.ec_type = Curve::SECP_256_R1; + } else { + LOG_WARN("Unknown ECC curve: {}", (int) eccDetails->curve()); + lock.ec_type = Curve::UNKNOWN_CURVE; + } } break; case KeyDetailsUnion::RsaKeyDetails: - if(const RsaKeyDetails *rsaDetails = server->recipient_key_details_as_RsaKeyDetails()) { - lock.pk_type = Lock::PKType::RSA; + if(const RsaKeyDetails *rsaDetails = capsule->recipient_key_details_as_RsaKeyDetails()) { + lock.pk_type = Algorithm::RSA; lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(rsaDetails->recipient_public_key())); } break; @@ -554,8 +562,8 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor return; } lock.type = Lock::Type::SERVER; - lock.setString(Lock::Params::KEYSERVER_ID, server->keyserver_id()->str()); - lock.setString(Lock::Params::TRANSACTION_ID, server->transaction_id()->str()); + lock.setString(Lock::Params::KEYSERVER_ID, capsule->keyserver_id()->str()); + lock.setString(Lock::Params::TRANSACTION_ID, capsule->transaction_id()->str()); } return; case Capsule::recipients_SymmetricKeyCapsule: diff --git a/cdoc/CDoc2Writer.cpp b/cdoc/CDoc2Writer.cpp index a79271b6..3f09fa17 100644 --- a/cdoc/CDoc2Writer.cpp +++ b/cdoc/CDoc2Writer.cpp @@ -95,6 +95,17 @@ CDoc2Writer::writeHeader(const std::vector &recipients) return OK; } +struct ECData { + int ossl_nid; + cdoc20::recipients::EllipticCurve fb_type; + const char *api_id; +}; + +static std::map ecdata = { + {Curve::SECP_384_R1, {NID_secp384r1, cdoc20::recipients::EllipticCurve::secp384r1, "ecc_secp384r1"}}, + {Curve::SECP_256_R1, {NID_X9_62_prime256v1, cdoc20::recipients::EllipticCurve::secp256r1, "ecc_secp256r1"}} +}; + static flatbuffers::Offset createRSACapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::Recipient& rcpt, const std::vector& encrypted_kek, const std::vector& xor_key) { @@ -131,7 +142,7 @@ static flatbuffers::Offset createECCCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::Recipient& rcpt, const std::vector& eph_public_key, const std::vector& xor_key) { auto capsule = cdoc20::recipients::CreateECCPublicKeyCapsule(builder, - cdoc20::recipients::EllipticCurve::secp384r1, + ecdata[rcpt.ec_type].fb_type, builder.CreateVector(rcpt.rcpt_key), builder.CreateVector(eph_public_key)); return cdoc20::header::CreateRecipientRecord(builder, @@ -146,7 +157,7 @@ static flatbuffers::Offset 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, + ecdata[rcpt.ec_type].fb_type, builder.CreateVector(rcpt.rcpt_key)); auto capsule = cdoc20::recipients::CreateKeyServerCapsule(builder, cdoc20::recipients::KeyDetailsUnion::EccKeyDetails, @@ -220,7 +231,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"); @@ -256,7 +267,12 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector& header, const std::vectorsendKey(cinfo, send_url, rcpt.rcpt_key, key_material, "ecc_secp384r1", rcpt.expiry_ts); + auto result = network->sendKey(cinfo, send_url, rcpt.rcpt_key, key_material, ecdata[rcpt.ec_type].api_id, rcpt.expiry_ts); if (result < 0) { setLastError(network->getLastErrorStr(result)); LOG_ERROR("{}", last_error); diff --git a/cdoc/CDocCipher.cpp b/cdoc/CDocCipher.cpp index c94efe9f..fb692f01 100644 --- a/cdoc/CDocCipher.cpp +++ b/cdoc/CDocCipher.cpp @@ -19,6 +19,7 @@ #include "CDocCipher.h" #include "CDocReader.h" #include "CDoc2.h" +#include "Crypto.h" #include "Lock.h" #include "NetworkBackend.h" #include "PKCS11Backend.h" @@ -35,20 +36,19 @@ #include #include #include +#include using namespace std; using namespace libcdoc; -static string GenerateRandomSequence(); - struct ToolPKCS11 : public libcdoc::PKCS11Backend { - const std::map& rcpts; + const std::vector& rcpts; - ToolPKCS11(const std::string& library, const std::map& vec) : libcdoc::PKCS11Backend(library), rcpts(vec) {} + ToolPKCS11(const std::string& library, const std::vector& vec) : libcdoc::PKCS11Backend(library), rcpts(vec) {} libcdoc::result_t connectToKey(int idx, bool priv) override final { - if (!rcpts.contains(idx)) return libcdoc::CRYPTO_ERROR; - libcdoc::RcptInfo rcpt = rcpts.at(idx); + if (idx >= rcpts.size()) idx = 0; + const libcdoc::RcptInfo& rcpt = rcpts[idx]; if (!priv) { return useSecretKey(rcpt.p11.slot, rcpt.secret, rcpt.p11.key_id, rcpt.p11.key_label); } else { @@ -59,26 +59,26 @@ struct ToolPKCS11 : public libcdoc::PKCS11Backend { #ifdef _WIN32 struct ToolWin : public libcdoc::WinBackend { - const std::map& rcpts; + const std::vector& rcpts; - ToolWin(const std::string& provider, const std::map& vec) : libcdoc::WinBackend(provider), rcpts(vec) {} + ToolWin(const std::string& provider, const std::vector& vec) : libcdoc::WinBackend(provider), rcpts(vec) {} result_t connectToKey(int idx, bool priv) { - if (!rcpts.contains(idx)) return libcdoc::CRYPTO_ERROR; - const libcdoc::RcptInfo &rcpt = rcpts.at(idx); + if (idx >= rcpts.size()) idx = 0; + const libcdoc::RcptInfo& rcpt = rcpts[idx]; return useKey(rcpt.p11.key_label, std::string(rcpt.secret.cbegin(), rcpt.secret.cend())); } }; #endif struct ToolCrypto : public libcdoc::CryptoBackend { - const std::map& rcpts; + const std::vector& rcpts; std::unique_ptr p11; #ifdef _WIN32 std::unique_ptr ncrypt; #endif - ToolCrypto(const std::map& recipients) : rcpts(recipients) { + ToolCrypto(const std::vector& recipients) : rcpts(recipients) { } bool connectLibrary(const std::string& library) { @@ -97,8 +97,8 @@ struct ToolCrypto : public libcdoc::CryptoBackend { libcdoc::result_t decryptRSA(std::vector& dst, const std::vector &data, bool oaep, unsigned int idx) override final { if (p11) return p11->decryptRSA(dst, data, oaep, idx); - if (!rcpts.contains(idx)) return libcdoc::CRYPTO_ERROR; - const libcdoc::RcptInfo &rcpt = rcpts.at(idx); + if (idx >= rcpts.size()) idx = 0; + const libcdoc::RcptInfo& rcpt = rcpts[idx]; if (rcpt.secret.empty()) return libcdoc::CRYPTO_ERROR; const uint8_t *p = rcpt.secret.data(); @@ -129,8 +129,8 @@ struct ToolCrypto : public libcdoc::CryptoBackend { } libcdoc::result_t deriveECDH1(std::vector& dst, const std::vector &public_key, unsigned int idx) override final { - if (!rcpts.contains(idx)) return libcdoc::CRYPTO_ERROR; - const libcdoc::RcptInfo &rcpt = rcpts.at(idx); + if (idx >= rcpts.size()) idx = 0; + const libcdoc::RcptInfo& rcpt = rcpts[idx]; if (rcpt.secret.empty()) return libcdoc::CRYPTO_ERROR; const uint8_t *p = rcpt.secret.data(); @@ -142,7 +142,6 @@ struct ToolCrypto : public libcdoc::CryptoBackend { EVP_PKEY *params = nullptr; if ((EVP_PKEY_paramgen_init(ctx.get()) < 0) || - (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), NID_secp384r1) < 0) || (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) < 0) || (EVP_PKEY_paramgen(ctx.get(), ¶ms) < 0)) return libcdoc::CRYPTO_ERROR; @@ -182,8 +181,8 @@ struct ToolCrypto : public libcdoc::CryptoBackend { } libcdoc::result_t getSecret(std::vector& secret, unsigned int idx) override final { - if (!rcpts.contains(idx)) return libcdoc::CRYPTO_ERROR; - const libcdoc::RcptInfo &rcpt = rcpts.at(idx); + if (idx >= rcpts.size()) idx = 0; + const libcdoc::RcptInfo& rcpt = rcpts[idx]; secret = rcpt.secret; return secret.empty() ? INVALID_PARAMS : libcdoc::OK; } @@ -205,10 +204,9 @@ struct ToolNetwork : public libcdoc::NetworkBackend { } libcdoc::result_t getClientTLSCertificate(std::vector& dst) override final { - if (!crypto->rcpts.contains(rcpt_idx)) return libcdoc::CRYPTO_ERROR; - const RcptInfo& rcpt = crypto->rcpts.at(rcpt_idx); - bool rsa = false; - return crypto->p11->getCertificate(dst, rsa, rcpt.p11.slot, rcpt.secret, rcpt.p11.key_id, rcpt.p11.key_label); + if (rcpt_idx >= crypto->rcpts.size()) rcpt_idx = 0; + const libcdoc::RcptInfo& rcpt = crypto->rcpts[rcpt_idx]; + return crypto->p11->getCertificate(dst, rcpt.p11.slot, rcpt.secret, rcpt.p11.key_id, rcpt.p11.key_label); } libcdoc::result_t getPeerTLSCertificates(std::vector> &dst) override final { @@ -217,12 +215,22 @@ struct ToolNetwork : public libcdoc::NetworkBackend { } libcdoc::result_t signTLS(std::vector& dst, libcdoc::CryptoBackend::HashAlgorithm algorithm, const std::vector &digest) override final { - if (!crypto->rcpts.contains(rcpt_idx)) return libcdoc::CRYPTO_ERROR; + if (rcpt_idx >= crypto->rcpts.size()) rcpt_idx = 0; + const libcdoc::RcptInfo& rcpt = crypto->rcpts[rcpt_idx]; return crypto->p11->sign(dst, algorithm, digest, rcpt_idx); } }; +static int +EVP_PKEY_get_nid(EVP_PKEY *pkey) +{ + std::array name; + if (SSL_FAILED(EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, name.data(), name.size(), nullptr), "EVP_PKEY_get_utf8_string_param")) + return NID_undef; + return OBJ_sn2nid(name.data()); +} + int CDocCipher::writer_push(CDocWriter& writer, const vector& rcpts, const vector& files) { for (const libcdoc::Recipient& rcpt : rcpts) { @@ -258,7 +266,7 @@ int CDocCipher::writer_push(CDocWriter& writer, const vector& rcpts, #define PUSH true static bool -fill_recipients_from_rcpt_info(ToolConf& conf, ToolCrypto& crypto, std::vector& rcpts, std::map& crypto_rcpts, const std::vector& recipients) +fill_recipients_from_rcpt_info(ToolConf& conf, ToolCrypto& crypto, std::vector& rcpts, const std::vector& recipients) { int idx = 0; for (const auto& rcpt : recipients) { @@ -266,8 +274,6 @@ fill_recipients_from_rcpt_info(ToolConf& conf, ToolCrypto& crypto, std::vector d(i2d_PublicKey(pkey, nullptr), 0); - uint8_t *p = d.data(); - i2d_PublicKey(pkey, &p); - if (id == EVP_PKEY_EC) { - key = libcdoc::Recipient::makePublicKey(label, rcpt.secret, libcdoc::Recipient::PKType::ECC); - } else if (id == EVP_PKEY_RSA) { - key = libcdoc::Recipient::makePublicKey(label, rcpt.secret, libcdoc::Recipient::PKType::RSA); + if (algo == libcdoc::Algorithm::RSA) { + key = libcdoc::Recipient::makeRSA(label, rcpt.secret); + } else { + key = libcdoc::Recipient::makeECC(label, rcpt.secret, curve); } } LOG_DBG("Creating public key:"); } else if (rcpt.type == RcptInfo::Type::P11_SYMMETRIC) { key = libcdoc::Recipient::makeSymmetric(label, 0); + key.key_name = rcpt.label; } else if (rcpt.type == RcptInfo::Type::P11_PKI) { std::vector val; - bool rsa; + libcdoc::Algorithm algo; ToolPKCS11* p11 = dynamic_cast(crypto.p11.get()); - int result = p11->getPublicKey(val, rsa, rcpt.p11.slot, rcpt.secret, rcpt.p11.key_id, rcpt.p11.key_label); + int result = p11->getPublicKey(val, algo, rcpt.p11.slot, rcpt.secret, rcpt.p11.key_id, rcpt.p11.key_label); if (result != libcdoc::OK) { LOG_ERROR("No such public key: {}", rcpt.p11.key_label); continue; } - LOG_DBG("Public key ({}): {}", rsa ? "rsa" : "ecc", toHex(val)); + LOG_DBG("Public key ({}): {}", (algo == libcdoc::Algorithm::RSA) ? "rsa" : "ecc", toHex(val)); if (!conf.servers.empty()) { - key = libcdoc::Recipient::makeServer(label, val, rsa ? libcdoc::Recipient::PKType::RSA : libcdoc::Recipient::PKType::ECC, conf.servers[0].ID); + key = libcdoc::Recipient::makeServer(label, val, algo, conf.servers[0].ID); } else { - key = libcdoc::Recipient::makePublicKey(label, val, rsa ? libcdoc::Recipient::PKType::RSA : libcdoc::Recipient::PKType::ECC); + key = libcdoc::Recipient::makePublicKey(label, val, algo); } } else if (rcpt.type == RcptInfo::Type::PASSWORD) { LOG_DBG("Creating password key:"); key = libcdoc::Recipient::makeSymmetric(label, 65535); + key.key_name = rcpt.label; } else if (rcpt.type == RcptInfo::Type::SHARE) { LOG_DBG("Creating keyshare recipient:"); key = libcdoc::Recipient::makeShare(label, conf.servers[0].ID, "PNOEE-" + rcpt.id); + } else { + LOG_ERROR("Invalid recipient type: {}", (int) rcpt.type); + return false; } rcpts.push_back(std::move(key)); @@ -327,8 +364,7 @@ fill_recipients_from_rcpt_info(ToolConf& conf, ToolCrypto& crypto, std::vector& recipients) { - std::map crypto_rcpts; - ToolCrypto crypto(crypto_rcpts); + ToolCrypto crypto(recipients); ToolNetwork network(&crypto); network.certs = std::move(conf.accept_certs); @@ -342,7 +378,7 @@ int CDocCipher::Encrypt(ToolConf& conf, std::vector& recipien } vector rcpts; - fill_recipients_from_rcpt_info(conf, crypto, rcpts, crypto_rcpts, recipients); + fill_recipients_from_rcpt_info(conf, crypto, rcpts, recipients); if (rcpts.empty()) { LOG_ERROR("No key for encryption was found"); @@ -370,8 +406,8 @@ int CDocCipher::Encrypt(ToolConf& conf, std::vector& recipien int CDocCipher::Decrypt(ToolConf& conf, const RcptInfo& recipient) { - std::map rcpts; - ToolCrypto crypto(rcpts); + std::vector r = {recipient}; + ToolCrypto crypto(r); ToolNetwork network(&crypto); network.certs = std::move(conf.accept_certs); @@ -403,10 +439,9 @@ int CDocCipher::Decrypt(ToolConf& conf, const RcptInfo& recipient) } lock_idx = recipient.lock_idx; } else if (crypto.p11) { - bool isRsa; vector cert_bytes; ToolPKCS11* p11 = dynamic_cast(crypto.p11.get()); - int64_t result = p11->getCertificate(cert_bytes, isRsa, (int) recipient.p11.slot, recipient.secret, recipient.p11.key_id, recipient.p11.key_label); + int64_t result = p11->getCertificate(cert_bytes, (int) recipient.p11.slot, recipient.secret, recipient.p11.key_id, recipient.p11.key_label); if (result != libcdoc::OK) { LOG_ERROR("Certificate reading from SC card failed. Key label: {}", recipient.p11.key_label); return 1; @@ -424,9 +459,8 @@ int CDocCipher::Decrypt(ToolConf& conf, const RcptInfo& recipient) return 1; } LOG_INFO("Found matching lock: {}", recipient.label); - rcpts[lock_idx] = recipient; - network.rcpt_idx = lock_idx; + return Decrypt(rdr, lock_idx, conf.out); } @@ -513,11 +547,11 @@ int CDocCipher::Decrypt(const unique_ptr& rdr, unsigned int lock_idx } int -CDocCipher::ReEncrypt(ToolConf& conf, const RcptInfo& lock_info, std::vector& recipients) +CDocCipher::ReEncrypt(ToolConf& conf, const RcptInfo& dec_info, std::vector& enc_info) { // Decryption part - std::map dec_info; - ToolCrypto crypto(dec_info); + std::vector rcpt_list = {dec_info}; + ToolCrypto crypto(rcpt_list); ToolNetwork network(&crypto); network.certs = std::move(conf.accept_certs); @@ -535,39 +569,36 @@ CDocCipher::ReEncrypt(ToolConf& conf, const RcptInfo& lock_info, std::vector& locks = rdr->getLocks(); int lock_idx = -1; - if (!lock_info.label.empty()) { + if (!dec_info.label.empty()) { for (unsigned int i = 0; i < locks.size(); i++) { - if (locks[i].label == lock_info.label) { + if (locks[i].label == dec_info.label) { lock_idx = i; break; } } - } else if (lock_info.lock_idx >= 0) { - if (lock_info.lock_idx >= locks.size()) { + } else if (dec_info.lock_idx >= 0) { + if (dec_info.lock_idx >= locks.size()) { LOG_ERROR("Label index is out of range"); return 1; } - lock_idx = lock_info.lock_idx; + lock_idx = dec_info.lock_idx; } if (lock_idx < 0) { - LOG_ERROR("Lock not found: {}", lock_info.label); + LOG_ERROR("Lock not found: {}", dec_info.label); return 1; } - dec_info[lock_idx] = lock_info; - network.rcpt_idx = lock_idx; // Encryption part - std::map crypto_rcpts; - ToolCrypto enc_crypto(crypto_rcpts); + ToolCrypto enc_crypto(enc_info); ToolNetwork enc_network(&enc_crypto); enc_network.certs = std::move(conf.accept_certs); if (!conf.library.empty()) { enc_crypto.connectLibrary(conf.library); } - for (const auto& rcpt : recipients) { + for (const auto& rcpt : enc_info) { if (rcpt.type == RcptInfo::NCRYPT) { enc_crypto.connectNCrypt(); break; @@ -575,7 +606,7 @@ CDocCipher::ReEncrypt(ToolConf& conf, const RcptInfo& lock_info, std::vector rcpts; - fill_recipients_from_rcpt_info(conf, enc_crypto, rcpts, crypto_rcpts, recipients); + fill_recipients_from_rcpt_info(conf, enc_crypto, rcpts, enc_info); if (rcpts.empty()) { LOG_ERROR("No key for encryption was found"); @@ -699,35 +730,3 @@ void CDocCipher::Locks(const char* file) const lock_id++; } } - -static string GenerateRandomSequence() -{ - constexpr uint32_t upperbound = 'z' - '0' + 1; - constexpr int MaxSequenceLength = 11; - - uint32_t rnd; - uint8_t rndByte; - ostringstream sequence; - for (int cnt = 0; cnt < MaxSequenceLength;) - { - if (RAND_bytes(&rndByte, 1) < 1) - { - rnd = rand() % upperbound + '0'; - } - else - { - rnd = rndByte % upperbound + '0'; - } - - // arc4random_uniform tends to be not available on all platforms. - // rnd = arc4random_uniform(upperbound) + '0'; - - if (isalnum(rnd)) - { - sequence << static_cast(rnd); - cnt++; - } - } - - return sequence.str(); -} diff --git a/cdoc/CMakeLists.txt b/cdoc/CMakeLists.txt index 61e43697..00886d70 100644 --- a/cdoc/CMakeLists.txt +++ b/cdoc/CMakeLists.txt @@ -89,7 +89,7 @@ target_link_libraries(cdoc PRIVATE ) if(BUILD_TOOLS) - add_executable(cdoc-tool cdoc-tool.cpp CDocCipher.cpp) + add_executable(cdoc-tool cdoc-tool.cpp CDocCipher.cpp Crypto.cpp) target_include_directories(cdoc-tool PRIVATE ${OPENSSL_INCLUDE_DIR}) target_link_libraries(cdoc-tool cdoc_ver cdoc OpenSSL::SSL) set_target_properties(cdoc-tool PROPERTIES diff --git a/cdoc/Certificate.cpp b/cdoc/Certificate.cpp index 60769c41..24dc274f 100644 --- a/cdoc/Certificate.cpp +++ b/cdoc/Certificate.cpp @@ -144,7 +144,7 @@ Certificate::getPublicKey() const return {}; } -Certificate::Algorithm +libcdoc::Algorithm Certificate::getAlgorithm() const { if(!cert) @@ -153,7 +153,7 @@ Certificate::getAlgorithm() const EVP_PKEY *pkey = X509_get0_pubkey(cert.get()); int alg = EVP_PKEY_get_base_id(pkey); - return (alg == EVP_PKEY_RSA) ? Algorithm::RSA : Algorithm::ECC; + return (alg == EVP_PKEY_RSA) ? Algorithm::RSA : (alg == EVP_PKEY_EC) ? Algorithm::ECC : Algorithm::UNKNOWN_ALGORITHM; } std::vector Certificate::getDigest() const diff --git a/cdoc/Certificate.h b/cdoc/Certificate.h index 4fd7a54d..d477521a 100644 --- a/cdoc/Certificate.h +++ b/cdoc/Certificate.h @@ -19,7 +19,7 @@ #ifndef SSLCERTIFICATE_H #define SSLCERTIFICATE_H -#include "Exports.h" +#include "CDoc.h" #include "utils/memory.h" @@ -32,11 +32,6 @@ namespace libcdoc { class Certificate { public: - enum Algorithm : unsigned char { - RSA, - ECC - }; - enum EIDType : unsigned char { Unknown, IDCard, diff --git a/cdoc/CryptoBackend.h b/cdoc/CryptoBackend.h index bf63d5b4..04253a2c 100644 --- a/cdoc/CryptoBackend.h +++ b/cdoc/CryptoBackend.h @@ -72,7 +72,7 @@ struct CDOC_EXPORT CryptoBackend { /** * @brief Derive shared secret * - * Derive a shared secret from private key of given lock and public key using ECDH1 algorithm. + * Derive a shared secret using the private key of recipient and the public key of lock using ECDH1 algorithm. * @param dst the container for shared secret * @param public_key ECDH public key used to derive shared secret * @param idx lock index (0-based) in container @@ -153,11 +153,11 @@ struct CDOC_EXPORT CryptoBackend { int32_t kdf_iter, unsigned int idx); /** - * @brief sign Sign message with given algorithm + * @brief sign Sign message with given hash algorithm using the private key of given lock * @param dst the destination container for signed message * @param algorithm hashing algorithm * @param digest a message to sign - * @param idx lock or recipient index (0-based) in container + * @param idx lock index (0-based) in container * @return error code or OK */ virtual result_t sign(std::vector& dst, HashAlgorithm algorithm, const std::vector &digest, unsigned int idx) { diff --git a/cdoc/Lock.h b/cdoc/Lock.h index 250eeb85..0d9dfd35 100644 --- a/cdoc/Lock.h +++ b/cdoc/Lock.h @@ -19,7 +19,7 @@ #ifndef __LOCK_H__ #define __LOCK_H__ -#include +#include #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 */ @@ -178,9 +164,13 @@ struct CDOC_EXPORT Lock */ Type type = Type::UNKNOWN; /** - * @brief algorithm type for public key based locks + * @brief The algorithm type for public key based locks + */ + Algorithm pk_type = Algorithm::ECC; + /** + * @brief The elliptic curve used */ - PKType pk_type = PKType::ECC; + Curve ec_type = Curve::SECP_384_R1; /** * @brief the lock label @@ -215,7 +205,7 @@ struct CDOC_EXPORT Lock * @brief check whether public key lock uses RSA algorithm * @return true if pk_type is RSA */ - constexpr bool isRSA() const noexcept { return pk_type == PKType::RSA; } + constexpr bool isRSA() const noexcept { return pk_type == Algorithm::RSA; } Lock() noexcept = default; Lock(Type _type) noexcept : type(_type) {}; diff --git a/cdoc/NetworkBackend.h b/cdoc/NetworkBackend.h index 446cff7d..57113531 100644 --- a/cdoc/NetworkBackend.h +++ b/cdoc/NetworkBackend.h @@ -141,7 +141,7 @@ struct CDOC_EXPORT NetworkBackend { * @param url server url * @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 type algorithm type, currently either "rsa", "ecc_secp384r1" or "ecc_secp256r1" * @param expiry_ts the requested capsule expiry timestamp, 0 - use server default * @return error code or OK */ diff --git a/cdoc/PKCS11Backend.cpp b/cdoc/PKCS11Backend.cpp index 914004c8..4f7cd58f 100644 --- a/cdoc/PKCS11Backend.cpp +++ b/cdoc/PKCS11Backend.cpp @@ -319,7 +319,7 @@ libcdoc::PKCS11Backend::usePrivateKey(int slot, const std::vector& pin, } libcdoc::result_t -libcdoc::PKCS11Backend::getCertificate(std::vector& val, bool& rsa, int slot, const std::vector& pin, const std::vector& id, const std::string& label) +libcdoc::PKCS11Backend::getCertificate(std::vector& val, int slot, const std::vector& pin, const std::vector& id, const std::string& label) { if(!d) return CRYPTO_ERROR; if (!d->session) { @@ -339,7 +339,7 @@ libcdoc::PKCS11Backend::getCertificate(std::vector& val, bool& rsa, int } libcdoc::result_t -libcdoc::PKCS11Backend::getPublicKey(std::vector& val, bool& rsa, int slot, const std::vector& pin, const std::vector& id, const std::string& label) +libcdoc::PKCS11Backend::getPublicKey(std::vector& val, libcdoc::Algorithm& algorithm, int slot, const std::vector& pin, const std::vector& id, const std::string& label) { if(!d) return CRYPTO_ERROR; if (!d->session) { @@ -355,8 +355,9 @@ libcdoc::PKCS11Backend::getPublicKey(std::vector& val, bool& rsa, int s LOG_DBG("PKCS11: getValue CKA_KEY_TYPE error"); return CRYPTO_ERROR; } - rsa = (*((CK_KEY_TYPE *) v.data()) == CKK_RSA); - if (rsa) return libcdoc::NOT_IMPLEMENTED; + if (*((CK_KEY_TYPE *) v.data()) != CKK_EC) + return libcdoc::NOT_IMPLEMENTED; + algorithm = Algorithm::ECC; v = d->attribute(d->session, handle, CKA_EC_PARAMS); if (v.empty()) { LOG_DBG("PKCS11: getValue CKA_EC_PARAMS error"); diff --git a/cdoc/PKCS11Backend.h b/cdoc/PKCS11Backend.h index 5deb3ed7..17993000 100644 --- a/cdoc/PKCS11Backend.h +++ b/cdoc/PKCS11Backend.h @@ -106,28 +106,27 @@ struct CDOC_EXPORT PKCS11Backend : public CryptoBackend { * Get a certificate value given slot, label and id. * Both key id and label have to match unless either one is empty. * @param val a destination container for value - * @param rsa will be set true is certificate uses RSA key * @param slot the slot to use - * @param pin the pin code + * @param pin the pin code or empty if public * @param id certificate id or empty vector * @param label certificate label or empty vector * @return error code or OK */ - result_t getCertificate(std::vector& val, bool& rsa, int slot, const std::vector& pin, const std::vector& id, const std::string& label); + result_t getCertificate(std::vector& val, int slot, const std::vector& pin, const std::vector& id, const std::string& label); /** * @brief get public key value * * Get a public key value given slot, label and id. * Both key id and label have to match unless either one is empty. * @param val a destination container for value - * @param rsa will be set true is public key uses RSA key + * @param algorithm the output parameter for public key algorithm * @param slot the slot to use - * @param pin the pin code + * @param pin the pin code or empty if public * @param id public key id or empty vector * @param label public key label or empty vector * @return error code or OK */ - result_t getPublicKey(std::vector& val, bool& rsa, int slot, const std::vector& pin, const std::vector& id, const std::string& label); + result_t getPublicKey(std::vector& val, libcdoc::Algorithm& algorithm, int slot, const std::vector& pin, const std::vector& id, const std::string& label); /** * @brief loads key for encryption/decryption diff --git a/cdoc/Recipient.cpp b/cdoc/Recipient.cpp index 0e699844..fcfa66ba 100644 --- a/cdoc/Recipient.cpp +++ b/cdoc/Recipient.cpp @@ -60,20 +60,29 @@ Recipient::makeSymmetric(std::string label, int32_t kdf_iter) } Recipient -Recipient::makePublicKey(std::string label, std::vector public_key, PKType pk_type) +Recipient::makeRSA(std::string label, std::vector public_key) { if (public_key.empty()) return {Type::NONE}; Recipient rcpt(Type::PUBLIC_KEY); 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 - auto evp = Crypto::fromECPublicKeyDer(public_key); - rcpt.rcpt_key = Crypto::toPublicKeyDer(evp.get()); - } else { - rcpt.rcpt_key = std::move(public_key); - } + rcpt.pk_type = RSA; + rcpt.rcpt_key = std::move(public_key); + return rcpt; +} + +Recipient +Recipient::makeECC(std::string label, std::vector public_key, Curve ec_type) +{ + if (public_key.empty()) + return {Type::NONE}; + Recipient rcpt(Type::PUBLIC_KEY); + rcpt.label = std::move(label); + rcpt.pk_type = ECC; + rcpt.ec_type = ec_type; + // 0x30 identifies SEQUENCE tag in ASN.1 encoding + auto evp = Crypto::fromECPublicKeyDer(public_key); + rcpt.rcpt_key = Crypto::toPublicKeyDer(evp.get()); return rcpt; } @@ -87,15 +96,26 @@ 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(); return rcpt; } Recipient -Recipient::makeServer(std::string label, std::vector public_key, PKType pk_type, std::string server_id) +Recipient::makeServerRSA(std::string label, std::vector public_key, std::string server_id) +{ + Recipient rcpt = makeRSA(std::move(label), std::move(public_key)); + rcpt.server_id = std::move(server_id); + const auto six_months_from_now = std::chrono::system_clock::now() + std::chrono::months(6); + const auto expiry_ts = std::chrono::system_clock::to_time_t(six_months_from_now); + rcpt.expiry_ts = uint64_t(expiry_ts); + return rcpt; +} + +Recipient +Recipient::makeServerECC(std::string label, std::vector public_key, Curve ec_type, std::string server_id) { - Recipient rcpt = makePublicKey(std::move(label), std::move(public_key), pk_type); + Recipient rcpt = makeECC(std::move(label), std::move(public_key), ec_type); rcpt.server_id = std::move(server_id); const auto six_months_from_now = std::chrono::system_clock::now() + std::chrono::months(6); const auto expiry_ts = std::chrono::system_clock::to_time_t(six_months_from_now); diff --git a/cdoc/Recipient.h b/cdoc/Recipient.h index 0b04755b..fa624ebc 100644 --- a/cdoc/Recipient.h +++ b/cdoc/Recipient.h @@ -19,7 +19,7 @@ #ifndef __RECIPIENT_H__ #define __RECIPIENT_H__ -#include +#include #include #include @@ -56,21 +56,7 @@ struct CDOC_EXPORT Recipient { KEYSHARE }; - /** - * @brief The public key type - */ - enum PKType : uint8_t { - /** - * Elliptic curve - */ - ECC, - /** - * RSA - */ - RSA - }; - - Recipient() = default; + Recipient() = default; /** * @brief The recipient type @@ -79,7 +65,8 @@ struct CDOC_EXPORT Recipient { /** * @brief The public key type */ - PKType pk_type = PKType::ECC; + Algorithm pk_type = Algorithm::ECC; + Curve ec_type = Curve::SECP_384_R1; /** * @brief The number of iterations for PBKDF. Value 0 means directly provided symmetric key. */ @@ -151,11 +138,6 @@ struct CDOC_EXPORT Recipient { */ bool isKeyShare() const { return type == Type::KEYSHARE; } - /** - * @brief Clear all values and set type to NONE - */ - void clear() { type = Type::NONE; pk_type = PKType::ECC; label.clear(); kdf_iter = 0; rcpt_key.clear(); cert.clear(); } - /** * @brief A convenience method to check whether two recipients are both public key based and have the same keys. * @param other another Recipient @@ -171,29 +153,61 @@ struct CDOC_EXPORT Recipient { /** * @brief Create a new symmetric key based Recipient + * + * If the label is empty, a machine-readable label will be created according to CDoc2 specification + * * @param label the label text * @param kdf_iter the number of PBKDF iterations (0 if full key is provided) * @return a new Recipient structure */ static Recipient makeSymmetric(std::string label, int32_t kdf_iter); + + /** + * @brief Create a new public key based Recipient with RSA algorithm + * + * @param label + * @param public_key + * @return Recipient + */ + static Recipient makeRSA(std::string label, std::vector public_key); + static Recipient makeECC(std::string label, std::vector public_key, Curve ec_type); /** * @brief Create a new public key based Recipient + * + * If the label is empty, a machine-readable label will be created 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) * @return a new Recipient structure */ - static Recipient makePublicKey(std::string label, std::vector public_key, PKType pk_type); + static Recipient makePublicKey(std::string label, std::vector public_key, Algorithm pk_type) { + switch(pk_type) { + case RSA: + return makeRSA(label, public_key); + case ECC: + return makeECC(label, public_key, Curve::SECP_384_R1); + default: + return {}; + } + } + /** * @brief Create a new certificate based Recipient + * + * If the label is empty, a machine-readable label will be created according to CDoc2 specification + * * @param label the label text * @param cert the certificate value (der-encoded) * @return a new Recipient structure */ static Recipient makeCertificate(std::string label, std::vector cert); + static Recipient makeServerRSA(std::string label, std::vector public_key, std::string server_id); + static Recipient makeServerECC(std::string label, std::vector public_key, Curve ec_type, std::string server_id); /** * @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 @@ -202,10 +216,20 @@ struct CDOC_EXPORT Recipient { * @param server_id the keyserver id * @return a new Recipient structure */ - static Recipient makeServer(std::string label, std::vector public_key, PKType pk_type, std::string server_id); + static Recipient makeServer(std::string label, std::vector public_key, Algorithm pk_type, std::string server_id) { + switch(pk_type) { + case RSA: + return makeServerRSA(label, public_key, server_id); + case ECC: + return makeServerECC(label, public_key, Curve::SECP_384_R1, server_id); + default: + return {}; + } + } /** * @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 @@ -218,6 +242,8 @@ struct CDOC_EXPORT Recipient { /** * @brief Create new keyshare recipient * + * If the label text is empty, a machine-readable label will be created according to CDoc2 specification + * * @param label the label text * @param server_id the id of share server group * @param recipient_id the recipient id (PNOEE-01234567890) diff --git a/cdoc/cdoc-tool.cpp b/cdoc/cdoc-tool.cpp index 30bd7364..1dacf2d5 100644 --- a/cdoc/cdoc-tool.cpp +++ b/cdoc/cdoc-tool.cpp @@ -52,28 +52,30 @@ static void print_usage(ostream& ofs) ofs << "cdoc-tool encrypt --rcpt RECIPIENT [--rcpt...] [-v1] [--genlabel] --out OUTPUTFILE FILE [FILE...]" << endl; ofs << " Encrypt files for one or more recipients" << endl; ofs << " RECIPIENT has to be one of the following:" << endl; - ofs << " [label]:cert:CERTIFICATE_FILE - public key from certificate" << 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]:cert:CERTIFICATE_FILE - public key from certificate file (DER format)" << endl; + ofs << " [label]:pkey:SECRET_KEY_HEX - hex encoded public key (DER format; rsa, secp384r1 or secp256r1 key)." << endl; + ofs << " [label]:pfkey:PUB_KEY_FILE - public key from file (DER format; rsa, secp384r1 or secp256r1 key)." << endl; + ofs << " [label]:skey:SECRET_KEY_HEX - AES key, hex encoded" << endl; + ofs << " [label]:pw:PASSWORD - AES key derived from password with 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; - ofs << " [label]:share:ID - use keyshares with given ID (personal code)" << endl; - ofs << " -v1 - creates CDOC1 version container. Supported only for encryption with certificate." << endl; - ofs << " --genlabel - If specified, the lock label is generated." << endl; + ofs << " [label]:share:ID - keyshares with given ID (personal code)" << endl; + ofs << " -v1 - creates CDOC1 version container. Supported only for encryption with certificate." << endl; + ofs << " --genlabel - Generate machine-readable label." << endl; ofs << endl; ofs << "cdoc-tool decrypt ARGUMENTS FILE [OUTPU_DIR]" << endl; - ofs << " Decrypt container using lock specified by label" << endl; + ofs << " Decrypt CDoc container using lock specified by label or number" << endl; ofs << " Supported arguments" << endl; - ofs << " --label LABEL - CDOC container's lock label" << endl; - ofs << " --label_idx INDEX - CDOC container's lock 1-based label index" << endl; - ofs << " --slot SLOT - PKCS11 slot number" << endl; - ofs << " --password PASSWORD - lock's password" << endl; - ofs << " --secret SECRET - secret phrase (AES key)" << endl; - ofs << " --pin PIN - PKCS11 pin" << endl; - ofs << " --key-id - PKCS11 key ID" << endl; - ofs << " --key-label - PKCS11 key label" << endl; + ofs << " --label LABEL - lock label" << endl; + ofs << " --label_idx INDEX - lock number (1-based)" << endl; + ofs << " --pkey PRIVATE_KEY_HEX - hex encoded private key (DER format)" << endl; + ofs << " --pfkey PRIVATE_KEY_HEX - private key from file (DER format)" << endl; + ofs << " --slot SLOT - PKCS11 slot number" << endl; + ofs << " --password PASSWORD - lock's password" << endl; + ofs << " --secret SECRET - secret (AES) key (hex encoded key value)" << endl; + ofs << " --pin PIN - PKCS11 pin" << endl; + ofs << " --key-id - PKCS11 key ID" << endl; + ofs << " --key-label - PKCS11 key label" << endl; ofs << endl; ofs << "cdoc-tool locks FILE" << endl; ofs << endl; @@ -81,11 +83,11 @@ static void print_usage(ostream& ofs) ofs << " Re-encrypts container for different recipient(s)" << endl; ofs << endl; ofs << "Common arguments:" << endl; - ofs << " --library - path to the PKCS11 library to be used" << endl; - ofs << " --server ID URL(S) - specifies a key or share server. The recipient key will be stored in server instead of in the document." << endl; - ofs << " for key server the url is either fetch or send url. For share server it is comma-separated list of share server urls." << endl; - ofs << " --accept FILENAME - specifies an accepted server certificate (in der encoding)" << endl; - ofs << " --log-level LEVEL - set logging level (FATAL, ERROR, WARNING, INFO, DEBUG, TRACE)" << endl; + ofs << " --library - path to the PKCS11 library to be used" << endl; + ofs << " --server ID URL(S) - specifies a key or share server. The recipient key will be stored in server instead of in the document." << endl; + ofs << " for key server the url is either fetch or send url. For share server it is comma-separated list of share server urls." << endl; + ofs << " --accept FILENAME - keyserver certificate file (in der encoding)" << endl; + ofs << " --log-level LEVEL - set logging level (FATAL, ERROR, WARNING, INFO, DEBUG, TRACE)" << endl; } static std::vector diff --git a/cdoc/schema/recipients.fbs b/cdoc/schema/recipients.fbs index 57b0e117..76061314 100644 --- a/cdoc/schema/recipients.fbs +++ b/cdoc/schema/recipients.fbs @@ -31,13 +31,15 @@ union KeyDetailsUnion { } // Elliptic curve type enum for ECCPublicKey recipient +// The clients should not crash with unknown values and try to continue, if possible. enum EllipticCurve:byte { UNKNOWN, - secp384r1 + secp384r1, + secp256r1 } table RsaKeyDetails { - //RSA pub key in DER - RFC8017 RSA Public Key Syntax (A.1.1) https://www.rfc-editor.org/rfc/rfc8017#page-54 + // RSA pub key in DER - RFC8017 RSA Public Key Syntax (A.1.1) https://www.rfc-editor.org/rfc/rfc8017#page-54 recipient_public_key: [ubyte] (required); } @@ -45,8 +47,8 @@ table EccKeyDetails { // Elliptic curve type enum curve: EllipticCurve = UNKNOWN; - //EC pub key in TLS 1.3 format https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.8.2 - //for secp384r1 curve: 0x04 + X 48 coord bytes + Y coord 48 bytes) + // EC pub key in TLS 1.3 format https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.8.2 + // for secp384r1 curve: 0x04 + X 48 coord bytes + Y coord 48 bytes) recipient_public_key: [ubyte] (required); } @@ -90,16 +92,14 @@ table PBKDF2Capsule { kdf_iterations: int32; } -// ShamirKeyShare share. One per key server. +// KeyShare url. Response KeyShare is defined https://github.com/open-eid/cdoc2-openapi/blob/55a0b02adae0d8c61f2589a47555a93e4cf31971/cdoc2-key-shares-openapi.yaml#L144 table KeyShare { //full url of key share, ex https://cdoc2-keyserver.dev.riaint.ee:8443/key-shares/SS0123456789ABCDEF - //url: string (required); - - // or server URL + // is defined as server_base_url // https://cdoc2-keyserver.dev.riaint.ee:8443/ server_base_url: string (required); - //SS0123456789ABCDEF + // and share_id SS0123456789ABCDEF share_id: string (required); } @@ -113,11 +113,16 @@ enum SharesScheme:byte { N_OF_N } -// SymmetricKey that is split between keyservers using Shamir Secret Sharing scheme +// SymmetricKey that is split between cdoc2-shares-servers table KeySharesCapsule { shares: [KeyShare] (required); salt: [ubyte] (required); recipient_type: KeyShareRecipientType = UNKNOWN; shares_scheme: SharesScheme = UNKNOWN; + + // recipient identifier, prefixed with type ("etsi/"). Part after "etsi/" must match subject/serialnumber in recipient certificate + // provided with auth token https://github.com/open-eid/cdoc2-openapi/blob/55a0b02adae0d8c61f2589a47555a93e4cf31971/cdoc2-key-shares-openapi.yaml#L54 + // example recipient_id "etsi/PNOEE-48010010101" where string after "etsi/" is ETSI Semantics Identifier defined in "ETSI EN 319 412-1" + // In future might support other identifiers in format "private/VENDOR/identifier" recipient_id: string (required); } diff --git a/doc/tool.md b/doc/tool.md index 24ade630..5801e3b9 100644 --- a/doc/tool.md +++ b/doc/tool.md @@ -51,7 +51,7 @@ One or more recipients must be specified, each with its own encryption method. | `[label]:cert:CERTIFICATE_HEX` | Encryption public-key from certificate. The certificate must be provided as hex-encoded string | | `[label]:skey:SECRET_KEY_HEX` | Symmetric encryption with AES key. The key must be provided as hex-encoded string | | `[label]:pkey:SECRET_KEY_HEX` | Encryption with public-key. The key must be provided as hex-encoded string | -| `[label]:pfkey:PUB_KEY_FILE` | Encryption with public-key where the key is provided via path to DER file with EC (**secp384r1** curve) public key | +| `[label]:pfkey:PUB_KEY_FILE` | Encryption with public-key. The key (**secp384r1** or **secp256r1**) is read from the DER-encoded file. | | `[label]:pw:PASSWORD` | Encryption with derive key using PWBKDF | | `[label]:p11sk:SLOT:[PIN]:[PKCS11 ID]:[PKCS11 LABEL]` | Encryption with AES key from PKCS11 module | | `[label]:p11pk:SLOT:[PIN]:[PKCS11 ID]:[PKCS11 LABEL]` | Encryption with public key from PKCS11 module | diff --git a/libcdoc.i b/libcdoc.i index b25531ac..aaee761d 100644 --- a/libcdoc.i +++ b/libcdoc.i @@ -178,9 +178,12 @@ Type getType() { return $self->type; } - PKType getPKType() { + Algorithm getAlgorithm() { return $self->pk_type; } + Curve getCurve() { + return $self->ec_type; + } std::string getLabel() { return $self->label; } diff --git a/test/data/ec-secp256r1-cert.der b/test/data/ec-secp256r1-cert.der new file mode 100644 index 00000000..01e8b747 Binary files /dev/null and b/test/data/ec-secp256r1-cert.der differ diff --git a/test/data/ec-secp256r1-priv.der b/test/data/ec-secp256r1-priv.der new file mode 100644 index 00000000..0a659930 Binary files /dev/null and b/test/data/ec-secp256r1-priv.der differ diff --git a/test/data/ec-secp256r1-pub.der b/test/data/ec-secp256r1-pub.der new file mode 100644 index 00000000..6e7bfd7f Binary files /dev/null and b/test/data/ec-secp256r1-pub.der differ diff --git a/test/libcdoc_boost.cpp b/test/libcdoc_boost.cpp index 1401dc39..1768e002 100644 --- a/test/libcdoc_boost.cpp +++ b/test/libcdoc_boost.cpp @@ -54,6 +54,9 @@ constexpr string_view TargetFile("test_data.txt.cdoc"); constexpr string_view ECPrivKeyFile("ec-secp384r1-priv.der"); constexpr string_view ECPubKeyFile("ec-secp384r1-pub.der"); constexpr string_view ECCertFile("ec-secp384r1-cert.der"); +constexpr string_view EC256PrivKeyFile("ec-secp256r1-priv.der"); +constexpr string_view EC256PubKeyFile("ec-secp256r1-pub.der"); +constexpr string_view EC256CertFile("ec-secp256r1-cert.der"); constexpr string_view RSAPrivKeyFile("rsa_2048_priv.der"); constexpr string_view RSAPubKeyFile("rsa_2048_pub.der"); constexpr string_view RSACertFile("rsa_2048_cert.der"); @@ -271,7 +274,7 @@ encryptV2(const std::vector& files, const std::string& container, c } static void -decrypt(const std::vector& files, const std::string& container, const std::string& dir, libcdoc::RcptInfo& rcpt) +decrypt(const std::vector& files, const std::string& container, const std::string& dir, libcdoc::RcptInfo& rcpt, bool remove = true) { libcdoc::ToolConf conf; conf.input_files.push_back(container); @@ -286,7 +289,7 @@ decrypt(const std::vector& files, const std::string& container, con } path = fs::path(container); - if (fs::exists(path)) { + if (remove && fs::exists(path)) { error_code e; fs::remove(path, e); if(e) @@ -294,10 +297,10 @@ decrypt(const std::vector& files, const std::string& container, con } static void -decrypt(const std::vector& files, const std::string& container, const std::string& dir, const std::vector& key) +decrypt(const std::vector& files, const std::string& container, const std::string& dir, const std::vector& key, int idx = 0, bool remove = true) { - libcdoc::RcptInfo rcpt {.type=libcdoc::RcptInfo::LOCK, .secret=key, .lock_idx=0}; - decrypt(files, container, dir, rcpt); + libcdoc::RcptInfo rcpt {.type=libcdoc::RcptInfo::LOCK, .secret=key, .lock_idx=idx}; + decrypt(files, container, dir, rcpt, remove); } static int unicode_to_utf8 (unsigned int uval, uint8_t *d, uint64_t size) @@ -578,7 +581,8 @@ BOOST_FIXTURE_TEST_CASE_WITH_DECOR(EncryptWithECKey, EncryptFixture, * utf::description("Encrypting a file with EC key")) { std::vector rcpts { - {libcdoc::RcptInfo::PKEY, {}, {}, fetchDataFile(ECPubKeyFile)} + {libcdoc::RcptInfo::PKEY, {}, {}, fetchDataFile(ECPubKeyFile)}, + {libcdoc::RcptInfo::PKEY, {}, {}, fetchDataFile(EC256PubKeyFile)} }; encrypt(2, {checkDataFile(sources[0])}, formTargetFile("ECKeyUsage.cdoc"), rcpts); } @@ -586,7 +590,8 @@ BOOST_FIXTURE_TEST_CASE_WITH_DECOR(DecryptWithECKey, DecryptFixture, * utf::depends_on("ECKeyUsage/EncryptWithECKey") * utf::description("Decrypting a file with with EC private key")) { - decrypt({checkDataFile(sources[0])}, checkTargetFile("ECKeyUsage.cdoc"), tmpDataPath.string(), fetchDataFile(ECPrivKeyFile)); + decrypt({checkDataFile(sources[0])}, checkTargetFile("ECKeyUsage.cdoc"), tmpDataPath.string(), fetchDataFile(ECPrivKeyFile), 0, false); + decrypt({checkDataFile(sources[0])}, checkTargetFile("ECKeyUsage.cdoc"), tmpDataPath.string(), fetchDataFile(EC256PrivKeyFile), 1, true); } BOOST_AUTO_TEST_SUITE_END()