Skip to content

Commit

Permalink
Add SignEcdsa().
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelRiabzev committed Jan 8, 2020
1 parent 8217955 commit 0d32539
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 32 deletions.
10 changes: 8 additions & 2 deletions src/starkware/algebra/big_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ class BigInt {
*/
static BigInt MulMod(const BigInt& a, const BigInt& b, const BigInt& modulus);

/*
Computes the inverse of *this in the field GF(prime).
If prime is not a prime number, the behavior is undefined.
*/
BigInt InvModPrime(const BigInt& prime) const;

/*
Return pair of the form (result, underflow_occurred).
*/
Expand All @@ -64,9 +70,9 @@ class BigInt {

constexpr bool operator>=(const BigInt& b) const { return !(*this < b); }

bool operator>(const BigInt& b) const { return b < *this; }
constexpr bool operator>(const BigInt& b) const { return b < *this; }

bool operator<=(const BigInt& b) const { return !(*this > b); }
constexpr bool operator<=(const BigInt& b) const { return !(*this > b); }

/*
Returns the pair (q, r) such that this == q*divisor + r and r < divisor.
Expand Down
8 changes: 8 additions & 0 deletions src/starkware/algebra/big_int.inl
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ BigInt<N> BigInt<N>::MulMod(const BigInt& a, const BigInt& b, const BigInt& modu
return res;
}

template <size_t N>
BigInt<N> BigInt<N>::InvModPrime(const BigInt& prime) const {
ASSERT(*this != BigInt::Zero(), "Inverse of 0 is not defined.");
return GenericPow(
*this, (prime - BigInt(2)).ToBoolVector(), BigInt::One(),
[&prime](const BigInt& multiplier, BigInt* dst) { *dst = MulMod(*dst, multiplier, prime); });
}

template <size_t N>
constexpr std::pair<BigInt<N>, bool> BigInt<N>::Sub(const BigInt& a, const BigInt& b) {
bool carry{};
Expand Down
21 changes: 15 additions & 6 deletions src/starkware/algebra/big_int_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,15 @@

#include <limits>

#include "gmock/gmock.h"
#include "gtest/gtest.h"

#include "starkware/utils/test_utils.h"

namespace starkware {
namespace {

using TestTypes = ::testing::Types<BigInt<1>, BigInt<2>, BigInt<5>, BigInt<10>>;

template <typename>
class BigIntTest : public ::testing::Test {};

TYPED_TEST_CASE(BigIntTest, TestTypes);
using testing::HasSubstr;

TEST(BigInt, Random) {
Prng prng;
Expand Down Expand Up @@ -108,6 +104,19 @@ TEST(BigInt, MulMod) {
EXPECT_EQ(BigInt<4>::MulMod(BigInt<4>(7), BigInt<4>(5), BigInt<4>(32)), BigInt<4>(3));
}

TEST(BigInt, InvMod) {
Prng prng;
const auto val = BigInt<4>::RandomBigInt(&prng);
const auto prime = 0xf04a65fa008b9e14bfe07094f9ff9bb7363ae6512e213a0a104adb17fb81b385_Z;
const auto inv = val.InvModPrime(prime);
EXPECT_EQ(BigInt<4>::MulMod(val, inv, prime), 0x1_Z);
}

TEST(BigInt, InvMod_Zero) {
const auto prime = 0xf04a65fa008b9e14bfe07094f9ff9bb7363ae6512e213a0a104adb17fb81b385_Z;
EXPECT_ASSERT(BigInt<4>::Zero().InvModPrime(prime), HasSubstr("Inverse of 0"));
}

TEST(BigInt, UserLiteral) {
auto a = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001_Z;
BigInt<4> b({0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48});
Expand Down
63 changes: 59 additions & 4 deletions src/starkware/crypto/ecdsa.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,70 @@
#include "starkware/algebra/fraction_field_element.h"
#include "starkware/crypto/elliptic_curve_constants.h"
#include "starkware/utils/error_handling.h"
#include "starkware/utils/prng.h"

namespace starkware {

EcPoint<PrimeFieldElement> GetPublicKey(const PrimeFieldElement::ValueType& private_key) {
const auto& generator = GetEcConstants().k_points[1];
const auto& alpha = GetEcConstants().k_alpha;
return generator.MultiplyByScalar(private_key, alpha);
}

Signature SignEcdsa(const PrimeFieldElement::ValueType& private_key, const PrimeFieldElement& z) {
using ValueType = typename PrimeFieldElement::ValueType;
const auto& generator = GetEcConstants().k_points[1];
const auto& alpha = GetEcConstants().k_alpha;
const auto& curve_order = GetEcConstants().k_order;
constexpr auto upper_bound = 0x800000000000000000000000000000000000000000000000000000000000000_Z;
static_assert(upper_bound <= PrimeFieldElement::kModulus);
ASSERT(upper_bound <= curve_order, "Unexpected curve size.");

ASSERT(z != PrimeFieldElement::Zero(), "Message cannot be zero.");
ASSERT(z.ToStandardForm() < upper_bound, "z is too big.");

Prng prng;

// The probability of this loop not terminating during it's first iteration is negligible.
for (size_t i = 0; i < 100; i++) {
// This randomness is unsafe, but good enough for testing purposes.
const auto k = ValueType::MulMod(ValueType::RandomBigInt(&prng), ValueType::One(), curve_order);
if (k == ValueType::Zero()) {
continue;
}
const PrimeFieldElement x = generator.MultiplyByScalar(k, alpha).x;
const ValueType r = x.ToStandardForm();
if (r >= curve_order || r == ValueType::Zero()) {
continue;
}

const ValueType k_inv = k.InvModPrime(curve_order);
ValueType s = ValueType::MulMod(r, private_key, curve_order);
// Non modular addition, requires the summands to be small enough to prevent overflow.
ASSERT(curve_order.NumLeadingZeros() > 0, "Implementation assumes smaller curve.");
s = s + z.ToStandardForm();
s = ValueType::MulMod(s, k_inv, curve_order);
if (s == ValueType::Zero()) {
continue;
}

const ValueType w = s.InvModPrime(curve_order);
if (w >= upper_bound) {
continue;
}
const PrimeFieldElement w_field = PrimeFieldElement::FromBigInt(w);
return {x, w_field};
}
ASSERT(false, "Could not find valid randomness for signing.");
}

bool VerifyEcdsa(
const EcPoint<PrimeFieldElement>& public_key, const PrimeFieldElement& z,
const PrimeFieldElement& r, const PrimeFieldElement& w) {
const Signature& sig) {
using FractionFieldElementT = FractionFieldElement<PrimeFieldElement>;
using EcPointT = EcPoint<FractionFieldElementT>;
const auto& r = sig.first;
const auto& w = sig.second;
// z, r, w should be smaller than 2^251.
const auto upper_bound = 0x800000000000000000000000000000000000000000000000000000000000000_Z;
ASSERT(z != PrimeFieldElement::Zero(), "Message cannot be zero.");
Expand All @@ -31,8 +87,7 @@ bool VerifyEcdsa(
}

bool VerifyEcdsaPartialKey(
const PrimeFieldElement& public_key_x, const PrimeFieldElement& z, const PrimeFieldElement& r,
const PrimeFieldElement& w) {
const PrimeFieldElement& public_key_x, const PrimeFieldElement& z, const Signature& sig) {
const auto alpha = GetEcConstants().k_alpha;
const auto beta = GetEcConstants().k_beta;
const auto public_key = EcPoint<PrimeFieldElement>::GetPointFromX(public_key_x, alpha, beta);
Expand All @@ -42,7 +97,7 @@ bool VerifyEcdsaPartialKey(

// There are two points on the elliptic curve with the given public_key_x, both will be
// tested by VerifyEcdsa().
return VerifyEcdsa(*public_key, z, r, w);
return VerifyEcdsa(*public_key, z, sig);
}

} // namespace starkware
28 changes: 24 additions & 4 deletions src/starkware/crypto/ecdsa.h
Original file line number Diff line number Diff line change
@@ -1,27 +1,47 @@
#ifndef STARKWARE_CRYPTO_ECDSA_H_
#define STARKWARE_CRYPTO_ECDSA_H_

#include <utility>

#include "starkware/algebra/elliptic_curve.h"
#include "starkware/algebra/prime_field_element.h"

namespace starkware {

/*
Type representing a signature (r, w).
*/
using Signature = std::pair<PrimeFieldElement, PrimeFieldElement>;

/*
Deduces the public key given a private key.
The x coordinate of the public key is also known as the partial public key,
and used in StarkEx to identify the user.
*/
EcPoint<PrimeFieldElement> GetPublicKey(const PrimeFieldElement::ValueType& private_key);

/*
Signs message hash z with the provided private_key.
NOTE: This function should ONLY be used for testing as it uses an unsafe random function for
choosing k.
*/
Signature SignEcdsa(const PrimeFieldElement::ValueType& private_key, const PrimeFieldElement& z);

/*
Verifies ECDSA signature of a given hash message z with a given public key.
Returns true if either public_key or -public_key signs the message.
NOTE: This function assumes that the public_key is on the curve.
*/
bool VerifyEcdsa(
const EcPoint<PrimeFieldElement>& public_key, const PrimeFieldElement& z,
const PrimeFieldElement& r, const PrimeFieldElement& w);
const EcPoint<PrimeFieldElement>& public_key, const PrimeFieldElement& z, const Signature& sig);

/*
Same as VerifyEcdsa() except that only the x coordinate of the public key is given. The y
coordinate is computed, and both options are checked.
*/
bool VerifyEcdsaPartialKey(
const PrimeFieldElement& public_key_x, const PrimeFieldElement& z, const PrimeFieldElement& r,
const PrimeFieldElement& w);
const PrimeFieldElement& public_key_x, const PrimeFieldElement& z, const Signature& sig);

} // namespace starkware

Expand Down
57 changes: 41 additions & 16 deletions src/starkware/crypto/ecdsa_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,30 @@
namespace starkware {
namespace {

TEST(ECDSA, PublicKeyFromPrivate) {
const auto private_key = 0x3c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc_Z;
const EcPoint<PrimeFieldElement> public_key(
PrimeFieldElement::FromBigInt(
0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43_Z),
PrimeFieldElement::FromBigInt(
0x54d7beec5ec728223671c627557efc5c9a6508425dc6c900b7741bf60afec06_Z));

EXPECT_EQ(public_key, GetPublicKey(private_key));
}

TEST(ECDSA, SignAndVerify) {
Prng prng;
using ValueType = PrimeFieldElement::ValueType;

// Draw test parameters.
const auto private_key = ValueType::RandomBigInt(&prng);
const auto public_key = GetPublicKey(private_key);
const auto msg = PrimeFieldElement::RandomElement(&prng);

const auto signature = SignEcdsa(private_key, msg);
EXPECT_TRUE(VerifyEcdsa(public_key, msg, signature));
}

TEST(VerifyEcdsa, Regression) {
Prng prng;
const auto alpha = GetEcConstants().k_alpha;
Expand All @@ -30,28 +54,29 @@ TEST(VerifyEcdsa, Regression) {
0x173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882_Z);
const auto w = PrimeFieldElement::FromBigInt(
0x1f2c44a7798f55192f153b4c48ea5c1241fbb69e6132cc8a0da9c5b62a4286e_Z);
EXPECT_TRUE(VerifyEcdsa(public_key, z, r, w));
EXPECT_TRUE(VerifyEcdsa(-public_key, z, r, w));
EXPECT_FALSE(VerifyEcdsa(wrong_public_key, z, r, w));
EXPECT_FALSE(VerifyEcdsa(public_key, z + PrimeFieldElement::One(), r, w));
EXPECT_FALSE(VerifyEcdsa(public_key, z, r + PrimeFieldElement::One(), w));
EXPECT_FALSE(VerifyEcdsa(public_key, z, r, w + PrimeFieldElement::One()));

EXPECT_TRUE(VerifyEcdsaPartialKey(public_key_x, z, r, w));
EXPECT_FALSE(VerifyEcdsaPartialKey(wrong_public_key.x, z, r, w));
EXPECT_FALSE(VerifyEcdsaPartialKey(public_key_x, z + PrimeFieldElement::One(), r, w));
EXPECT_FALSE(VerifyEcdsaPartialKey(public_key_x, z, r + PrimeFieldElement::One(), w));
EXPECT_FALSE(VerifyEcdsaPartialKey(public_key_x, z, r, w + PrimeFieldElement::One()));
EXPECT_FALSE(VerifyEcdsaPartialKey(public_key_y, z, r, w));

EXPECT_TRUE(VerifyEcdsa(public_key, z, {r, w}));
EXPECT_TRUE(VerifyEcdsa(-public_key, z, {r, w}));
EXPECT_FALSE(VerifyEcdsa(wrong_public_key, z, {r, w}));
EXPECT_FALSE(VerifyEcdsa(public_key, z + PrimeFieldElement::One(), {r, w}));
EXPECT_FALSE(VerifyEcdsa(public_key, z, {r + PrimeFieldElement::One(), w}));
EXPECT_FALSE(VerifyEcdsa(public_key, z, {r, w + PrimeFieldElement::One()}));

EXPECT_TRUE(VerifyEcdsaPartialKey(public_key_x, z, {r, w}));
EXPECT_FALSE(VerifyEcdsaPartialKey(wrong_public_key.x, z, {r, w}));
EXPECT_FALSE(VerifyEcdsaPartialKey(public_key_x, z + PrimeFieldElement::One(), {r, w}));
EXPECT_FALSE(VerifyEcdsaPartialKey(public_key_x, z, {r + PrimeFieldElement::One(), w}));
EXPECT_FALSE(VerifyEcdsaPartialKey(public_key_x, z, {r, w + PrimeFieldElement::One()}));
EXPECT_FALSE(VerifyEcdsaPartialKey(public_key_y, z, {r, w}));
}

TEST(VerifyEcdsa, Benchmark) {
Prng prng;
for (size_t i = 0; i < 100; i++) {
EXPECT_FALSE(VerifyEcdsa(
{PrimeFieldElement::RandomElement(&prng), PrimeFieldElement::RandomElement(&prng)},
PrimeFieldElement::RandomElement(&prng), PrimeFieldElement::RandomElement(&prng),
PrimeFieldElement::RandomElement(&prng)));
PrimeFieldElement::RandomElement(&prng),
{PrimeFieldElement::RandomElement(&prng), PrimeFieldElement::RandomElement(&prng)}));
}
}

Expand All @@ -62,7 +87,7 @@ TEST(VerifyEcdsaPartialKey, Benchmark) {
for (size_t i = 0; i < 100; i++) {
EXPECT_FALSE(VerifyEcdsaPartialKey(
public_key_x, PrimeFieldElement::RandomElement(&prng),
PrimeFieldElement::RandomElement(&prng), PrimeFieldElement::RandomElement(&prng)));
{PrimeFieldElement::RandomElement(&prng), PrimeFieldElement::RandomElement(&prng)}));
}
}

Expand Down

0 comments on commit 0d32539

Please sign in to comment.