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

k256/p256: use new SignPrimitive API; add BlindedScalar #99

Merged
merged 1 commit into from
Jul 28, 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
32 changes: 16 additions & 16 deletions Cargo.lock

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

4 changes: 2 additions & 2 deletions k256/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ keywords = ["bitcoin", "crypto", "ecc", "ethereum", "secp256k1"]
[dependencies]
cfg-if = "0.1"
ecdsa = { version = "= 0.7.0-pre", optional = true, default-features = false, features = ["hazmat"] }
elliptic-curve = { version = "= 0.5.0-pre", default-features = false, features = ["weierstrass"] }
sha2 = { version = "0.9", optional = true }
elliptic-curve = { version = "= 0.5.0-pre", default-features = false, features = ["weierstrass"] }
sha2 = { version = "0.9", optional = true, default-features = false }
zeroize = { version = "1", optional = true, default-features = false }

[dev-dependencies]
Expand Down
19 changes: 18 additions & 1 deletion k256/src/arithmetic/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ cfg_if! {

use crate::ScalarBytes;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Shr, Sub, SubAssign};
use elliptic_curve::subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use elliptic_curve::{
ops::Invert,
subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption},
};

#[cfg(feature = "digest")]
use ecdsa::signature::digest::{consts::U32, Digest};
Expand Down Expand Up @@ -248,6 +251,12 @@ impl Scalar {
}
}

impl AsRef<Scalar> for Scalar {
fn as_ref(&self) -> &Scalar {
self
}
}

impl Shr<usize> for Scalar {
type Output = Self;

Expand Down Expand Up @@ -372,6 +381,14 @@ impl MulAssign<Scalar> for Scalar {
}
}

impl Invert for Scalar {
type Output = Self;

fn invert(&self) -> CtOption<Self> {
Scalar::invert(self)
}
}

impl From<Scalar> for ScalarBytes {
fn from(scalar: Scalar) -> Self {
scalar.to_bytes().into()
Expand Down
29 changes: 17 additions & 12 deletions k256/src/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use {
hazmat::{SignPrimitive, VerifyPrimitive},
Error,
},
elliptic_curve::subtle::{ConditionallySelectable, CtOption},
elliptic_curve::{
ops::Invert,
subtle::{ConditionallySelectable, CtOption},
},
};

/// ECDSA/secp256k1 signature (fixed-size)
Expand All @@ -26,20 +29,22 @@ impl ecdsa::hazmat::DigestPrimitive for Secp256k1 {
#[cfg(feature = "arithmetic")]
impl SignPrimitive<Secp256k1> for Scalar {
#[allow(clippy::many_single_char_names)]
fn try_sign_prehashed(
fn try_sign_prehashed<K>(
&self,
ephemeral_scalar: &Scalar,
masking_scalar: Option<&Scalar>,
ephemeral_scalar: &K,
hashed_msg: &ScalarBytes,
) -> Result<Signature, Error> {
let k = ephemeral_scalar;

if k.is_zero().into() {
) -> Result<Signature, Error>
where
K: AsRef<Scalar> + Invert<Output = Scalar>,
{
let k_inverse = ephemeral_scalar.invert();
let k = ephemeral_scalar.as_ref();

if k_inverse.is_none().into() || k.is_zero().into() {
return Err(Error::new());
}

// TODO(tarcieri): masking_scalar
assert!(masking_scalar.is_none(), "todo: masking_scalar support");
let k_inverse = k_inverse.unwrap();

// Compute `x`-coordinate of affine point 𝑘×𝑮
let x = (ProjectivePoint::generator() * k).to_affine().unwrap().x;
Expand All @@ -51,8 +56,8 @@ impl SignPrimitive<Secp256k1> for Scalar {
// Reduce message hash to an element of the scalar field
let z = Scalar::from_bytes_reduced(hashed_msg.as_ref());

// Compute `s` as a signature over `r` and `z`
let s = k.invert().unwrap() * &(z + &(r * self));
// Compute `s` as a signature over `r` and `z`.
let s = k_inverse * &(z + &(r * self));

if s.is_zero().into() {
return Err(Error::new());
Expand Down
4 changes: 2 additions & 2 deletions p256/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ keywords = ["crypto", "ecc", "nist", "prime256v1", "secp256r1"]

[dependencies]
ecdsa = { version = "= 0.7.0-pre", optional = true, default-features = false }
elliptic-curve = { version = "= 0.5.0-pre", default-features = false, features = ["weierstrass"] }
sha2 = { version = "0.9", default-features = false, optional = true }
elliptic-curve = { version = "= 0.5.0-pre", default-features = false, features = ["weierstrass"] }
sha2 = { version = "0.9", optional = true, default-features = false }
zeroize = { version = "1", optional = true, default-features = false }

[dev-dependencies]
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 @@ -88,7 +88,7 @@ impl FieldElement {

/// Returns a uniformly-random element within the field.
#[cfg(feature = "rand")]
pub fn generate(rng: &mut (impl CryptoRng + RngCore)) -> Self {
pub fn generate(mut rng: impl CryptoRng + RngCore) -> Self {
// We reduce a random 512-bit value into a 256-bit field, which results in a
// negligible bias from the uniform distribution.
let mut buf = [0; 64];
Expand Down
22 changes: 21 additions & 1 deletion p256/src/arithmetic/scalar.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
//! Scalar field arithmetic modulo n = 115792089210356248762697446949407573529996955224135760342422259061068512044369

#[cfg(feature = "rand")]
pub mod blinding;

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

#[cfg(feature = "rand")]
use elliptic_curve::{
Expand Down Expand Up @@ -111,6 +117,12 @@ fn shr1(u256: &mut U256) {
}
}

impl AsRef<Scalar> for Scalar {
fn as_ref(&self) -> &Scalar {
self
}
}

impl Ord for Scalar {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
use core::cmp::Ordering::*;
Expand Down Expand Up @@ -657,6 +669,14 @@ impl ConstantTimeEq for Scalar {
}
}

impl Invert for Scalar {
type Output = Self;

fn invert(&self) -> CtOption<Self> {
Scalar::invert(self)
}
}

impl From<Scalar> for ScalarBytes {
fn from(scalar: Scalar) -> Self {
scalar.to_bytes().into()
Expand Down
72 changes: 72 additions & 0 deletions p256/src/arithmetic/scalar/blinding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//! Random blinding support for [`Scalar`]

// TODO(tarcieri): make this generic (along with `Scalar::invert_vartime`)
// and extract it into the `elliptic-curve` crate so it can be reused across curves

use super::Scalar;
use elliptic_curve::{
ops::Invert,
rand_core::{CryptoRng, RngCore},
subtle::CtOption,
Generate,
};

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

/// Scalar blinded with a randomly generated masking value.
///
/// This provides a randomly blinded impl of [`Invert`] which is useful for
/// ECDSA ephemeral (`k`) scalars.
#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))]
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
pub struct BlindedScalar {
/// Actual scalar value
scalar: Scalar,

/// Mask value
mask: Scalar,
}

impl BlindedScalar {
/// Create a new [`BlindedScalar`] from a scalar and a [`CryptoRng`]
pub fn new(scalar: Scalar, rng: impl CryptoRng + RngCore) -> Self {
Self {
scalar,
mask: Scalar::generate(rng),
}
}
}

impl AsRef<Scalar> for BlindedScalar {
fn as_ref(&self) -> &Scalar {
&self.scalar
}
}

impl Invert for BlindedScalar {
type Output = Scalar;

fn invert(&self) -> CtOption<Scalar> {
// prevent side channel analysis of scalar inversion by pre-and-post-multiplying
// with the random masking scalar
(self.scalar * &self.mask)
.invert_vartime()
.map(|s| s * &self.mask)
}
}

#[cfg(feature = "zeroize")]
impl Zeroize for BlindedScalar {
fn zeroize(&mut self) {
self.scalar.zeroize();
self.mask.zeroize();
}
}

#[cfg(feature = "zeroize")]
impl Drop for BlindedScalar {
fn drop(&mut self) {
self.zeroize();
}
}
Loading