Skip to content

Commit

Permalink
Refactor EC crypto code to be closer to C++ style.
Browse files Browse the repository at this point in the history
  • Loading branch information
ni4 committed Nov 13, 2024
1 parent 4c5c9c2 commit 9f78304
Show file tree
Hide file tree
Showing 28 changed files with 817 additions and 964 deletions.
37 changes: 35 additions & 2 deletions src/lib/crypto/botan_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,17 @@ class bn {
botan_mp_init(&bn_);
}

bn(const pgp::mpi &val)
bn(const uint8_t *val, size_t len)
{
botan_mp_init(&bn_);
if (bn_ && botan_mp_from_bin(bn_, val.mpi, val.len)) {
if (bn_ && botan_mp_from_bin(bn_, val, len)) {
botan_mp_destroy(bn_);
bn_ = NULL;
}
}

bn(const pgp::mpi &val) : bn(val.mpi, val.len){};

~bn()
{
botan_mp_destroy(bn_);
Expand Down Expand Up @@ -78,6 +80,15 @@ class bn {
return botan_mp_to_bin(bn_, val.mpi) == 0;
}

bool
bin(uint8_t b[]) const noexcept

Check notice

Code scanning / CodeQL

No raw arrays in interfaces Note

Raw arrays should not be used in interfaces. A container class should be used instead.
{
if (!b) {
return false;
}
return botan_mp_to_bin(bn_, b) == 0;
}

size_t
bytes() const noexcept
{
Expand Down Expand Up @@ -121,6 +132,12 @@ class Privkey {
{
return key_;
};

const botan_privkey_t &
get() const noexcept
{
return key_;
};
};

namespace op {
Expand Down Expand Up @@ -188,6 +205,22 @@ class Sign {
};
};

class KeyAgreement {
botan_pk_op_ka_t op_;

public:
KeyAgreement() : op_(NULL){};
~KeyAgreement()
{
botan_pk_op_key_agreement_destroy(op_);
}
botan_pk_op_ka_t &
get() noexcept
{
return op_;
};
};

} // namespace op
} // namespace botan
} // namespace rnp
Expand Down
143 changes: 56 additions & 87 deletions src/lib/crypto/ec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,19 @@
#include "utils.h"
#include "mem.h"
#include "bn.h"
#include "botan_utils.hpp"
#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC)
#include "x25519.h"
#include "ed25519.h"
#include "botan_utils.hpp"
#include "botan/bigint.h"
#include "botan/ecdh.h"
#include <cassert>
#endif

namespace pgp {
namespace ec {

static id_str_pair ec_algo_to_botan[] = {
{PGP_PKA_ECDH, "ECDH"},
{PGP_PKA_ECDSA, "ECDSA"},
Expand All @@ -48,123 +53,91 @@ static id_str_pair ec_algo_to_botan[] = {
};

rnp_result_t
x25519_generate(rnp::RNG *rng, pgp_ec_key_t *key)
Key::generate_x25519(rnp::RNG &rng)
{
botan_privkey_t pr_key = NULL;
botan_pubkey_t pu_key = NULL;
rnp_result_t ret = RNP_ERROR_KEY_GENERATION;

rnp::secure_array<uint8_t, 32> keyle;

if (botan_privkey_create(&pr_key, "Curve25519", "", rng->handle())) {
goto end;
rnp::botan::Privkey pr_key;
if (botan_privkey_create(&pr_key.get(), "Curve25519", "", rng.handle())) {
return RNP_ERROR_KEY_GENERATION;
}

if (botan_privkey_export_pubkey(&pu_key, pr_key)) {
goto end;
rnp::botan::Pubkey pu_key;
if (botan_privkey_export_pubkey(&pu_key.get(), pr_key.get())) {
return RNP_ERROR_KEY_GENERATION;
}

/* botan returns key in little-endian, while mpi is big-endian */
if (botan_privkey_x25519_get_privkey(pr_key, keyle.data())) {
goto end;
rnp::secure_array<uint8_t, 32> keyle;
if (botan_privkey_x25519_get_privkey(pr_key.get(), keyle.data())) {
return RNP_ERROR_KEY_GENERATION;
}
for (int i = 0; i < 32; i++) {
key->x.mpi[31 - i] = keyle[i];
x.mpi[31 - i] = keyle[i];
}
key->x.len = 32;
x.len = 32;
/* botan doesn't tweak secret key bits, so we should do that here */
if (!x25519_tweak_bits(*key)) {
goto end;
if (!x25519_tweak_bits(*this)) {
return RNP_ERROR_KEY_GENERATION;
}

if (botan_pubkey_x25519_get_pubkey(pu_key, &key->p.mpi[1])) {
goto end;
if (botan_pubkey_x25519_get_pubkey(pu_key.get(), &p.mpi[1])) {
return RNP_ERROR_KEY_GENERATION;
}
key->p.len = 33;
key->p.mpi[0] = 0x40;

ret = RNP_SUCCESS;
end:
botan_privkey_destroy(pr_key);
botan_pubkey_destroy(pu_key);
return ret;
p.len = 33;
p.mpi[0] = 0x40;
return RNP_SUCCESS;
}

rnp_result_t
ec_generate(rnp::RNG * rng,
pgp_ec_key_t * key,
const pgp_pubkey_alg_t alg_id,
const pgp_curve_t curve)
Key::generate(rnp::RNG &rng, const pgp_pubkey_alg_t alg_id, const pgp_curve_t curve)
{
/**
* Keeps "0x04 || x || y"
* \see 13.2. ECDSA, ECDH, SM2 Conversion Primitives
*
* P-521 is biggest supported curve
*/
botan_privkey_t pr_key = NULL;
botan_pubkey_t pu_key = NULL;
bignum_t * px = NULL;
bignum_t * py = NULL;
bignum_t * x = NULL;
rnp_result_t ret = RNP_ERROR_KEY_GENERATION;
size_t filed_byte_size = 0;

if (!alg_allows_curve(alg_id, curve)) {
if (!Curve::alg_allows(alg_id, curve)) {
return RNP_ERROR_BAD_PARAMETERS;
}

const char *ec_algo = id_str_pair::lookup(ec_algo_to_botan, alg_id, NULL);
assert(ec_algo);
auto ec_desc = get_curve_desc(curve);
auto ec_desc = Curve::get(curve);
if (!ec_desc) {
ret = RNP_ERROR_BAD_PARAMETERS;
goto end;
return RNP_ERROR_BAD_PARAMETERS;
}
filed_byte_size = BITS_TO_BYTES(ec_desc->bitlen);

// at this point it must succeed
if (botan_privkey_create(&pr_key, ec_algo, ec_desc->botan_name, rng->handle())) {
goto end;
rnp::botan::Privkey pr_key;
if (botan_privkey_create(&pr_key.get(), ec_algo, ec_desc->botan_name, rng.handle())) {
return RNP_ERROR_KEY_GENERATION;
}

if (botan_privkey_export_pubkey(&pu_key, pr_key)) {
goto end;
rnp::botan::Pubkey pu_key;
if (botan_privkey_export_pubkey(&pu_key.get(), pr_key.get())) {
return RNP_ERROR_KEY_GENERATION;
}

// Crash if seckey is null. It's clean and easy to debug design
px = bn_new();
py = bn_new();
x = bn_new();
rnp::bn px;
rnp::bn py;
rnp::bn bx;

if (!px || !py || !x) {
if (!px || !py || !bx) {
RNP_LOG("Allocation failed");
ret = RNP_ERROR_OUT_OF_MEMORY;
goto end;
return RNP_ERROR_OUT_OF_MEMORY;
}

if (botan_pubkey_get_field(BN_HANDLE_PTR(px), pu_key, "public_x")) {
goto end;
if (botan_pubkey_get_field(px.get(), pu_key.get(), "public_x") ||
botan_pubkey_get_field(py.get(), pu_key.get(), "public_y") ||
botan_privkey_get_field(bx.get(), pr_key.get(), "x")) {
return RNP_ERROR_KEY_GENERATION;
}

if (botan_pubkey_get_field(BN_HANDLE_PTR(py), pu_key, "public_y")) {
goto end;
}

if (botan_privkey_get_field(BN_HANDLE_PTR(x), pr_key, "x")) {
goto end;
}

size_t x_bytes;
size_t y_bytes;
x_bytes = bn_num_bytes(*px);
y_bytes = bn_num_bytes(*py);

// Safety check
if ((x_bytes > filed_byte_size) || (y_bytes > filed_byte_size)) {
size_t field_size = BITS_TO_BYTES(ec_desc->bitlen);
if ((px.bytes() > field_size) || (py.bytes() > field_size)) {
RNP_LOG("Key generation failed");
ret = RNP_ERROR_BAD_PARAMETERS;
goto end;
return RNP_ERROR_BAD_PARAMETERS;
}

/*
Expand All @@ -176,23 +149,19 @@ ec_generate(rnp::RNG * rng,
* Note: Generated pk/sk may not always have exact number of bytes
* which is important when converting to octet-string
*/
memset(key->p.mpi, 0, sizeof(key->p.mpi));
key->p.mpi[0] = 0x04;
bn_bn2bin(px, &key->p.mpi[1 + filed_byte_size - x_bytes]);
bn_bn2bin(py, &key->p.mpi[1 + filed_byte_size + (filed_byte_size - y_bytes)]);
key->p.len = 2 * filed_byte_size + 1;
memset(p.mpi, 0, sizeof(p.mpi));
p.mpi[0] = 0x04;
px.bin(&p.mpi[1 + field_size - px.bytes()]);
py.bin(&p.mpi[1 + 2 * field_size - py.bytes()]);
p.len = 2 * field_size + 1;
/* secret key value */
bn2mpi(x, &key->x);
ret = RNP_SUCCESS;
end:
botan_privkey_destroy(pr_key);
botan_pubkey_destroy(pu_key);
bn_free(px);
bn_free(py);
bn_free(x);
return ret;
bx.mpi(x);
return RNP_SUCCESS;
}

} // namespace ec
} // namespace pgp

#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC)
static bool
is_generic_prime_curve(pgp_curve_t curve)
Expand Down
Loading

0 comments on commit 9f78304

Please sign in to comment.