Skip to content

Commit

Permalink
Add dedicated types for containing EC key information
Browse files Browse the repository at this point in the history
This is analagous to the DL scheme key types added in #3210, but here
we have to retain the existing classes as we are constrained by SemVer.

The new types contain both our old types (BigInt, EC_Point) and new types
(EC_Scalar, EC_AffinePoint). Eventually the legacy types will be removed,
but we can't do that until the next major version. GH #4027
  • Loading branch information
randombit committed Jul 12, 2024
1 parent 6945e90 commit dbf18fe
Show file tree
Hide file tree
Showing 15 changed files with 303 additions and 99 deletions.
41 changes: 24 additions & 17 deletions src/lib/prov/pkcs11/p11_ecc_key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,24 @@
#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)

#include <botan/ber_dec.h>
#include <botan/internal/ec_key_data.h>
#include <botan/internal/workfactor.h>

namespace Botan::PKCS11 {

namespace {

/// Converts a DER-encoded ANSI X9.62 ECPoint to EC_Point
EC_Point decode_public_point(const secure_vector<uint8_t>& ec_point_data, const EC_Group& group) {
secure_vector<uint8_t> ec_point;
EC_AffinePoint decode_public_point(const EC_Group& group, std::span<const uint8_t> ec_point_data) {
std::vector<uint8_t> ec_point;
BER_Decoder(ec_point_data).decode(ec_point, ASN1_Type::OctetString);
return group.OS2ECP(ec_point);
if(auto pt = EC_AffinePoint::deserialize(group, ec_point)) {
return *pt;
} else {
throw Decoding_Error("Encoded EC public point was invalid");
}
}

} // namespace

EC_PublicKeyGenerationProperties::EC_PublicKeyGenerationProperties(const std::vector<uint8_t>& ec_params) :
Expand All @@ -38,20 +46,19 @@ EC_PublicKeyImportProperties::EC_PublicKeyImportProperties(const std::vector<uin
}

PKCS11_EC_PublicKey::PKCS11_EC_PublicKey(Session& session, ObjectHandle handle) : Object(session, handle) {
secure_vector<uint8_t> ec_parameters = get_attribute_value(AttributeType::EcParams);
m_domain_params = EC_Group(unlock(ec_parameters));
m_public_key = decode_public_point(get_attribute_value(AttributeType::EcPoint), m_domain_params);
m_domain_encoding = EC_Group_Encoding::NamedCurve;
auto ec_parameters = get_attribute_value(AttributeType::EcParams);
auto pt_bytes = get_attribute_value(AttributeType::EcPoint);

EC_Group group(ec_parameters);
auto pt = decode_public_point(group, pt_bytes);
m_public_key = std::make_shared<EC_PublicKey_Data>(group, pt);
}

PKCS11_EC_PublicKey::PKCS11_EC_PublicKey(Session& session, const EC_PublicKeyImportProperties& props) :
Object(session, props) {
m_domain_params = EC_Group(props.ec_params());

secure_vector<uint8_t> ec_point;
BER_Decoder(props.ec_point()).decode(ec_point, ASN1_Type::OctetString);
m_public_key = m_domain_params.OS2ECP(ec_point);
m_domain_encoding = EC_Group_Encoding::NamedCurve;
EC_Group group(props.ec_params());
auto pt = decode_public_point(group, props.ec_point());
m_public_key = std::make_shared<EC_PublicKey_Data>(group, pt);
}

EC_PrivateKeyImportProperties::EC_PrivateKeyImportProperties(const std::vector<uint8_t>& ec_params,
Expand All @@ -61,8 +68,7 @@ EC_PrivateKeyImportProperties::EC_PrivateKeyImportProperties(const std::vector<u
add_binary(AttributeType::Value, m_value.serialize());
}

PKCS11_EC_PrivateKey::PKCS11_EC_PrivateKey(Session& session, ObjectHandle handle) :
Object(session, handle), m_domain_params(), m_public_key() {
PKCS11_EC_PrivateKey::PKCS11_EC_PrivateKey(Session& session, ObjectHandle handle) : Object(session, handle) {
secure_vector<uint8_t> ec_parameters = get_attribute_value(AttributeType::EcParams);
m_domain_params = EC_Group(unlock(ec_parameters));
}
Expand Down Expand Up @@ -96,9 +102,10 @@ PKCS11_EC_PrivateKey::PKCS11_EC_PrivateKey(Session& session,
&priv_key_handle);

this->reset_handle(priv_key_handle);

Object public_key(session, pub_key_handle);
m_public_key = decode_public_point(public_key.get_attribute_value(AttributeType::EcPoint), m_domain_params);

auto pt_bytes = public_key.get_attribute_value(AttributeType::EcPoint);
m_public_key = decode_public_point(m_domain_params, pt_bytes).to_legacy_point();
}

size_t PKCS11_EC_PrivateKey::key_length() const {
Expand Down
7 changes: 5 additions & 2 deletions src/lib/pubkey/ec_group/ec_apoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,11 @@ EC_AffinePoint EC_AffinePoint::hash_to_curve_nu(const EC_Group& group,
EC_AffinePoint::~EC_AffinePoint() = default;

std::optional<EC_AffinePoint> EC_AffinePoint::deserialize(const EC_Group& group, std::span<const uint8_t> bytes) {
auto pt = group._data()->point_deserialize(bytes);
return EC_AffinePoint(std::move(pt));
if(auto pt = group._data()->point_deserialize(bytes)) {
return EC_AffinePoint(std::move(pt));
} else {
return {};
}
}

EC_AffinePoint EC_AffinePoint::g_mul(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector<BigInt>& ws) {
Expand Down
12 changes: 9 additions & 3 deletions src/lib/pubkey/ec_group/ec_group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,15 @@ std::pair<std::shared_ptr<EC_Group_Data>, bool> EC_Group::BER_decode_EC_group(st
BER_Object obj = ber.get_next_object();

if(obj.type() == ASN1_Type::ObjectId) {
OID dom_par_oid;
BER_Decoder(bits).decode(dom_par_oid);
return std::make_pair(ec_group_data().lookup(dom_par_oid), false);
OID oid;
BER_Decoder(bits).decode(oid);

auto data = ec_group_data().lookup(oid);
if(!data) {
throw Decoding_Error(fmt("Unknown namedCurve OID '{}'", oid.to_string()));
}

return std::make_pair(data, false);
}

if(obj.type() == ASN1_Type::Sequence) {
Expand Down
13 changes: 13 additions & 0 deletions src/lib/pubkey/ec_group/ec_scalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ EC_Scalar EC_Scalar::from_bigint(const EC_Group& group, const BigInt& bn) {
return EC_Scalar(group._data()->scalar_from_bigint(bn));
}

BigInt EC_Scalar::to_bigint() const {
secure_vector<uint8_t> bytes(m_scalar->bytes());
m_scalar->serialize_to(bytes);
return BigInt::from_bytes(bytes);
}

EC_Scalar EC_Scalar::gk_x_mod_order(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector<BigInt>& ws) {
const auto& group = scalar._inner().group();
return EC_Scalar(group->gk_x_mod_order(scalar.inner(), rng, ws));
Expand Down Expand Up @@ -106,6 +112,13 @@ std::optional<EC_Scalar> EC_Scalar::deserialize(const EC_Group& group, std::span
}
}

EC_Scalar::EC_Scalar(const EC_Group& group, std::span<const uint8_t> bytes) {
m_scalar = group._data()->scalar_deserialize(bytes);
if(!m_scalar) {
throw Decoding_Error("Failed to deserialize elliptic curve scalar");
}
}

bool EC_Scalar::is_zero() const {
return inner().is_zero();
}
Expand Down
18 changes: 18 additions & 0 deletions src/lib/pubkey/ec_group/ec_scalar.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ class BOTAN_UNSTABLE_API EC_Scalar final {
*/
static EC_Scalar random(const EC_Group& group, RandomNumberGenerator& rng);

/**
* Convert a bytestring to an EC_Scalar
*
* This is similar to deserialize but instead of returning nullopt if the input
* is invalid, it will throw an exception.
*/
EC_Scalar(const EC_Group& group, std::span<const uint8_t> bytes);

/**
* Return the scalar value 1
*/
Expand Down Expand Up @@ -180,6 +188,16 @@ class BOTAN_UNSTABLE_API EC_Scalar final {
*/
bool is_eq(const EC_Scalar& x) const;

/**
* Convert *this to a BigInt
*/
BigInt to_bigint() const;

/**
* Return the underlying group
*/
const EC_Group& group() const;

friend EC_Scalar operator+(const EC_Scalar& x, const EC_Scalar& y) { return x.add(y); }

friend EC_Scalar operator-(const EC_Scalar& x, const EC_Scalar& y) { return x.sub(y); }
Expand Down
56 changes: 56 additions & 0 deletions src/lib/pubkey/ecc_key/ec_key_data.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* (C) 2024 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/internal/ec_key_data.h>

namespace Botan {

EC_PublicKey_Data::EC_PublicKey_Data(const EC_Group& group, std::span<const uint8_t> bytes) :
m_group(group), m_point(EC_AffinePoint(group, bytes)), m_legacy_point(m_point.to_legacy_point()) {}

EC_PrivateKey_Data::EC_PrivateKey_Data(const EC_Group& group, RandomNumberGenerator& rng) :
m_group(group), m_scalar(EC_Scalar::random(m_group, rng)), m_legacy_x(m_scalar.to_bigint()) {}

EC_PrivateKey_Data::EC_PrivateKey_Data(const EC_Group& group, const BigInt& x) :
m_group(group), m_scalar(EC_Scalar::from_bigint(m_group, x)), m_legacy_x(m_scalar.to_bigint()) {}

EC_PrivateKey_Data::EC_PrivateKey_Data(const EC_Group& group, std::span<const uint8_t> bytes) :
m_group(group), m_scalar(m_group, bytes), m_legacy_x(m_scalar.to_bigint()) {}

std::shared_ptr<EC_PublicKey_Data> EC_PrivateKey_Data::public_key(RandomNumberGenerator& rng,
bool with_modular_inverse) const {
auto public_point = [&]() {
std::vector<BigInt> ws;
if(with_modular_inverse) {
return EC_AffinePoint::g_mul(m_scalar.invert(), rng, ws);
} else {
return EC_AffinePoint::g_mul(m_scalar, rng, ws);
}
};

return std::make_shared<EC_PublicKey_Data>(m_group, public_point());
}

std::shared_ptr<EC_PublicKey_Data> EC_PrivateKey_Data::public_key(bool with_modular_inverse) const {
// TODO this is using unblinded Montgomery ladder since we don't have an RNG
// Probably need to handle Null_RNG in g_mul
auto public_point = [&]() {
std::vector<BigInt> ws;
if(with_modular_inverse) {
return EC_AffinePoint(group(), group().get_base_point() * m_scalar.invert().to_bigint());
} else {
return EC_AffinePoint(group(), group().get_base_point() * m_scalar.to_bigint());
}
};

return std::make_shared<EC_PublicKey_Data>(m_group, public_point());
}

void EC_PrivateKey_Data::serialize_to(std::span<uint8_t> output) const {
m_scalar.serialize_to(output);
}

} // namespace Botan
76 changes: 76 additions & 0 deletions src/lib/pubkey/ecc_key/ec_key_data.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* (C) 2024 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#ifndef BOTAN_EC_KEY_DATA_H_
#define BOTAN_EC_KEY_DATA_H_

#include <botan/ec_apoint.h>
#include <botan/ec_group.h>
#include <botan/ec_scalar.h>

#include <botan/bigint.h>
#include <botan/ec_point.h>

namespace Botan {

class RandomNumberGenerator;

class EC_PublicKey_Data final {
public:
EC_PublicKey_Data(EC_Group group, EC_AffinePoint pt) :
m_group(std::move(group)), m_point(std::move(pt)), m_legacy_point(m_point.to_legacy_point()) {}

EC_PublicKey_Data(const EC_Group& group, std::span<const uint8_t> bytes);

const EC_Group& group() const { return m_group; }

const EC_AffinePoint& public_key() const { return m_point; }

const EC_Point& legacy_point() const { return m_legacy_point; }

private:
EC_Group m_group;
EC_AffinePoint m_point;
EC_Point m_legacy_point;
};

class EC_PrivateKey_Data final {
public:
EC_PrivateKey_Data(const EC_Group& group, RandomNumberGenerator& rng);

EC_PrivateKey_Data(const EC_Group& group, const BigInt& x);

EC_PrivateKey_Data(const EC_Group& group, std::span<const uint8_t> bytes);

std::shared_ptr<EC_PublicKey_Data> public_key(RandomNumberGenerator& rng, bool with_modular_inverse) const;

std::shared_ptr<EC_PublicKey_Data> public_key(bool with_modular_inverse) const;

void serialize_to(std::span<uint8_t> output) const;

template <typename T>
T serialize() const {
T bytes(this->group().get_order_bytes());
this->serialize_to(bytes);
return bytes;
}

const EC_Group& group() const { return m_group; }

const EC_Scalar& private_key() const { return m_scalar; }

const BigInt& legacy_bigint() const { return m_legacy_x; }

private:
EC_Group m_group;

EC_Scalar m_scalar;
BigInt m_legacy_x;
};

} // namespace Botan

#endif
Loading

0 comments on commit dbf18fe

Please sign in to comment.