diff --git a/.gitignore b/.gitignore index f10d741a..d2bd5631 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ obj-x86_64-linux-gnu/ test/data/tmp/ vcpkg/ vcpkg_installed/ +examples/java/.gradle +examples/java/bin diff --git a/cdoc/CDoc.h b/cdoc/CDoc.h index d64c6368..235d39a3 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 @@ -31,20 +31,6 @@ namespace libcdoc { */ using result_t = int64_t; -/** - * @brief The public key type - */ -enum class PKType : uint8_t { - /** - * Elliptic curve - */ - ECC, - /** - * RSA - */ - RSA -}; - enum { /** * @brief Operation completed successfully @@ -154,6 +140,32 @@ 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, + SECP_521_R1 +}; + + // Logging interface /** diff --git a/cdoc/CDoc1Reader.cpp b/cdoc/CDoc1Reader.cpp index fa9b365a..0c9f18b8 100644 --- a/cdoc/CDoc1Reader.cpp +++ b/cdoc/CDoc1Reader.cpp @@ -83,12 +83,12 @@ CDoc1Reader::getLockForCert(const std::vector& cert) ll.encrypted_fmk.empty()) continue; switch(cc.getAlgorithm()) { - case libcdoc::PKType::RSA: + case libcdoc::Algorithm::RSA: if (ll.getString(Lock::Params::METHOD) == libcdoc::Crypto::RSA_MTH) { return i; } break; - case libcdoc::PKType::ECC: + case libcdoc::Algorithm::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; diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index 28d0fc74..7b3992ac 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -504,44 +504,56 @@ 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 = 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 if (capsule->curve() == EllipticCurve::secp521r1) { + lock.ec_type = Curve::SECP_521_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 = 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 = 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 if (eccDetails->curve() == EllipticCurve::secp521r1) { + lock.ec_type = Curve::SECP_521_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 = 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; @@ -550,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 af07d84a..b2ebb455 100644 --- a/cdoc/CDoc2Writer.cpp +++ b/cdoc/CDoc2Writer.cpp @@ -93,6 +93,18 @@ 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"}}, + {Curve::SECP_521_R1, {NID_secp521r1, cdoc20::recipients::EllipticCurve::secp521r1, "ecc_secp521r1"}} +}; + static flatbuffers::Offset createRSACapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::Recipient& rcpt, const std::vector& encrypted_kek, const std::vector& xor_key) { @@ -130,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, @@ -145,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, @@ -211,7 +223,7 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vectorrandom(kek, libcdoc::CDoc2::KEY_LEN); if (auto err = libcdoc::Crypto::xor_data(xor_key, fmk, kek); err != libcdoc::OK) FAIL("Internal error", err); @@ -238,9 +250,13 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector sharedSecret = libcdoc::Crypto::deriveSharedSecret(ephKey.get(), publicKey.get()); key_material = libcdoc::Crypto::toPublicKeyDer(ephKey.get()); @@ -260,10 +276,9 @@ CDoc2Writer::buildHeader(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) FAIL(network->getLastErrorStr(result), result); - LOG_DBG("Keyserver Id: {}", rcpt.server_id); LOG_DBG("Transaction Id: {}", cinfo.transaction_id); diff --git a/cdoc/CDocCipher.cpp b/cdoc/CDocCipher.cpp index 307dc5b0..9f445895 100644 --- a/cdoc/CDocCipher.cpp +++ b/cdoc/CDocCipher.cpp @@ -18,6 +18,8 @@ #include "CDocCipher.h" #include "CDocReader.h" +#include "CDoc2.h" +#include "Crypto.h" #include "Io.h" #include "Lock.h" #include "NetworkBackend.h" @@ -34,18 +36,19 @@ #include #include #include +#include using namespace std; using namespace libcdoc; 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 { @@ -56,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) { @@ -94,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(); @@ -126,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(); @@ -139,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; @@ -179,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; } @@ -202,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 { @@ -214,7 +215,7 @@ 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; return crypto->p11->sign(dst, algorithm, digest, rcpt_idx); } @@ -255,7 +256,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) { @@ -263,12 +264,10 @@ 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(std::move(label), rcpt.secret, libcdoc::PKType::ECC); - } else if (id == EVP_PKEY_RSA) { - key = libcdoc::Recipient::makePublicKey(std::move(label), rcpt.secret, libcdoc::PKType::RSA); - } + key = libcdoc::Recipient::makePublicKey(std::move(label), rcpt.secret); } LOG_DBG("Creating public key:"); } else if (rcpt.type == RcptInfo::Type::P11_SYMMETRIC) { @@ -300,18 +289,17 @@ fill_recipients_from_rcpt_info(ToolConf& conf, ToolCrypto& crypto, std::vector val; - bool rsa; 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, 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: {}", toHex(val)); if (!conf.servers.empty()) { - key = libcdoc::Recipient::makeServer(std::move(label), std::move(val), rsa ? libcdoc::PKType::RSA : libcdoc::PKType::ECC, conf.servers[0].ID); + key = libcdoc::Recipient::makePublicKey(std::move(label), val, conf.servers[0].ID); } else { - key = libcdoc::Recipient::makePublicKey(std::move(label), std::move(val), rsa ? libcdoc::PKType::RSA : libcdoc::PKType::ECC); + key = libcdoc::Recipient::makePublicKey(std::move(label), val); } } else if (rcpt.type == RcptInfo::Type::PASSWORD) { LOG_DBG("Creating password key:"); @@ -332,8 +320,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); @@ -347,7 +334,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"); @@ -375,8 +362,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); @@ -408,10 +395,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; @@ -429,9 +415,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); } @@ -518,11 +503,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); @@ -540,39 +525,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; @@ -580,7 +562,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"); diff --git a/cdoc/Certificate.cpp b/cdoc/Certificate.cpp index 36091f50..192e563d 100644 --- a/cdoc/Certificate.cpp +++ b/cdoc/Certificate.cpp @@ -148,7 +148,15 @@ Certificate::getPublicKey() const return {}; } -PKType +std::vector +Certificate::getPublicKeyLong() const +{ + if(cert) + return Crypto::toPublicKeyDerLong(X509_get0_pubkey(cert.get())); + return {}; +} + +Algorithm Certificate::getAlgorithm() const { if(!cert) @@ -157,7 +165,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) ? PKType::RSA : PKType::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 45b537d2..69c593fd 100644 --- a/cdoc/Certificate.h +++ b/cdoc/Certificate.h @@ -49,7 +49,8 @@ class Certificate { EIDType getEIDType() const; std::vector getPublicKey() const; - PKType getAlgorithm() const; + std::vector getPublicKeyLong() const; + Algorithm getAlgorithm() const; time_t getNotAfter() const; std::vector getDigest() const; diff --git a/cdoc/Crypto.cpp b/cdoc/Crypto.cpp index 29b09f3e..47238ec2 100644 --- a/cdoc/Crypto.cpp +++ b/cdoc/Crypto.cpp @@ -310,6 +310,17 @@ Crypto::pbkdf2_sha256(const std::vector& pw, const std::vector return key; } +Crypto::EVP_PKEY_ptr +Crypto::fromPublicKeyDer(const std::vector &der) +{ + if (auto key = d2i(der, nullptr)) + return key; + auto key = d2i(der, EVP_PKEY_RSA, nullptr); + if(!key) + LOG_SSL_ERROR("d2i_PublicKey"); + return key; +} + Crypto::EVP_PKEY_ptr Crypto::fromRSAPublicKeyDer(const std::vector &der) { @@ -376,6 +387,19 @@ Crypto::toPublicKeyDer(EVP_PKEY *key) return der; } +std::vector +Crypto::toPublicKeyDerLong(EVP_PKEY *key) +{ + if(!key) return {}; + std::vector der(i2d_PUBKEY(key, nullptr), 0); + if(auto *p = der.data(); i2d_PUBKEY(key, &p) != der.size()) + { + LOG_SSL_ERROR("i2d_PUBKEY"); + der.clear(); + } + return der; +} + std::vector Crypto::random(uint32_t len) { diff --git a/cdoc/Crypto.h b/cdoc/Crypto.h index d60bffe5..0c2bcaa4 100644 --- a/cdoc/Crypto.h +++ b/cdoc/Crypto.h @@ -89,9 +89,11 @@ class Crypto static EVP_PKEY_ptr fromECPublicKeyDer(const std::vector &der, int curveName); /* Create public key from long encoding (0x30...) */ static EVP_PKEY_ptr fromECPublicKeyDer(const std::vector &der); + static EVP_PKEY_ptr fromPublicKeyDer(const std::vector &der); static EVP_PKEY_ptr genECKey(EVP_PKEY *params); static std::vector toPublicKeyDer(EVP_PKEY *key); + static std::vector toPublicKeyDerLong(EVP_PKEY *key); static std::vector random(uint32_t len = 32); static int xor_data(std::vector& dst, const std::vector &lhs, const std::vector &rhs); 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 c46cea21..e9adf3c0 100644 --- a/cdoc/Lock.h +++ b/cdoc/Lock.h @@ -19,7 +19,7 @@ #ifndef __LOCK_H__ #define __LOCK_H__ -#include "CDoc.h" +#include #include #include @@ -168,9 +168,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 */ - PKType pk_type = PKType::ECC; + Algorithm pk_type = Algorithm::ECC; + /** + * @brief The elliptic curve used + */ + Curve ec_type = Curve::SECP_384_R1; /** * @brief the lock label @@ -205,7 +209,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 4d1dc3d9..3ec43618 100644 --- a/cdoc/NetworkBackend.h +++ b/cdoc/NetworkBackend.h @@ -145,7 +145,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", "ecc_secp256r1" or "ecc_secp521r1" * @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 f28ff5c3..d69e7428 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, 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,8 @@ 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; 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..b0084ad8 100644 --- a/cdoc/PKCS11Backend.h +++ b/cdoc/PKCS11Backend.h @@ -106,28 +106,26 @@ 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 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, 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 88cf579a..1077f5a7 100644 --- a/cdoc/Recipient.cpp +++ b/cdoc/Recipient.cpp @@ -27,6 +27,9 @@ #include #include +#include +#include + using namespace std; namespace libcdoc { @@ -41,31 +44,84 @@ Recipient::makeSymmetric(std::string label, int32_t kdf_iter) return rcpt; } -Recipient -Recipient::makePublicKey(std::string label, std::vector public_key, PKType pk_type) +static Recipient +makeRSA(std::string label, std::vector public_key) { if (public_key.empty()) - return {Type::NONE}; - Recipient rcpt(Type::PUBLIC_KEY); + return {}; + Recipient rcpt; + rcpt.type = Recipient::Type::PUBLIC_KEY; rcpt.label = std::move(label); - rcpt.lbl_parts[std::string(CDoc2::Label::TYPE)] = CDoc2::Label::TYPE_PUBLIC_KEY; - rcpt.pk_type = pk_type; - if (pk_type == PKType::ECC && public_key[0] == 0x30) { - // 0x30 identifies SEQUENCE tag in ASN.1 encoding - auto evp = Crypto::fromECPublicKeyDer(public_key); - rcpt.rcpt_key = Crypto::toPublicKeyDer(evp.get()); + rcpt.pk_type = RSA; + rcpt.rcpt_key = std::move(public_key); + return rcpt; +} + +static Recipient +makeECC(std::string label, std::vector public_key, Curve ec_type) +{ + if (public_key.empty()) + return {}; + Recipient rcpt; + rcpt.type = Recipient::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; +} + +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()); +} + +Recipient +Recipient::makePublicKey(std::string label, std::vector public_key, std::string server_id) { + auto pkey = Crypto::fromPublicKeyDer(public_key); + if (!pkey) + return {}; + Recipient rcpt; + int id = EVP_PKEY_get_id(pkey.get()); + if (id == EVP_PKEY_RSA) { + rcpt = makeRSA(label, public_key); } else { - rcpt.rcpt_key = std::move(public_key); + int nid = EVP_PKEY_get_nid(pkey.get()); + switch(nid) { + case NID_secp384r1: + rcpt = makeECC(label, public_key, Curve::SECP_384_R1); + break; + case NID_X9_62_prime256v1: + rcpt = makeECC(label, public_key, Curve::SECP_256_R1); + break; + case NID_secp521r1: + rcpt = makeECC(label, public_key, Curve::SECP_521_R1); + break; + default: + return rcpt; + } + } + if (!server_id.empty()) { + rcpt.server_id = std::move(server_id); + const auto six_months_from_now = std::chrono::system_clock::now() + std::chrono::months(6); + rcpt.expiry_ts = std::chrono::system_clock::to_time_t(six_months_from_now); } return rcpt; } Recipient -Recipient::makePublicKey(const Lock &lock) +Recipient::makePublicKey(const Lock &lock, std::string server_id) { auto params = Lock::parseLabel(lock.label); Recipient rcpt(Type::PUBLIC_KEY); rcpt.pk_type = lock.pk_type; + rcpt.ec_type = lock.ec_type; rcpt.rcpt_key = lock.getBytes(Lock::RCPT_KEY); if (rcpt.rcpt_key.empty()) return {Type::NONE}; @@ -80,20 +136,20 @@ Recipient::makePublicKey(const Lock &lock) params.erase(CDoc2::Label::EXPIRY); } rcpt.lbl_parts = std::move(params); + rcpt.server_id = std::move(server_id); return rcpt; } Recipient -Recipient::makeCertificate(std::string label, std::vector cert) +Recipient::makeCertificate(std::string label, std::vector cert, std::string server_id) { Certificate x509(cert); if (!x509) return {Type::NONE}; - Recipient rcpt(Type::PUBLIC_KEY); - rcpt.label = std::move(label); + Recipient rcpt = makePublicKey(label, x509.getPublicKeyLong()); + if (rcpt.type == Type::NONE) + return rcpt; rcpt.cert = std::move(cert); - rcpt.rcpt_key = x509.getPublicKey(); - rcpt.pk_type = x509.getAlgorithm(); rcpt.expiry_ts = x509.getNotAfter(); if (auto eid = x509.getEIDType(); eid != Certificate::Unknown) { rcpt.lbl_parts = { @@ -110,33 +166,12 @@ Recipient::makeCertificate(std::string label, std::vector cert) {std::string(CDoc2::Label::CERT_SHA1), toHex(x509.getDigest())}, }; } - return rcpt; -} - -Recipient -Recipient::makeServer(std::string label, std::vector public_key, PKType pk_type, std::string server_id) -{ - Recipient rcpt = makePublicKey(std::move(label), std::move(public_key), pk_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); - rcpt.expiry_ts = uint64_t(expiry_ts); - return rcpt; -} - -Recipient -Recipient::makeServer(std::string label, std::vector cert, std::string server_id) -{ - Recipient rcpt = makeCertificate(std::move(label), std::move(cert)); - rcpt.server_id = std::move(server_id); - return rcpt; -} - -Recipient -Recipient::makeServer(const Lock &lock, std::string server_id) -{ - auto rcpt = makePublicKey(lock); - rcpt.server_id = std::move(server_id); + if (!server_id.empty()) { + 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 = std::min(rcpt.expiry_ts, uint64_t(expiry_ts)); + } return rcpt; } diff --git a/cdoc/Recipient.h b/cdoc/Recipient.h index 8374bc4b..31798bcd 100644 --- a/cdoc/Recipient.h +++ b/cdoc/Recipient.h @@ -19,7 +19,7 @@ #ifndef __RECIPIENT_H__ #define __RECIPIENT_H__ -#include "CDoc.h" +#include #include #include @@ -69,7 +69,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. */ @@ -135,19 +136,15 @@ struct CDOC_EXPORT Recipient { bool isKeyShare() const { return type == Type::KEYSHARE; } #endif - /** - * @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 * @return true if the public keys are identical */ bool isTheSameRecipient(const Recipient &other) const; + /** - * @brief A convenience method to check whether a recipient is public key based and has the given keys. + * @brief A convenience method to check whether a recipient is public key based and has the given key. * @param public_key a public key to test * @return true if the public keys are identical */ @@ -155,69 +152,53 @@ 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 + * + * 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) + * @param server_id the keyserver id or empty string for offline capsule * @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, std::string server_id = {}); + /** * @brief Create a new public key based Recipient * @param lock Lock to derive parameters from + * @param server_id the keyserver id or empty string for offline capsule * @return a new Recipient structure */ - static Recipient makePublicKey(const Lock &lock); - /** - * @brief Create a new certificate based Recipient - * @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 makePublicKey(const Lock &lock, 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. + * @brief Create a new certificate based Recipient * - * @param label the label text - * @param public_key the public key value - * @param pk_type the algorithm type (either ECC or RSA) - * @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); - - /** - * @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. + * If the label is empty, a machine-readable label will be created 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 makeServer(std::string label, std::vector cert, std::string server_id); - - /** - * @brief Create a new capsule server based Recipient - * - * @param lock Lock to derive parameters from - * @param server_id the keyserver id + * @param cert the certificate value (der-encoded) + * @param server_id the keyserver id or empty string for offline capsule * @return a new Recipient structure */ - static Recipient makeServer(const Lock &lock, std::string server_id); + static Recipient makeCertificate(std::string label, std::vector cert, std::string server_id = {}); #ifdef HAS_KEYSHARES /** * @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 9caa41fc..52fe681f 100644 --- a/cdoc/cdoc-tool.cpp +++ b/cdoc/cdoc-tool.cpp @@ -53,27 +53,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:PUB_KEY_HEX - hex-encoded public key from command line" << 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 from provided password 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 << " -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 +84,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..1be17c39 100644 --- a/cdoc/schema/recipients.fbs +++ b/cdoc/schema/recipients.fbs @@ -31,13 +31,16 @@ 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, + secp521r1 } 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 +48,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 +93,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 +114,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 b34b421a..40469569 100644 --- a/libcdoc.i +++ b/libcdoc.i @@ -181,9 +181,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/data/ec-secp521r1-cert.der b/test/data/ec-secp521r1-cert.der new file mode 100644 index 00000000..411ae7a5 Binary files /dev/null and b/test/data/ec-secp521r1-cert.der differ diff --git a/test/data/ec-secp521r1-priv.der b/test/data/ec-secp521r1-priv.der new file mode 100644 index 00000000..a60db7b7 Binary files /dev/null and b/test/data/ec-secp521r1-priv.der differ diff --git a/test/data/ec-secp521r1-pub.der b/test/data/ec-secp521r1-pub.der new file mode 100644 index 00000000..323dbb62 Binary files /dev/null and b/test/data/ec-secp521r1-pub.der differ diff --git a/test/libcdoc_boost.cpp b/test/libcdoc_boost.cpp index 904e9986..8573e4f6 100644 --- a/test/libcdoc_boost.cpp +++ b/test/libcdoc_boost.cpp @@ -51,9 +51,15 @@ constexpr string_view SourceFile3("test_data3.txt"); * @brief Encrypted file name. */ 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 EC384PrivKeyFile("ec-secp384r1-priv.der"); +constexpr string_view EC384PubKeyFile("ec-secp384r1-pub.der"); +constexpr string_view EC384CertFile("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 EC521PrivKeyFile("ec-secp521r1-priv.der"); +constexpr string_view EC521PubKeyFile("ec-secp521r1-pub.der"); +constexpr string_view EC521CertFile("ec-secp521r1-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"); @@ -277,7 +283,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); @@ -292,7 +298,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) @@ -301,10 +307,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) @@ -559,60 +565,38 @@ BOOST_FIXTURE_TEST_CASE_WITH_DECOR(DecryptWithPasswordLabelIndex, DecryptFixture } BOOST_AUTO_TEST_SUITE_END() -// CDoc2 AES key +// CDoc2 public/private/symmetric key -BOOST_AUTO_TEST_SUITE(AESKeyUsage) -BOOST_FIXTURE_TEST_CASE_WITH_DECOR(EncryptWithAESKey, EncryptFixture, - * utf::description("Encrypting a file with symmetric AES key")) +BOOST_AUTO_TEST_SUITE(CDoc2KeyUsage) +static constexpr string_view CONTAINER("CDoc2KeyUsage.cdoc"); +BOOST_FIXTURE_TEST_CASE_WITH_DECOR(EncryptWithCDoc2Key, EncryptFixture, + * utf::description("Encrypting a CDoc2 file with a key")) { std::vector rcpts { + {libcdoc::RcptInfo::PKEY, {}, {}, fetchDataFile(EC384PubKeyFile)}, + {libcdoc::RcptInfo::CERT, {}, fetchDataFile(EC384CertFile)}, + {libcdoc::RcptInfo::PKEY, {}, {}, fetchDataFile(EC256PubKeyFile)}, + {libcdoc::RcptInfo::CERT, {}, fetchDataFile(EC256CertFile)}, + {libcdoc::RcptInfo::PKEY, {}, {}, fetchDataFile(EC521PubKeyFile)}, + {libcdoc::RcptInfo::CERT, {}, fetchDataFile(EC521CertFile)}, + {libcdoc::RcptInfo::PKEY, {}, {}, fetchDataFile(RSAPubKeyFile)}, {libcdoc::RcptInfo::SKEY, "AES", {}, libcdoc::fromHex(AESKey)} }; - encrypt(2, {checkDataFile(sources[0])}, formTargetFile("AESKeyUsage.cdoc"), rcpts); + encrypt(2, {checkDataFile(sources[0])}, formTargetFile(CONTAINER), rcpts); } -BOOST_FIXTURE_TEST_CASE_WITH_DECOR(DecryptWithAESKey, DecryptFixture, - * utf::depends_on("AESKeyUsage/EncryptWithAESKey") - * utf::description("Decrypting a file with with symmetric AES key")) -{ - decrypt({checkDataFile(sources[0])}, checkTargetFile("AESKeyUsage.cdoc"), tmpDataPath.string(), libcdoc::fromHex(AESKey)); -} -BOOST_AUTO_TEST_SUITE_END() -// CDoc2 EC public/private key - -BOOST_AUTO_TEST_SUITE(ECKeyUsage) -BOOST_FIXTURE_TEST_CASE_WITH_DECOR(EncryptWithECKey, EncryptFixture, - * utf::description("Encrypting a file with EC key")) -{ - std::vector rcpts { - {libcdoc::RcptInfo::PKEY, {}, {}, fetchDataFile(ECPubKeyFile)} - }; - encrypt(2, {checkDataFile(sources[0])}, formTargetFile("ECKeyUsage.cdoc"), rcpts); -} -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)); -} -BOOST_AUTO_TEST_SUITE_END() - -// CDoc2 RSA public/private key - -BOOST_AUTO_TEST_SUITE(RSAKeyUsage) -BOOST_FIXTURE_TEST_CASE_WITH_DECOR(EncryptWithRSAKey, EncryptFixture, - * utf::description("Encrypting a file with RSA key")) -{ - std::vector rcpts { - {libcdoc::RcptInfo::PKEY, {}, {}, fetchDataFile(RSAPubKeyFile)} - }; - encrypt(2, {checkDataFile(sources[0])}, formTargetFile("RSAKeyUsage.cdoc"), rcpts); -} -BOOST_FIXTURE_TEST_CASE_WITH_DECOR(DecryptWithRSAKey, DecryptFixture, - * utf::depends_on("RSAKeyUsage/EncryptWithRSAKey") - * utf::description("Decrypting a file with with RSA private key")) +BOOST_FIXTURE_TEST_CASE_WITH_DECOR(DecryptWithCDoc2Key, DecryptFixture, + * utf::depends_on("CDoc2KeyUsage/EncryptWithCDoc2Key") + * utf::description("Decrypting a CDoc2 file with a key")) { - decrypt({checkDataFile(sources[0])}, checkTargetFile("RSAKeyUsage.cdoc"), tmpDataPath.string(), fetchDataFile(RSAPrivKeyFile)); + decrypt({checkDataFile(sources[0])}, checkTargetFile(CONTAINER), tmpDataPath.string(), fetchDataFile(EC384PrivKeyFile), 0, false); + decrypt({checkDataFile(sources[0])}, checkTargetFile(CONTAINER), tmpDataPath.string(), fetchDataFile(EC384PrivKeyFile), 1, false); + decrypt({checkDataFile(sources[0])}, checkTargetFile(CONTAINER), tmpDataPath.string(), fetchDataFile(EC256PrivKeyFile), 2, false); + decrypt({checkDataFile(sources[0])}, checkTargetFile(CONTAINER), tmpDataPath.string(), fetchDataFile(EC256PrivKeyFile), 3, false); + decrypt({checkDataFile(sources[0])}, checkTargetFile(CONTAINER), tmpDataPath.string(), fetchDataFile(EC521PrivKeyFile), 4, false); + decrypt({checkDataFile(sources[0])}, checkTargetFile(CONTAINER), tmpDataPath.string(), fetchDataFile(EC521PrivKeyFile), 5, false); + decrypt({checkDataFile(sources[0])}, checkTargetFile(CONTAINER), tmpDataPath.string(), fetchDataFile(RSAPrivKeyFile), 6, false); + decrypt({checkDataFile(sources[0])}, checkTargetFile(CONTAINER), tmpDataPath.string(), libcdoc::fromHex(AESKey), 7, true); } BOOST_AUTO_TEST_SUITE_END() @@ -622,13 +606,13 @@ BOOST_AUTO_TEST_SUITE(CDoc1ECKeySingle) BOOST_FIXTURE_TEST_CASE_WITH_DECOR(EncryptWithECKeyV1, EncryptFixture, * utf::description("Encrypting a file with EC key in CDoc1 format")) { - encryptV1({checkDataFile(sources[0])}, formTargetFile("ECKeyUsageV1.cdoc"), fetchDataFile(ECCertFile)); + encryptV1({checkDataFile(sources[0])}, formTargetFile("ECKeyUsageV1.cdoc"), fetchDataFile(EC384CertFile)); } BOOST_FIXTURE_TEST_CASE_WITH_DECOR(DecryptWithECKeyV1, DecryptFixture, * utf::depends_on("CDoc1ECKeySingle/EncryptWithECKeyV1") * utf::description("Decrypting a file in CDoc1 format with with EC private key")) { - decrypt({checkDataFile(sources[0])}, checkTargetFile("ECKeyUsageV1.cdoc"), tmpDataPath.string(), fetchDataFile(ECPrivKeyFile)); + decrypt({checkDataFile(sources[0])}, checkTargetFile("ECKeyUsageV1.cdoc"), tmpDataPath.string(), fetchDataFile(EC384PrivKeyFile)); } BOOST_AUTO_TEST_SUITE_END() @@ -636,13 +620,13 @@ BOOST_AUTO_TEST_SUITE(CDoc1ECKeyMulti) BOOST_FIXTURE_TEST_CASE_WITH_DECOR(EncryptWithECKeyV1Multi, EncryptFixture, * utf::description("Encrypting multiple files with EC key in CDoc1 format")) { - encryptV1({checkDataFile(sources[0]), checkDataFile(sources[1]), checkDataFile(sources[2])}, formTargetFile("ECKeyUsageV1Multi.cdoc"), fetchDataFile(ECCertFile)); + encryptV1({checkDataFile(sources[0]), checkDataFile(sources[1]), checkDataFile(sources[2])}, formTargetFile("ECKeyUsageV1Multi.cdoc"), fetchDataFile(EC384CertFile)); } BOOST_FIXTURE_TEST_CASE_WITH_DECOR(DecryptWithECKeyV1Multi, DecryptFixture, * utf::depends_on("CDoc1ECKeyMulti/EncryptWithECKeyV1Multi") * utf::description("Decrypting multiple files in CDoc1 format with with EC private key")) { - decrypt({checkDataFile(sources[0]), checkDataFile(sources[1]), checkDataFile(sources[2])}, checkTargetFile("ECKeyUsageV1Multi.cdoc"), tmpDataPath.string(), fetchDataFile(ECPrivKeyFile)); + decrypt({checkDataFile(sources[0]), checkDataFile(sources[1]), checkDataFile(sources[2])}, checkTargetFile("ECKeyUsageV1Multi.cdoc"), tmpDataPath.string(), fetchDataFile(EC384PrivKeyFile)); } BOOST_AUTO_TEST_SUITE_END()