diff --git a/cdoc/CDoc.h b/cdoc/CDoc.h index d64c6368..a5fa2255 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 @@ -154,6 +154,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 436c405a..e9305d74 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -508,44 +508,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; @@ -554,8 +566,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 eb29fee3..97d858a8 100644 --- a/cdoc/CDoc2Writer.cpp +++ b/cdoc/CDoc2Writer.cpp @@ -94,6 +94,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) { @@ -131,7 +143,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 +158,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, @@ -212,7 +224,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) FAIL("Internal error", libcdoc::CRYPTO_ERROR); @@ -239,9 +251,13 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector sharedSecret = libcdoc::Crypto::deriveSharedSecret(ephKey.get(), publicKey.get()); key_material = libcdoc::Crypto::toPublicKeyDer(ephKey.get()); @@ -261,10 +277,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 22ccd53f..7fd7b868 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" @@ -35,20 +37,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 +60,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 +98,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 +130,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 +143,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 +182,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 +205,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 +216,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 +267,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 +275,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::PKType::ECC); - } else if (id == EVP_PKEY_RSA) { - key = libcdoc::Recipient::makePublicKey(label, rcpt.secret, libcdoc::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:"); @@ -303,18 +339,18 @@ fill_recipients_from_rcpt_info(ToolConf& conf, ToolCrypto& crypto, 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::PKType::RSA : libcdoc::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::PKType::RSA : libcdoc::PKType::ECC); + key = libcdoc::Recipient::makePublicKey(label, val, algo); } } else if (rcpt.type == RcptInfo::Type::PASSWORD) { LOG_DBG("Creating password key:"); @@ -335,8 +371,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); @@ -350,7 +385,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"); @@ -378,8 +413,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); @@ -411,10 +446,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; @@ -432,9 +466,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); } @@ -521,11 +554,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); @@ -543,39 +576,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; @@ -583,7 +613,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"); @@ -707,35 +737,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/Certificate.cpp b/cdoc/Certificate.cpp index 36091f50..1c047a41 100644 --- a/cdoc/Certificate.cpp +++ b/cdoc/Certificate.cpp @@ -148,7 +148,7 @@ Certificate::getPublicKey() const return {}; } -PKType +Algorithm Certificate::getAlgorithm() const { if(!cert) @@ -157,7 +157,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..316369ca 100644 --- a/cdoc/Certificate.h +++ b/cdoc/Certificate.h @@ -49,7 +49,7 @@ class Certificate { EIDType getEIDType() const; std::vector getPublicKey() const; - PKType getAlgorithm() const; + Algorithm getAlgorithm() const; time_t getNotAfter() const; std::vector getDigest() const; 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..8410f195 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" 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 49e7f653..19fb6bcb 100644 --- a/cdoc/Recipient.cpp +++ b/cdoc/Recipient.cpp @@ -42,21 +42,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.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()); - } 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; } @@ -66,6 +74,7 @@ Recipient::makePublicKey(const Lock &lock) 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}; @@ -114,9 +123,20 @@ Recipient::makeCertificate(std::string label, std::vector cert) } 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 8374bc4b..c71ab2c1 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,11 +136,6 @@ 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 @@ -155,19 +151,45 @@ 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 public key based Recipient * @param lock Lock to derive parameters from @@ -176,14 +198,20 @@ struct CDOC_EXPORT Recipient { static Recipient makePublicKey(const Lock &lock); /** * @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 @@ -192,10 +220,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 +256,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 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/libcdoc_boost.cpp b/test/libcdoc_boost.cpp index dc332a98..061ee877 100644 --- a/test/libcdoc_boost.cpp +++ b/test/libcdoc_boost.cpp @@ -54,6 +54,12 @@ 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 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"); @@ -271,7 +277,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 +292,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 +300,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) @@ -552,60 +558,32 @@ 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(ECPubKeyFile)}, + {libcdoc::RcptInfo::PKEY, {}, {}, fetchDataFile(EC256PubKeyFile)}, + {libcdoc::RcptInfo::PKEY, {}, {}, fetchDataFile(EC521PubKeyFile)}, + {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(ECPrivKeyFile), 0, false); + decrypt({checkDataFile(sources[0])}, checkTargetFile(CONTAINER), tmpDataPath.string(), fetchDataFile(EC256PrivKeyFile), 1, false); + decrypt({checkDataFile(sources[0])}, checkTargetFile(CONTAINER), tmpDataPath.string(), fetchDataFile(EC521PrivKeyFile), 2, false); + decrypt({checkDataFile(sources[0])}, checkTargetFile(CONTAINER), tmpDataPath.string(), fetchDataFile(RSAPrivKeyFile), 3, false); + decrypt({checkDataFile(sources[0])}, checkTargetFile(CONTAINER), tmpDataPath.string(), libcdoc::fromHex(AESKey), 4, true); } BOOST_AUTO_TEST_SUITE_END()