Skip to content

Commit

Permalink
Further refactor crypto code to use rnp::secure_vector instead of raw…
Browse files Browse the repository at this point in the history
… pointers/sizes.
  • Loading branch information
ni4 committed Dec 23, 2024
1 parent 6ef4b14 commit adf0deb
Show file tree
Hide file tree
Showing 22 changed files with 502 additions and 573 deletions.
107 changes: 49 additions & 58 deletions src/lib/crypto/ecdh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@
#include "utils.h"
#include "mem.h"

namespace pgp {
namespace ecdh {
// Produces kek of size kek_len which corresponds to length of wrapping key
static bool
compute_kek(uint8_t * kek,
size_t kek_len,
const std::vector<uint8_t> &other_info,
const pgp::ec::Curve * curve_desc,
const pgp::mpi & ec_pubkey,
const ec::Curve * curve_desc,
const mpi & ec_pubkey,
const rnp::botan::Privkey & ec_prvkey,
const pgp_hash_alg_t hash_alg)
{
Expand Down Expand Up @@ -74,9 +76,9 @@ compute_kek(uint8_t * kek,
}

static bool
ecdh_load_public_key(rnp::botan::Pubkey &pubkey, const pgp::ec::Key &key)
load_public_key(rnp::botan::Pubkey &pubkey, const ec::Key &key)
{
auto curve = pgp::ec::Curve::get(key.curve);
auto curve = ec::Curve::get(key.curve);
if (!curve) {
RNP_LOG("unknown curve");
return false;
Expand Down Expand Up @@ -112,9 +114,9 @@ ecdh_load_public_key(rnp::botan::Pubkey &pubkey, const pgp::ec::Key &key)
}

static bool
ecdh_load_secret_key(rnp::botan::Privkey &seckey, const pgp::ec::Key &key)
load_secret_key(rnp::botan::Privkey &seckey, const ec::Key &key)
{
auto curve = pgp::ec::Curve::get(key.curve);
auto curve = ec::Curve::get(key.curve);
if (!curve) {
return false;
}
Expand All @@ -137,44 +139,40 @@ ecdh_load_secret_key(rnp::botan::Privkey &seckey, const pgp::ec::Key &key)
}

rnp_result_t
ecdh_validate_key(rnp::RNG &rng, const pgp::ec::Key &key, bool secret)
validate_key(rnp::RNG &rng, const ec::Key &key, bool secret)
{
auto curve_desc = pgp::ec::Curve::get(key.curve);
auto curve_desc = ec::Curve::get(key.curve);
if (!curve_desc) {
return RNP_ERROR_NOT_SUPPORTED;
}

rnp::botan::Pubkey bpkey;
if (!ecdh_load_public_key(bpkey, key) ||
botan_pubkey_check_key(bpkey.get(), rng.handle(), 0)) {
if (!load_public_key(bpkey, key) || botan_pubkey_check_key(bpkey.get(), rng.handle(), 0)) {
return RNP_ERROR_BAD_PARAMETERS;
}
if (!secret) {
return RNP_SUCCESS;
}

rnp::botan::Privkey bskey;
if (!ecdh_load_secret_key(bskey, key) ||
if (!load_secret_key(bskey, key) ||
botan_privkey_check_key(bskey.get(), rng.handle(), 0)) {
return RNP_ERROR_BAD_PARAMETERS;
}
return RNP_SUCCESS;
}

rnp_result_t
ecdh_encrypt_pkcs5(rnp::RNG & rng,
pgp_ecdh_encrypted_t & out,
const uint8_t *const in,
size_t in_len,
const pgp::ec::Key & key,
const pgp_fingerprint_t &fingerprint)
encrypt_pkcs5(rnp::RNG & rng,
Encrypted & out,
const rnp::secure_bytes & in,
const ec::Key & key,
const std::vector<uint8_t> &fp)
{
// 'm' is padded to the 8-byte granularity
uint8_t m[MAX_SESSION_KEY_SIZE];
if (!in || (in_len > sizeof(m))) {
if (in.size() > MAX_SESSION_KEY_SIZE) {
return RNP_ERROR_BAD_PARAMETERS;
}
const size_t m_padded_len = ((in_len / 8) + 1) * 8;
const size_t m_padded_len = ((in.size() / 8) + 1) * 8;
// +8 because of AES-wrap adds 8 bytes
if (ECDH_WRAPPED_KEY_SIZE < (m_padded_len + 8)) {
return RNP_ERROR_BAD_PARAMETERS;
Expand All @@ -186,7 +184,7 @@ ecdh_encrypt_pkcs5(rnp::RNG & rng,
return RNP_ERROR_NOT_IMPLEMENTED;
}
#endif
auto curve_desc = pgp::ec::Curve::get(key.curve);
auto curve_desc = ec::Curve::get(key.curve);
if (!curve_desc) {
RNP_LOG("unsupported curve");
return RNP_ERROR_NOT_SUPPORTED;
Expand All @@ -195,7 +193,7 @@ ecdh_encrypt_pkcs5(rnp::RNG & rng,
// See 13.5 of RFC 4880 for definition of other_info size
const size_t kek_len = pgp_key_size(key.key_wrap_alg);
auto other_info =
kdf_other_info_serialize(curve_desc, fingerprint, key.kdf_hash_alg, key.key_wrap_alg);
kdf_other_info_serialize(*curve_desc, fp, key.kdf_hash_alg, key.key_wrap_alg);
assert(other_info.size() == curve_desc->OID.size() + 46);

rnp::botan::Privkey eph_prv_key;
Expand All @@ -217,22 +215,22 @@ ecdh_encrypt_pkcs5(rnp::RNG & rng,
return RNP_ERROR_GENERIC;
}

memcpy(m, in, in_len);
if (!pad_pkcs7(m, m_padded_len, in_len)) {
// Should never happen
return RNP_ERROR_GENERIC;
}
// 'm' is padded to the 8-byte granularity
rnp::secure_bytes m = in;
pad_pkcs7(m, m_padded_len - m.size());

out.mlen = sizeof(out.m);
size_t mlen = ECDH_WRAPPED_KEY_SIZE;
out.m.resize(ECDH_WRAPPED_KEY_SIZE);
#if defined(CRYPTO_BACKEND_BOTAN3)
char name[16];
snprintf(name, sizeof(name), "AES-%zu", 8 * kek_len);
if (botan_nist_kw_enc(name, 0, m, m_padded_len, kek, kek_len, out.m, &out.mlen)) {
if (botan_nist_kw_enc(name, 0, m.data(), m.size(), kek, kek_len, out.m.data(), &mlen)) {
#else
if (botan_key_wrap3394(m, m_padded_len, kek, kek_len, out.m, &out.mlen)) {
if (botan_key_wrap3394(m.data(), m.size(), kek, kek_len, out.m.data(), &mlen)) {
#endif
return RNP_ERROR_GENERIC;
}
out.m.resize(mlen);

/* we need to prepend 0x40 for the x25519 */
if (key.curve == PGP_CURVE_25519) {
Expand All @@ -255,17 +253,16 @@ ecdh_encrypt_pkcs5(rnp::RNG & rng,
}

rnp_result_t
ecdh_decrypt_pkcs5(uint8_t * out,
size_t * out_len,
const pgp_ecdh_encrypted_t &in,
const pgp::ec::Key & key,
const pgp_fingerprint_t & fingerprint)
decrypt_pkcs5(rnp::secure_bytes & out,
const Encrypted & in,
const ec::Key & key,
const std::vector<uint8_t> &fp)
{
if (!out || !out_len || !key.x.bytes()) {
if (!key.x.bytes()) {
return RNP_ERROR_BAD_PARAMETERS;
}

auto curve_desc = pgp::ec::Curve::get(key.curve);
auto curve_desc = ec::Curve::get(key.curve);
if (!curve_desc) {
RNP_LOG("unknown curve");
return RNP_ERROR_NOT_SUPPORTED;
Expand All @@ -274,28 +271,23 @@ ecdh_decrypt_pkcs5(uint8_t * out,
auto wrap_alg = key.key_wrap_alg;
auto kdf_hash = key.kdf_hash_alg;
/* Ensure that AES is used for wrapping */
if ((wrap_alg != PGP_SA_AES_128) && (wrap_alg != PGP_SA_AES_192) &&
(wrap_alg != PGP_SA_AES_256)) {
if (!pgp_is_sa_aes(wrap_alg)) {
RNP_LOG("non-aes wrap algorithm");
return RNP_ERROR_NOT_SUPPORTED;
}

// See 13.5 of RFC 4880 for definition of other_info_size
auto other_info = kdf_other_info_serialize(curve_desc, fingerprint, kdf_hash, wrap_alg);
auto other_info = kdf_other_info_serialize(*curve_desc, fp, kdf_hash, wrap_alg);
assert(other_info.size() == curve_desc->OID.size() + 46);

rnp::botan::Privkey prv_key;
if (!ecdh_load_secret_key(prv_key, key)) {
if (!load_secret_key(prv_key, key)) {
RNP_LOG("failed to load ecdh secret key");
return RNP_ERROR_GENERIC;
}

// Size of SHA-256 or smaller
rnp::secure_array<uint8_t, MAX_SYMM_KEY_SIZE> kek;
rnp::secure_array<uint8_t, MAX_SESSION_KEY_SIZE> deckey;

size_t deckey_len = deckey.size();
size_t offset = 0;
rnp::secure_array<uint8_t, MAX_SYMM_KEY_SIZE> kek;

/* Security: Always return same error code in case compute_kek,
* botan_key_unwrap3394 or unpad_pkcs7 fails
Expand All @@ -305,30 +297,29 @@ ecdh_decrypt_pkcs5(uint8_t * out,
return RNP_ERROR_GENERIC;
}

size_t deckey_len = MAX_SESSION_KEY_SIZE;
out.resize(deckey_len);
#if defined(CRYPTO_BACKEND_BOTAN3)
char name[16];
snprintf(name, sizeof(name), "AES-%zu", 8 * kek_len);
if (botan_nist_kw_dec(
name, 0, in.m, in.mlen, kek.data(), kek_len, deckey.data(), &deckey_len)) {
name, 0, in.m.data(), in.m.size(), kek.data(), kek_len, out.data(), &deckey_len)) {
#else
if (botan_key_unwrap3394(in.m, in.mlen, kek.data(), kek_len, deckey.data(), &deckey_len)) {
if (botan_key_unwrap3394(
in.m.data(), in.m.size(), kek.data(), kek_len, out.data(), &deckey_len)) {
#endif
return RNP_ERROR_GENERIC;
}

if (!unpad_pkcs7(deckey.data(), deckey_len, &offset)) {
out.resize(deckey_len);
if (!unpad_pkcs7(out)) {
return RNP_ERROR_GENERIC;
}

if (*out_len < offset) {
return RNP_ERROR_SHORT_BUFFER;
}

*out_len = offset;
memcpy(out, deckey.data(), *out_len);
return RNP_SUCCESS;
}

} // namespace ecdh
} // namespace pgp

#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC)
rnp_result_t
ecdh_kem_gen_keypair_native(rnp::RNG * rng,
Expand Down
55 changes: 25 additions & 30 deletions src/lib/crypto/ecdh.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,17 @@
#define ECDH_WRAPPED_KEY_SIZE 48

/* Forward declarations */
typedef struct pgp_fingerprint_t pgp_fingerprint_t;

typedef struct pgp_ecdh_encrypted_t {
pgp::mpi p;
uint8_t m[ECDH_WRAPPED_KEY_SIZE];
size_t mlen;
const pgp_fingerprint_t *fp;
} pgp_ecdh_encrypted_t;
namespace pgp {
namespace ecdh {
class Encrypted {
public:
mpi p{};
std::vector<uint8_t> m;
std::vector<uint8_t> fp;
};

rnp_result_t ecdh_validate_key(rnp::RNG &rng, const pgp::ec::Key &key, bool secret);
rnp_result_t validate_key(rnp::RNG &rng, const ec::Key &key, bool secret);

/*
* @brief Sets hash algorithm and key wrapping algo
Expand All @@ -58,37 +59,30 @@ rnp_result_t ecdh_validate_key(rnp::RNG &rng, const pgp::ec::Key &key, bool secr
*
* @returns false if curve is not supported, otherwise true
*/
bool ecdh_set_params(pgp::ec::Key &key, pgp_curve_t curve_id);
bool set_params(ec::Key &key, pgp_curve_t curve_id);

/*
* Encrypts session key with a KEK agreed during ECDH as specified in
* RFC 4880 bis 01, 13.5
*
* @param rng initialized rnp::RNG object
* @param session_key key to be encrypted
* @param session_key_len length of the key buffer
* @param wrapped_key [out] resulting key wrapped in by some AES
* @param out [out] resulting key wrapped in by some AES
* as specified in RFC 3394
* @param wrapped_key_len [out] length of the `wrapped_key' buffer
* Current implementation always produces 48 bytes as key
* is padded with PKCS-5/7
* @param ephemeral_key [out] public ephemeral ECDH key used for key
* agreement (private part). Must be initialized
* @param pubkey public key to be used for encryption
* @param fingerprint fingerprint of the pubkey
* @param in data to be encrypted
* @param key public key to be used for encryption
* @param fp fingerprint of the encrypting key
*
* @return RNP_SUCCESS on success and output parameters are populated
* @return RNP_ERROR_NOT_SUPPORTED unknown curve
* @return RNP_ERROR_BAD_PARAMETERS unexpected input provided
* @return RNP_ERROR_SHORT_BUFFER `wrapped_key_len' to small to store result
* @return RNP_ERROR_GENERIC implementation error
*/
rnp_result_t ecdh_encrypt_pkcs5(rnp::RNG & rng,
pgp_ecdh_encrypted_t & out,
const uint8_t *const in,
size_t in_len,
const pgp::ec::Key & key,
const pgp_fingerprint_t &fingerprint);
rnp_result_t encrypt_pkcs5(rnp::RNG & rng,
Encrypted & out,
const rnp::secure_bytes & in,
const ec::Key & key,
const std::vector<uint8_t> &fp);

/*
* Decrypts session key with a KEK agreed during ECDH as specified in
Expand All @@ -110,11 +104,12 @@ rnp_result_t ecdh_encrypt_pkcs5(rnp::RNG & rng,
* @return RNP_ERROR_SHORT_BUFFER `session_key_len' to small to store result
* @return RNP_ERROR_GENERIC decryption failed or implementation error
*/
rnp_result_t ecdh_decrypt_pkcs5(uint8_t * out,
size_t * out_len,
const pgp_ecdh_encrypted_t &in,
const pgp::ec::Key & key,
const pgp_fingerprint_t & fingerprint);
rnp_result_t decrypt_pkcs5(rnp::secure_bytes & out,
const Encrypted & in,
const ec::Key & key,
const std::vector<uint8_t> &fp);
} // namespace ecdh
} // namespace pgp

#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC)
/* Generate an ECDH key pair in "native" format, i.e.,
Expand Down
Loading

0 comments on commit adf0deb

Please sign in to comment.