Skip to content

Commit

Permalink
Add a generic pcurves instance
Browse files Browse the repository at this point in the history
Unlike the existing system which instantiates everything at compile time based
on the curve parameters, this allows choosing the parameters at runtime. This
can be used for application specific curves, or when it is desirable to not use
the dedicated modules for binary size reasons.

Comparing the three approaches (pcurves, pcurves_generic, BigInt) for some
common EC operations (keygen, ECDSA sign, ECDSA verify, ECDH agreement).  All
numbers are operations/second for the brainpool256r1 curve.

| impl     | keygen   | sign     | verify   | agree    |
| -------- | -------- | -------- | -------- | -------- |
| pcurves  | 20770    | 16191    | 7410     | 5598     |
| generic  | 10208    | 8367     | 4013     | 3077     |
| BigInt   | 10405    | 7686     | 3707     | 2463     |

The advantage of the fully compile-time implementation is clear (and could
become even stronger if/when compilers improve their constexpr support), but
this module has performance at least as good as the old BigInt-based EC and with
a significantly improved situation regarding side channels.

This implementation does not support curves which are cursed:

* Curves with a order of different bitlength than the field
* Curves with primes not congruent to 3 modulo 4
* Curves with a cofactor

One notable cursed curve is P-224 (due to p == 1 mod 4), which is only
supported if the dedicated pcurves_secp224r1 module is included.
  • Loading branch information
randombit committed Jan 15, 2025
1 parent 1afcdf2 commit a4dd13e
Show file tree
Hide file tree
Showing 9 changed files with 2,204 additions and 25 deletions.
25 changes: 25 additions & 0 deletions src/lib/math/mp/mp_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,31 @@ constexpr std::array<W, N> redc_crandall(std::span<const W, 2 * N> z) {
return r;
}

// Extract a WindowBits sized window out of s, depending on offset.
template <size_t WindowBits, typename W, size_t N>
constexpr size_t read_window_bits(std::span<const W, N> words, size_t offset) {
static_assert(WindowBits >= 1 && WindowBits <= 7);

constexpr uint8_t WindowMask = static_cast<uint8_t>(1 << WindowBits) - 1;

constexpr size_t W_bits = sizeof(W) * 8;
const auto bit_shift = offset % W_bits;
const auto word_offset = words.size() - 1 - (offset / W_bits);

const bool single_byte_window = bit_shift <= (W_bits - WindowBits) || word_offset == 0;

const auto w0 = words[word_offset];

if(single_byte_window) {
return (w0 >> bit_shift) & WindowMask;
} else {
// Otherwise we must join two words and extract the result
const auto w1 = words[word_offset - 1];
const auto combined = ((w0 >> bit_shift) | (w1 << (W_bits - bit_shift)));
return combined & WindowMask;
}
}

} // namespace Botan

#endif
15 changes: 15 additions & 0 deletions src/lib/math/pcurves/pcurves.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@

namespace Botan::PCurve {

#if !defined(BOTAN_HAS_PCURVES_GENERIC)
//static
std::shared_ptr<const PrimeOrderCurve> PCurveInstance::from_params(
const BigInt& p, const BigInt& a, const BigInt& b, const BigInt& base_x, const BigInt& base_y, const BigInt& order) {
BOTAN_UNUSED(p, a, b, base_x, base_y, order);
return nullptr;
}
#endif

#if !defined(BOTAN_HAS_PCURVES_SECP192R1)
//static
std::shared_ptr<const PrimeOrderCurve> PCurveInstance::secp192r1() {
Expand Down Expand Up @@ -98,6 +107,12 @@ std::shared_ptr<const PrimeOrderCurve> PCurveInstance::numsp512d1() {
}
#endif

//static
std::shared_ptr<const PrimeOrderCurve> PrimeOrderCurve::from_params(
const BigInt& p, const BigInt& a, const BigInt& b, const BigInt& base_x, const BigInt& base_y, const BigInt& order) {
return PCurveInstance::from_params(p, a, b, base_x, base_y, order);
}

std::shared_ptr<const PrimeOrderCurve> PrimeOrderCurve::from_id(PrimeOrderCurveId id) {
switch(id.code()) {
case PrimeOrderCurveId::secp192r1:
Expand Down
15 changes: 15 additions & 0 deletions src/lib/math/pcurves/pcurves.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

namespace Botan {

class BigInt;
class RandomNumberGenerator;

} // namespace Botan
Expand All @@ -41,6 +42,7 @@ class PrimeOrderCurve {
/// Number of words used to store MaximumByteLength
static const size_t StorageWords = (MaximumByteLength + sizeof(word) - 1) / sizeof(word);

/// @returns nullptr if the curve specified is not available
static std::shared_ptr<const PrimeOrderCurve> from_name(std::string_view name) {
if(auto id = PrimeOrderCurveId::from_string(name)) {
return PrimeOrderCurve::from_id(id.value());
Expand All @@ -49,8 +51,21 @@ class PrimeOrderCurve {
}
}

/// @returns nullptr if the curve specified is not available
static std::shared_ptr<const PrimeOrderCurve> from_id(PrimeOrderCurveId id);

/// @returns nullptr if the parameters seem unsuitable for pcurves
/// for example if the prime is too large
///
/// This function *should* accept the same subset of curves as
/// the EC_Group constructor that accepts BigInts.
static std::shared_ptr<const PrimeOrderCurve> from_params(const BigInt& p,
const BigInt& a,
const BigInt& b,
const BigInt& base_x,
const BigInt& base_y,
const BigInt& order);

typedef std::array<word, StorageWords> StorageUnit;
typedef std::shared_ptr<const PrimeOrderCurve> CurvePtr;

Expand Down
13 changes: 13 additions & 0 deletions src/lib/math/pcurves/pcurves_generic/info.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<defines>
PCURVES_GENERIC -> 20250112
</defines>

<module_info>
name -> "PCurve generic"
</module_info>

<requires>
bigint
numbertheory
mp
</requires>
Loading

0 comments on commit a4dd13e

Please sign in to comment.