Skip to content

Commit

Permalink
Add base point multiplication interface
Browse files Browse the repository at this point in the history
Just for testing right now
  • Loading branch information
randombit committed Apr 7, 2024
1 parent 4cd59f7 commit cd40575
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 10 deletions.
13 changes: 13 additions & 0 deletions src/lib/pubkey/pcurves/pcurves.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,17 @@ std::vector<uint8_t> hash_to_curve(PrimeOrderCurveId curve,
}
}

std::vector<uint8_t> mul_by_g(PrimeOrderCurveId curve, std::span<const uint8_t> scalar_bytes) {
switch(curve.code()) {
case PrimeOrderCurveId::P256:
if(auto scalar = P256::Scalar::deserialize(scalar_bytes)) {
return P256::MulByG(*scalar).to_affine().serialize_to_vec();
} else {
throw Invalid_Argument("Invalid scalar");
}
default:
throw Not_Implemented("Point mul not implemented for this curve");
}
}

} // namespace Botan::PCurve
2 changes: 2 additions & 0 deletions src/lib/pubkey/pcurves/pcurves.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ std::vector<uint8_t> hash_to_curve(PrimeOrderCurveId curve,
std::span<const uint8_t> input,
std::span<const uint8_t> domain_sep);

std::vector<uint8_t> BOTAN_TEST_API mul_by_g(PrimeOrderCurveId curve, std::span<const uint8_t> scalar);

} // namespace Botan::PCurve

#endif
82 changes: 72 additions & 10 deletions src/lib/pubkey/pcurves/pcurves_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,15 @@ class MontgomeryInteger {
}

// Returns nullopt if the input is an encoding greater than or equal P
constexpr static std::optional<Self> deserialize(std::span<const uint8_t, Self::BYTES> bytes) {
const auto words = bytes_to_words<W, N, BYTES>(bytes);
constexpr static std::optional<Self> deserialize(std::span<const uint8_t> bytes) {
// We could allow either short inputs or longer zero padded
// inputs here, however it seems best to avoid non-canonical
// representations unless required
if(bytes.size() != Self::BYTES) {
return {};
}

const auto words = bytes_to_words<W, N, BYTES>(&bytes[0]);

if(!bigint_ct_is_lt(words.data(), N, Self::P.data(), N).as_bool()) {
return {};
Expand Down Expand Up @@ -335,6 +342,11 @@ class AffineCurvePoint {

constexpr Self negate() const { return Self(m_x, m_y.negate()); }

std::vector<uint8_t> serialize_to_vec() const {
const auto b = this->serialize();
return std::vector(b.begin(), b.end());
}

constexpr std::array<uint8_t, Self::BYTES> serialize() const {
std::array<uint8_t, Self::BYTES> r = {};
BufferStuffer pack(r);
Expand Down Expand Up @@ -430,8 +442,54 @@ class ProjectiveCurvePoint {
}

constexpr static Self add_mixed(const Self& a, const AffinePoint& b) {
// TODO use proper mixed addition formula
return Self::add(a, Self::from_affine(b));
// TODO avoid these early returns by masking instead
if(a.is_identity()) {
return Self::from_affine(b);
}

if(b.is_identity()) {
return a;
}

/*
https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
TODO rename these vars
TODO reduce vars
TODO use a complete addition formula??? (YES)
https://eprint.iacr.org/2015/1060.pdf
*/

const auto Z1Z1 = a.z().square();
const auto U2 = b.x() * Z1Z1;
const auto S2 = b.y() * a.z() * Z1Z1;
const auto H = U2 - a.x();
const auto r = S2 - a.y();

if(H.is_zero()) {
if(r.is_zero()) {
return a.dbl();
} else {
return Self::identity();
}
}

const auto HH = H.square();
const auto HHH = H * HH;
const auto V = a.x() * HH;
const auto t2 = r.square();
const auto t3 = V + V;
const auto t4 = t2 - HHH;
const auto X3 = t4 - t3;
const auto t5 = V - X3;
const auto t6 = a.y() * HHH;
const auto t7 = r * t5;
const auto Y3 = t7 - t6;
const auto Z3 = a.z() * H;

return Self(X3, Y3, Z3);
}

constexpr static Self add(const Self& a, const Self& b) {
Expand All @@ -451,8 +509,6 @@ class ProjectiveCurvePoint {
TODO reduce vars
TODO take advantage of A = 0 and A = -3
TODO use a complete addition formula??? (YES)
https://eprint.iacr.org/2015/1060.pdf
*/
Expand Down Expand Up @@ -631,7 +687,7 @@ class PrecomputedMulTable {
m_table = ProjectivePoint::to_affine_batch(table);
}

constexpr AffinePoint operator()(const Scalar& s) const {
constexpr ProjectivePoint operator()(const Scalar& s) const {
const auto bits = s.serialize();

auto accum = ProjectivePoint::identity();
Expand All @@ -642,7 +698,7 @@ class PrecomputedMulTable {
accum.conditional_add(b_set, m_table[i]);
}

return accum.to_affine();
return accum;
}

private:
Expand Down Expand Up @@ -682,6 +738,13 @@ class EllipticCurve {

typedef PrecomputedMulTable<AffinePoint, ProjectivePoint, Scalar> MulTable;

static const MulTable& MulByGTable() {
static const auto MulG = MulTable(G);
return MulG;
}

static const ProjectivePoint MulByG(const Scalar& scalar) { return MulByGTable()(scalar); }

// (-B / A), will be zero if A == 0 or B == 0 or Z == 0
static const FieldElement& SSWU_C1() {
// We derive it from C2 to avoid a second inversion
Expand Down Expand Up @@ -745,8 +808,7 @@ inline std::vector<uint8_t> hash_to_curve_sswu(std::string_view hash,
pt += map_to_curve_sswu<C>(u);
}

const auto pt_bytes = pt.to_affine().serialize();
return std::vector<uint8_t>(pt_bytes.begin(), pt_bytes.end());
return pt.to_affine().serialize_to_vec();
}

} // namespace Botan
Expand Down
2 changes: 2 additions & 0 deletions src/lib/pubkey/pcurves/pcurves_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ template <WordType W, size_t N, size_t L>
inline constexpr auto bytes_to_words(const uint8_t bytes[L]) {
static_assert(L <= WordInfo<W>::bytes * N);

// TODO: This could be optimized quite a bit which is relevant
// since it executes at runtime
std::array<W, N> r = {};
for(size_t i = 0; i != L; ++i) {
shift_left<8>(r);
Expand Down
15 changes: 15 additions & 0 deletions src/tests/test_ecc_pointmul.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
#include <botan/ec_group.h>
#endif

#if defined(BOTAN_HAS_PCURVES)
#include <botan/internal/pcurves.h>
#endif

namespace Botan_Tests {

namespace {
Expand Down Expand Up @@ -48,6 +52,17 @@ class ECC_Basepoint_Mul_Tests final : public Text_Based_Test {
result.test_eq("p3 affine X", p3.get_affine_x(), X);
result.test_eq("p3 affine Y", p3.get_affine_y(), Y);

#if defined(BOTAN_HAS_PCURVES)
if(group_id == "secp256r1") {
const auto mbits = BigInt::encode_1363(m, group.get_order_bytes());

const auto pbits = Botan::PCurve::mul_by_g(Botan::PCurve::PrimeOrderCurveId::P256, mbits);

const Botan::EC_Point pc = group.OS2ECP(pbits);
result.test_eq("pcurves affine X", pc.get_affine_x(), X);
result.test_eq("pcurves affine Y", pc.get_affine_y(), Y);
}
#endif
return result;
}
};
Expand Down

0 comments on commit cd40575

Please sign in to comment.