Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add weierstrass::FixedBaseScalarMul trait #49

Merged
merged 1 commit into from
Jun 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,6 +4,28 @@ pub mod curve;
pub mod point;
pub mod public_key;

pub use curve::{Curve, ScalarBytes};
pub use curve::Curve;
pub use point::{CompressedPoint, CompressedPointSize, UncompressedPoint, 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>,
CompressedPoint<Self>: From<Self::Point>,
UncompressedPoint<Self>: From<Self::Point>,
CompressedPointSize<Self::ScalarSize>: ArrayLength<u8>,
UncompressedPointSize<Self::ScalarSize>: ArrayLength<u8>,
{
/// Elliptic curve point type
type Point: ConditionallySelectable;

/// 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>;
36 changes: 33 additions & 3 deletions elliptic-curve-crate/src/weierstrass/public_key.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
//! Public keys for Weierstrass curves: wrapper around compressed or
//! uncompressed elliptic curve points.

use super::point::{
CompressedPoint, CompressedPointSize, UncompressedPoint, UncompressedPointSize,
use super::{
point::{CompressedPoint, CompressedPointSize, UncompressedPoint, UncompressedPointSize},
Curve, FixedBaseScalarMul,
};
use super::Curve;
use crate::SecretKey;
use core::fmt::{self, Debug};
use core::ops::Add;
use generic_array::{
Expand Down Expand Up @@ -101,6 +102,35 @@ where
}
}

impl<C: Curve> PublicKey<C>
where
C: FixedBaseScalarMul,
<C::ScalarSize as Add>::Output: Add<U1>,
CompressedPoint<C>: From<C::Point>,
UncompressedPoint<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) -> Option<Self> {
let ct_option = C::mul_base(secret_key.secret_scalar());

if ct_option.is_some().into() {
let affine_point = ct_option.unwrap();

if compress {
Some(PublicKey::Compressed(affine_point.into()))
} else {
Some(PublicKey::Uncompressed(affine_point.into()))
}
} else {
None
}
Comment on lines +117 to +130
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to eliminate the Default bound on AffinePoint by having this return an Option rather than a CtOption as soon as scalar multiplication is complete (i.e. unwrapping the CtOption containing the public key).

Since we're dealing with a public key after scalar multiplication is complete, this seems ok to me.

}
}

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
75 changes: 64 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::{CompressedPoint, PublicKey, UncompressedPoint};
use crate::{CompressedPoint, PublicKey, ScalarBytes, Secp256k1, UncompressedPoint};
use field::{FieldElement, MODULUS};

/// b = 7 in Montgomery form (aR mod p, where R = 2**256.
Expand Down Expand Up @@ -124,28 +127,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())
}
tarcieri marked this conversation as resolved.
Show resolved Hide resolved
}

impl From<AffinePoint> for CompressedPoint {
/// Returns the SEC-1 compressed encoding of this point.
pub fn to_compressed_pubkey(&self) -> CompressedPoint {
fn from(affine_point: AffinePoint) -> CompressedPoint {
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());

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

impl From<AffinePoint> for UncompressedPoint {
/// Returns the SEC-1 uncompressed encoding of this point.
pub fn to_uncompressed_pubkey(&self) -> UncompressedPoint {
fn from(affine_point: AffinePoint) -> UncompressedPoint {
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());

UncompressedPoint::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 +525,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, UncompressedPoint,
};
use elliptic_curve::weierstrass::FixedBaseScalarMul;

const CURVE_EQUATION_B_BYTES: &str =
"0000000000000000000000000000000000000000000000000000000000000007";
Expand Down Expand Up @@ -723,4 +760,20 @@ 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 =
UncompressedPoint::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",
},
];
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.
///
/// Byte array containing a serialized scalar value (i.e. an integer)
pub type ScalarBytes = elliptic_curve::ScalarBytes<U32>;

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

/// K-256 Uncompressed Curve Point
/// K-256 Uncompressed Curve Point.
pub type UncompressedPoint = elliptic_curve::weierstrass::UncompressedPoint<Secp256k1>;
Loading