Skip to content

Commit

Permalink
[WIP] 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 9442cf9
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 44 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.

5 changes: 3 additions & 2 deletions elliptic-curve-crate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@
#[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;
Expand Down
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
71 changes: 55 additions & 16 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 @@ -53,6 +56,12 @@ impl PartialEq for AffinePoint {
}
}

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

impl Eq for AffinePoint {}

impl AffinePoint {
Expand Down Expand Up @@ -124,36 +133,66 @@ 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 Neg for AffinePoint {
type Output = AffinePoint;

fn neg(self) -> Self::Output {
AffinePoint {
x: self.x,
y: -self.y,
}
}
}

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 Neg for AffinePoint {
type Output = AffinePoint;
impl FixedBaseScalarMul for Secp256k1 {
/// Elliptic curve point type
type Point = AffinePoint;

fn neg(self) -> Self::Output {
AffinePoint {
x: self.x,
y: -self.y,
}
/// 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())
}
}

Expand Down
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()
}
}
13 changes: 9 additions & 4 deletions k256/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,19 @@ impl Curve for Secp256k1 {
type ScalarSize = U32;
}

/// K-256 (secp256k1) Secret Key
/// K-256 (secp256k1) Secret Key.
pub type SecretKey = elliptic_curve::SecretKey<U32>;

/// K-256 (secp256k1) Public Key
/// K-256 (secp256k1) Public Key.
pub type PublicKey = elliptic_curve::weierstrass::PublicKey<Secp256k1>;

/// K-256 Compressed Curve Point
/// K-256 Scalar Bytes.
///
/// This is the size of a bytestring representing a scalar (i.e. an integer)
pub type ScalarBytes = elliptic_curve::ScalarBytes<U32>;

/// K-256 Compressed Curve Point.
pub type CompressedCurvePoint = elliptic_curve::weierstrass::CompressedCurvePoint<Secp256k1>;

/// K-256 Uncompressed Curve Point
/// K-256 Uncompressed Curve Point.
pub type UncompressedCurvePoint = elliptic_curve::weierstrass::UncompressedCurvePoint<Secp256k1>;
6 changes: 3 additions & 3 deletions p256/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 @@ -28,7 +28,7 @@ proptest = "0.9"

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

Expand Down
6 changes: 4 additions & 2 deletions p256/src/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ 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},
};

use crate::{CompressedCurvePoint, PublicKey, UncompressedCurvePoint};
use field::{FieldElement, MODULUS};
Expand Down
2 changes: 1 addition & 1 deletion p256/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
Loading

0 comments on commit 9442cf9

Please sign in to comment.