Skip to content

Commit

Permalink
Add weierstrass::FixedBaseScalarMul trait
Browse files Browse the repository at this point in the history
Adds a trait for fixed-base scalar multiplication which accepts
`&ScalarBytes` as input and returns an associated point type, whose
bounds allow for a `From` conversion to either the
`CompressedCurvePoint` or `UncompressedCurvePoint` for a given curve.

Using this trait, a `weierstrass::PublicKey<C>::from_secret_key` method
is conditionally implemented when the curve `C` impls the
`FixedBaseScalarMul` trait, allowing generic computation of a public key
from a secret key, with optional point compression (selected via a
`compress` argument).
  • Loading branch information
tarcieri committed Jun 19, 2020
1 parent d204d2d commit 9634ecd
Show file tree
Hide file tree
Showing 18 changed files with 299 additions and 48 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
coverage:
status:
patch: off
project: off
7 changes: 4 additions & 3 deletions elliptic-curve-crate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@
#[cfg(feature = "std")]
extern crate std;

pub use generic_array::{self, typenum::consts};

pub mod error;
pub mod secret_key;

pub use generic_array::{self, typenum::consts};
pub use subtle;

// TODO(tarcieri): other curve forms
#[cfg(feature = "weierstrass")]
pub mod weierstrass;

pub use self::{error::Error, secret_key::SecretKey};

/// Byte array containing a serialized scalar value
/// Byte array containing a serialized scalar value (i.e. an integer)
pub type ScalarBytes<Size> = generic_array::GenericArray<u8, Size>;
24 changes: 23 additions & 1 deletion elliptic-curve-crate/src/weierstrass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,30 @@ pub mod curve;
pub mod point;
pub mod public_key;

pub use curve::{Curve, ScalarBytes};
pub use curve::Curve;
pub use point::{
CompressedCurvePoint, CompressedPointSize, UncompressedCurvePoint, UncompressedPointSize,
};
pub use public_key::PublicKey;

use crate::{consts::U1, ScalarBytes};
use core::ops::Add;
use generic_array::ArrayLength;
use subtle::{ConditionallySelectable, CtOption};

/// Fixed-base scalar multiplication
pub trait FixedBaseScalarMul: Curve
where
<Self::ScalarSize as Add>::Output: Add<U1>,
CompressedCurvePoint<Self>: From<Self::Point>,
UncompressedCurvePoint<Self>: From<Self::Point>,
CompressedPointSize<Self::ScalarSize>: ArrayLength<u8>,
UncompressedPointSize<Self::ScalarSize>: ArrayLength<u8>,
{
/// Elliptic curve point type
type Point: ConditionallySelectable + Default;

/// Multiply the given scalar by the generator point for this elliptic
/// curve.
fn mul_base(scalar: &ScalarBytes<Self::ScalarSize>) -> CtOption<Self::Point>;
}
3 changes: 0 additions & 3 deletions elliptic-curve-crate/src/weierstrass/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,5 @@ pub trait Curve: Clone + Debug + Default + Eq + Ord + Send + Sync {
type ScalarSize: ArrayLength<u8> + Add + Add<U1> + Eq + Ord + Unsigned;
}

/// Alias for [`ScalarBytes`] type for a given Weierstrass curve
pub type ScalarBytes<C> = crate::ScalarBytes<<C as Curve>::ScalarSize>;

/// Alias for [`SecretKey`] type for a given Weierstrass curve
pub type SecretKey<C> = crate::secret_key::SecretKey<<C as Curve>::ScalarSize>;
37 changes: 34 additions & 3 deletions elliptic-curve-crate/src/weierstrass/public_key.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
//! Public keys for Weierstrass curves: wrapper around compressed or
//! uncompressed elliptic curve points.

use super::point::{
CompressedCurvePoint, CompressedPointSize, UncompressedCurvePoint, UncompressedPointSize,
};
use super::Curve;
use super::{
point::{
CompressedCurvePoint, CompressedPointSize, UncompressedCurvePoint, UncompressedPointSize,
},
FixedBaseScalarMul,
};
use crate::SecretKey;
use core::fmt::{self, Debug};
use core::ops::Add;
use generic_array::{
typenum::{Unsigned, U1},
ArrayLength, GenericArray,
};
use subtle::CtOption;

/// Size of an untagged point for given elliptic curve.
// TODO(tarcieri): const generics
Expand Down Expand Up @@ -101,6 +106,32 @@ where
}
}

impl<C: Curve> PublicKey<C>
where
C: FixedBaseScalarMul,
<C::ScalarSize as Add>::Output: Add<U1>,
CompressedCurvePoint<C>: From<C::Point>,
UncompressedCurvePoint<C>: From<C::Point>,
CompressedPointSize<C::ScalarSize>: ArrayLength<u8>,
UncompressedPointSize<C::ScalarSize>: ArrayLength<u8>,
{
/// Compute the [`PublicKey`] for the provided [`SecretKey`].
///
/// The `compress` flag requests point compression.
pub fn from_secret_key(
secret_key: &SecretKey<C::ScalarSize>,
compress: bool,
) -> CtOption<Self> {
C::mul_base(secret_key.secret_scalar()).map(|affine_point| {
if compress {
PublicKey::Compressed(affine_point.into())
} else {
PublicKey::Uncompressed(affine_point.into())
}
})
}
}

impl<C: Curve> AsRef<[u8]> for PublicKey<C>
where
<C::ScalarSize as Add>::Output: Add<U1>,
Expand Down
6 changes: 3 additions & 3 deletions k256/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ path = "../elliptic-curve-crate"
default-features = false
features = ["weierstrass"]

[dependencies.subtle]
version = "2.2.2"
[dependencies.zeroize]
version = "1"
optional = true
default-features = false

Expand All @@ -29,7 +29,7 @@ fiat-crypto = "0.1.0"

[features]
default = ["arithmetic", "std"]
arithmetic = ["subtle"]
arithmetic = []
test-vectors = []
std = ["elliptic-curve/std"]

Expand Down
82 changes: 71 additions & 11 deletions k256/src/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ pub use self::scalar::Scalar;

use core::convert::TryInto;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use elliptic_curve::generic_array::GenericArray;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use elliptic_curve::{
generic_array::GenericArray,
subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption},
weierstrass::FixedBaseScalarMul,
};

use crate::{CompressedCurvePoint, PublicKey, UncompressedCurvePoint};
use crate::{CompressedCurvePoint, PublicKey, ScalarBytes, Secp256k1, UncompressedCurvePoint};
use field::{FieldElement, MODULUS};

/// b = 7 in Montgomery form (aR mod p, where R = 2**256.
Expand Down Expand Up @@ -47,6 +50,12 @@ impl ConstantTimeEq for AffinePoint {
}
}

impl Default for AffinePoint {
fn default() -> AffinePoint {
AffinePoint::generator()
}
}

impl PartialEq for AffinePoint {
fn eq(&self, other: &AffinePoint) -> bool {
self.ct_eq(other).into()
Expand Down Expand Up @@ -124,28 +133,58 @@ impl AffinePoint {
}
}

/// Returns a [`PublicKey`] with the SEC-1 compressed encoding of this point.
pub fn to_compressed_pubkey(&self) -> PublicKey {
PublicKey::Compressed(self.clone().into())
}

/// Returns a [`PublicKey`] with the SEC-1 uncompressed encoding of this point.
pub fn to_uncompressed_pubkey(&self) -> PublicKey {
PublicKey::Uncompressed(self.clone().into())
}
}

impl From<AffinePoint> for CompressedCurvePoint {
/// Returns the SEC-1 compressed encoding of this point.
pub fn to_compressed_pubkey(&self) -> CompressedCurvePoint {
fn from(affine_point: AffinePoint) -> CompressedCurvePoint {
let mut encoded = [0; 33];
encoded[0] = if self.y.is_odd().into() { 0x03 } else { 0x02 };
encoded[1..33].copy_from_slice(&self.x.to_bytes());
encoded[0] = if affine_point.y.is_odd().into() {
0x03
} else {
0x02
};
encoded[1..33].copy_from_slice(&affine_point.x.to_bytes());

CompressedCurvePoint::from_bytes(GenericArray::clone_from_slice(&encoded[..]))
.expect("we encoded it correctly")
}
}

impl From<AffinePoint> for UncompressedCurvePoint {
/// Returns the SEC-1 uncompressed encoding of this point.
pub fn to_uncompressed_pubkey(&self) -> UncompressedCurvePoint {
fn from(affine_point: AffinePoint) -> UncompressedCurvePoint {
let mut encoded = [0; 65];
encoded[0] = 0x04;
encoded[1..33].copy_from_slice(&self.x.to_bytes());
encoded[33..65].copy_from_slice(&self.y.to_bytes());
encoded[1..33].copy_from_slice(&affine_point.x.to_bytes());
encoded[33..65].copy_from_slice(&affine_point.y.to_bytes());

UncompressedCurvePoint::from_bytes(GenericArray::clone_from_slice(&encoded[..]))
.expect("we encoded it correctly")
}
}

impl FixedBaseScalarMul for Secp256k1 {
/// Elliptic curve point type
type Point = AffinePoint;

/// Multiply the given scalar by the generator point for this elliptic
/// curve.
fn mul_base(scalar_bytes: &ScalarBytes) -> CtOption<Self::Point> {
Scalar::from_bytes((*scalar_bytes).into())
.and_then(|scalar| (&ProjectivePoint::generator() * &scalar).to_affine())
}
}

impl Neg for AffinePoint {
type Output = AffinePoint;

Expand Down Expand Up @@ -492,9 +531,13 @@ mod tests {

use super::{AffinePoint, ProjectivePoint, Scalar, CURVE_EQUATION_B};
use crate::{
arithmetic::test_vectors::group::{ADD_TEST_VECTORS, MUL_TEST_VECTORS},
PublicKey,
arithmetic::test_vectors::{
group::{ADD_TEST_VECTORS, MUL_TEST_VECTORS},
mul_base::MUL_BASE_TEST_VECTORS,
},
PublicKey, ScalarBytes, Secp256k1, UncompressedCurvePoint,
};
use elliptic_curve::weierstrass::FixedBaseScalarMul;

const CURVE_EQUATION_B_BYTES: &str =
"0000000000000000000000000000000000000000000000000000000000000007";
Expand Down Expand Up @@ -723,4 +766,21 @@ mod tests {
);
}
}

#[test]
fn fixed_base_scalar_mul() {
for vector in MUL_BASE_TEST_VECTORS {
let m = hex::decode(&vector.m).unwrap();
let x = hex::decode(&vector.x).unwrap();
let y = hex::decode(&vector.y).unwrap();

let point = UncompressedCurvePoint::from(
Secp256k1::mul_base(ScalarBytes::from_slice(&m)).unwrap(),
)
.into_bytes();

assert_eq!(x, &point[1..=32]);
assert_eq!(y, &point[33..]);
}
}
}
2 changes: 1 addition & 1 deletion k256/src/arithmetic/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use core::convert::TryInto;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use elliptic_curve::subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};

#[cfg(feature = "getrandom")]
use getrandom::getrandom;
Expand Down
25 changes: 23 additions & 2 deletions k256/src/arithmetic/scalar.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! Scalar field arithmetic.

use core::convert::TryInto;
use subtle::{Choice, CtOption};
use elliptic_curve::subtle::{Choice, ConditionallySelectable, CtOption};

#[cfg(feature = "zeroize")]
use zeroize::Zeroize;

use crate::arithmetic::util::sbb;

Expand All @@ -20,7 +23,7 @@ const MODULUS: [u64; LIMBS] = [
/// An element in the finite field modulo n.
// TODO: This currently uses native representation internally, but will probably move to
// Montgomery representation later.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, Default)]
pub struct Scalar(pub(crate) [u64; LIMBS]);

impl From<u64> for Scalar {
Expand Down Expand Up @@ -74,3 +77,21 @@ impl Scalar {
ret
}
}

impl ConditionallySelectable for Scalar {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Scalar([
u64::conditional_select(&a.0[0], &b.0[0], choice),
u64::conditional_select(&a.0[1], &b.0[1], choice),
u64::conditional_select(&a.0[2], &b.0[2], choice),
u64::conditional_select(&a.0[3], &b.0[3], choice),
])
}
}

#[cfg(feature = "zeroize")]
impl Zeroize for Scalar {
fn zeroize(&mut self) {
self.0.as_mut().zeroize()
}
}
1 change: 1 addition & 0 deletions k256/src/arithmetic/test_vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

pub mod field;
pub mod group;
pub mod mul_base;
44 changes: 44 additions & 0 deletions k256/src/arithmetic/test_vectors/mul_base.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! Fixed-base scalar multiplication test vectors for secp256k1

/// Fixed-base scalar multiplication test vector
pub struct MulBaseVector {
/// Random value modulo the curve order n
pub m: &'static str,

/// Resulting x-coordinate after scalar multiplication
pub x: &'static str,

/// Resulting y-coordinate after scalar multiplication
pub y: &'static str,
}

/// Vectors for secp256k1 are difficult to find. Fortunately Thomas Pornin
/// made some and he seems to know a thing or two about cryptography:
/// <https://crypto.stackexchange.com/a/787>
pub const MUL_BASE_TEST_VECTORS: &[MulBaseVector] = &[
MulBaseVector {
m: "AA5E28D6A97A2479A65527F7290311A3624D4CC0FA1578598EE3C2613BF99522",
x: "34F9460F0E4F08393D192B3C5133A6BA099AA0AD9FD54EBCCFACDFA239FF49C6",
y: "0B71EA9BD730FD8923F6D25A7A91E7DD7728A960686CB5A901BB419E0F2CA232",
},
MulBaseVector {
m: "7E2B897B8CEBC6361663AD410835639826D590F393D90A9538881735256DFAE3",
x: "D74BF844B0862475103D96A611CF2D898447E288D34B360BC885CB8CE7C00575",
y: "131C670D414C4546B88AC3FF664611B1C38CEB1C21D76369D7A7A0969D61D97D",
},
MulBaseVector {
m: "6461E6DF0FE7DFD05329F41BF771B86578143D4DD1F7866FB4CA7E97C5FA945D",
x: "E8AECC370AEDD953483719A116711963CE201AC3EB21D3F3257BB48668C6A72F",
y: "C25CAF2F0EBA1DDB2F0F3F47866299EF907867B7D27E95B3873BF98397B24EE1",
},
MulBaseVector {
m: "376A3A2CDCD12581EFFF13EE4AD44C4044B8A0524C42422A7E1E181E4DEECCEC",
x: "14890E61FCD4B0BD92E5B36C81372CA6FED471EF3AA60A3E415EE4FE987DABA1",
y: "297B858D9F752AB42D3BCA67EE0EB6DCD1C2B7B0DBE23397E66ADC272263F982",
},
MulBaseVector {
m: "1B22644A7BE026548810C378D0B2994EEFA6D2B9881803CB02CEFF865287D1B9",
x: "F73C65EAD01C5126F28F442D087689BFA08E12763E0CEC1D35B01751FD735ED3",
y: "F449A8376906482A84ED01479BD18882B919C140D638307F0C0934BA12590BDE",
},
];
Loading

0 comments on commit 9634ecd

Please sign in to comment.