Add `Scalar` newtype and use it in tweaking APIs

This adds `Scalar` newtype to better represent values accepted by
tweaking functions. This type is always 32-bytes and guarantees being
within curve order.
This commit is contained in:
Martin Habovstiak 2022-06-09 14:35:41 +02:00
parent 4f7f138797
commit 5a0332463d
3 changed files with 174 additions and 58 deletions

View File

@ -29,6 +29,7 @@ use crate::ffi::types::c_uint;
use crate::{Message, ecdsa, SECP256K1};
#[cfg(all(feature = "global-context", feature = "rand-std"))]
use crate::schnorr;
use crate::Scalar;
/// Secret 256-bit key used as `x` in an ECDSA signature.
///
@ -232,15 +233,11 @@ impl SecretKey {
///
/// # Errors
///
/// Returns an error if the resulting key would be invalid or if the tweak was not a 32-byte
/// length slice.
/// Returns an error if the resulting key would be invalid.
pub fn add_assign(
&mut self,
other: &[u8],
other: &Scalar,
) -> Result<(), Error> {
if other.len() != 32 {
return Err(Error::InvalidTweak);
}
unsafe {
if ffi::secp256k1_ec_seckey_tweak_add(
ffi::secp256k1_context_no_precomp,
@ -257,15 +254,11 @@ impl SecretKey {
#[inline]
/// Multiplies one secret key by another, modulo the curve order. Will
/// return an error if the resulting key would be invalid or if
/// the tweak was not a 32-byte length slice.
/// return an error if the resulting key would be invalid.
pub fn mul_assign(
&mut self,
other: &[u8],
other: &Scalar,
) -> Result<(), Error> {
if other.len() != 32 {
return Err(Error::InvalidTweak);
}
unsafe {
if ffi::secp256k1_ec_seckey_tweak_mul(
ffi::secp256k1_context_no_precomp,
@ -498,20 +491,16 @@ impl PublicKey {
}
#[inline]
/// Adds the `other` public key to `self` in place.
/// Adds `other * G` to `self` in place.
///
/// # Errors
///
/// Returns an error if the resulting key would be invalid or if the tweak was not a 32-byte
/// length slice.
/// Returns an error if the resulting key would be invalid.
pub fn add_exp_assign<C: Verification>(
&mut self,
secp: &Secp256k1<C>,
other: &[u8]
other: &Scalar
) -> Result<(), Error> {
if other.len() != 32 {
return Err(Error::InvalidTweak);
}
unsafe {
if ffi::secp256k1_ec_pubkey_tweak_add(secp.ctx, &mut self.0, other.as_c_ptr()) == 1 {
Ok(())
@ -526,16 +515,12 @@ impl PublicKey {
///
/// # Errors
///
/// Returns an error if the resulting key would be invalid or if the tweak was not a 32-byte
/// length slice.
/// Returns an error if the resulting key would be invalid.
pub fn mul_assign<C: Verification>(
&mut self,
secp: &Secp256k1<C>,
other: &[u8],
other: &Scalar,
) -> Result<(), Error> {
if other.len() != 32 {
return Err(Error::InvalidTweak);
}
unsafe {
if ffi::secp256k1_ec_pubkey_tweak_mul(secp.ctx, &mut self.0, other.as_c_ptr()) == 1 {
Ok(())
@ -860,8 +845,7 @@ impl KeyPair {
///
/// # Errors
///
/// Returns an error if the resulting key would be invalid or if the tweak was not a 32-byte
/// length slice.
/// Returns an error if the resulting key would be invalid.
///
/// NB: Will not error if the tweaked public key has an odd value and can't be used for
/// BIP 340-342 purposes.
@ -870,12 +854,11 @@ impl KeyPair {
///
/// ```
/// # #[cfg(all(feature = "std", feature = "rand-std"))] {
/// use secp256k1::{Secp256k1, KeyPair};
/// use secp256k1::{Secp256k1, KeyPair, Scalar};
/// use secp256k1::rand::{RngCore, thread_rng};
///
/// let secp = Secp256k1::new();
/// let mut tweak = [0u8; 32];
/// thread_rng().fill_bytes(&mut tweak);
/// let tweak = Scalar::random();
///
/// let mut key_pair = KeyPair::new(&secp, &mut thread_rng());
/// key_pair.tweak_add_assign(&secp, &tweak).expect("Improbable to fail with a randomly generated tweak");
@ -886,12 +869,8 @@ impl KeyPair {
pub fn tweak_add_assign<C: Verification>(
&mut self,
secp: &Secp256k1<C>,
tweak: &[u8],
tweak: &Scalar,
) -> Result<(), Error> {
if tweak.len() != 32 {
return Err(Error::InvalidTweak);
}
unsafe {
let err = ffi::secp256k1_keypair_xonly_tweak_add(
secp.ctx,
@ -1150,12 +1129,11 @@ impl XOnlyPublicKey {
///
/// ```
/// # #[cfg(all(feature = "std", feature = "rand-std"))] {
/// use secp256k1::{Secp256k1, KeyPair};
/// use secp256k1::{Secp256k1, KeyPair, Scalar};
/// use secp256k1::rand::{RngCore, thread_rng};
///
/// let secp = Secp256k1::new();
/// let mut tweak = [0u8; 32];
/// thread_rng().fill_bytes(&mut tweak);
/// let tweak = Scalar::random();
///
/// let mut key_pair = KeyPair::new(&secp, &mut thread_rng());
/// let (mut public_key, _parity) = key_pair.x_only_public_key();
@ -1165,12 +1143,8 @@ impl XOnlyPublicKey {
pub fn tweak_add_assign<V: Verification>(
&mut self,
secp: &Secp256k1<V>,
tweak: &[u8],
tweak: &Scalar,
) -> Result<Parity, Error> {
if tweak.len() != 32 {
return Err(Error::InvalidTweak);
}
let mut pk_parity = 0;
unsafe {
let mut pubkey = ffi::PublicKey::new();
@ -1215,12 +1189,11 @@ impl XOnlyPublicKey {
///
/// ```
/// # #[cfg(all(feature = "std", feature = "rand-std"))] {
/// use secp256k1::{Secp256k1, KeyPair};
/// use secp256k1::{Secp256k1, KeyPair, Scalar};
/// use secp256k1::rand::{thread_rng, RngCore};
///
/// let secp = Secp256k1::new();
/// let mut tweak = [0u8; 32];
/// thread_rng().fill_bytes(&mut tweak);
/// let tweak = Scalar::random();
///
/// let mut key_pair = KeyPair::new(&secp, &mut thread_rng());
/// let (mut public_key, _) = key_pair.x_only_public_key();
@ -1234,7 +1207,7 @@ impl XOnlyPublicKey {
secp: &Secp256k1<V>,
tweaked_key: &Self,
tweaked_parity: Parity,
tweak: [u8; 32],
tweak: Scalar,
) -> bool {
let tweaked_ser = tweaked_key.serialize();
unsafe {
@ -1512,6 +1485,7 @@ mod test {
use super::{XOnlyPublicKey, PublicKey, Secp256k1, SecretKey, KeyPair, Parity};
use crate::{constants, from_hex, to_hex};
use crate::Error::{InvalidPublicKey, InvalidSecretKey};
use crate::Scalar;
macro_rules! hex {
($hex:expr) => ({
@ -1770,15 +1744,17 @@ mod test {
let (mut sk1, mut pk1) = s.generate_keypair(&mut thread_rng());
let (mut sk2, mut pk2) = s.generate_keypair(&mut thread_rng());
let scalar1 = Scalar::from(sk1);
let scalar2 = Scalar::from(sk1);
assert_eq!(PublicKey::from_secret_key(&s, &sk1), pk1);
assert!(sk1.add_assign(&sk2[..]).is_ok());
assert!(pk1.add_exp_assign(&s, &sk2[..]).is_ok());
assert!(sk1.add_assign(&scalar2).is_ok());
assert!(pk1.add_exp_assign(&s, &scalar2).is_ok());
assert_eq!(PublicKey::from_secret_key(&s, &sk1), pk1);
assert_eq!(PublicKey::from_secret_key(&s, &sk2), pk2);
assert!(sk2.add_assign(&sk1[..]).is_ok());
assert!(pk2.add_exp_assign(&s, &sk1[..]).is_ok());
assert!(sk2.add_assign(&scalar1).is_ok());
assert!(pk2.add_exp_assign(&s, &scalar1).is_ok());
assert_eq!(PublicKey::from_secret_key(&s, &sk2), pk2);
}
@ -1789,15 +1765,17 @@ mod test {
let (mut sk1, mut pk1) = s.generate_keypair(&mut thread_rng());
let (mut sk2, mut pk2) = s.generate_keypair(&mut thread_rng());
let scalar1 = Scalar::from(sk1);
let scalar2 = Scalar::from(sk1);
assert_eq!(PublicKey::from_secret_key(&s, &sk1), pk1);
assert!(sk1.mul_assign(&sk2[..]).is_ok());
assert!(pk1.mul_assign(&s, &sk2[..]).is_ok());
assert!(sk1.mul_assign(&scalar2).is_ok());
assert!(pk1.mul_assign(&s, &scalar2).is_ok());
assert_eq!(PublicKey::from_secret_key(&s, &sk1), pk1);
assert_eq!(PublicKey::from_secret_key(&s, &sk2), pk2);
assert!(sk2.mul_assign(&sk1[..]).is_ok());
assert!(pk2.mul_assign(&s, &sk1[..]).is_ok());
assert!(sk2.mul_assign(&scalar1).is_ok());
assert!(pk2.mul_assign(&s, &scalar1).is_ok());
assert_eq!(PublicKey::from_secret_key(&s, &sk2), pk2);
}
@ -1913,7 +1891,7 @@ mod test {
assert!(sum2.is_ok());
assert_eq!(sum1, sum2);
assert!(sk1.add_assign(&sk2.as_ref()[..]).is_ok());
assert!(sk1.add_assign(&Scalar::from(sk2)).is_ok());
let sksum = PublicKey::from_secret_key(&s, &sk1);
assert_eq!(Ok(sksum), sum1);
}
@ -1999,8 +1977,7 @@ mod test {
let s = Secp256k1::new();
for _ in 0..10 {
let mut tweak = [0u8; 32];
thread_rng().fill_bytes(&mut tweak);
let tweak = Scalar::random();
let mut kp = KeyPair::new(&s, &mut thread_rng());
let (mut pk, _parity) = kp.x_only_public_key();

View File

@ -173,6 +173,7 @@ mod key;
pub mod constants;
pub mod ecdh;
pub mod ecdsa;
pub mod scalar;
pub mod schnorr;
#[cfg(feature = "serde")]
mod serde_util;
@ -190,6 +191,7 @@ pub use secp256k1_sys as ffi;
pub use crate::key::{PublicKey, SecretKey};
pub use crate::context::*;
pub use crate::key::*;
pub use crate::scalar::Scalar;
#[cfg(feature = "global-context")]
#[cfg_attr(docsrs, doc(cfg(feature = "global-context")))]

137
src/scalar.rs Normal file
View File

@ -0,0 +1,137 @@
//! Provides [`Scalar`] and related types.
//!
//! In elliptic curve cryptography scalars are non-point values that can be used to multiply
//! points. The most common type of scalars are private keys. However not all scalars are private
//! keys. They can even be public *values*. To make handling them safer and easier this module
//! provides the `Scalar` type and related.
use core::fmt;
/// Positive 256-bit integer guaranteed to be less than the secp256k1 curve order.
///
/// The difference between `PrivateKey` and `Scalar` is that `Scalar` doesn't guarantee being
/// securely usable as a private key.
///
/// **Warning: the operations on this type are NOT constant time!**
/// Using this with secret values is not advised.
// Internal represenation is big endian to match what `libsecp256k1` uses.
// Also easier to implement comparison.
// Debug impl omitted for now, the bytes may be secret
#[allow(missing_debug_implementations)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Scalar([u8; 32]);
const MAX_RAW: [u8; 32] = [
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x40
];
impl Scalar {
/// Scalar representing `0`
pub const ZERO: Scalar = Scalar([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
/// Scalar representing `1`
pub const ONE: Scalar = Scalar([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
/// Maximum valid value: `curve_order - 1`
pub const MAX: Scalar = Scalar(MAX_RAW);
/// Generates a random scalar
#[cfg(any(test, feature = "rand-std"))]
#[cfg_attr(docsrs, doc(cfg(feature = "rand-std")))]
pub fn random() -> Self {
Self::random_custom(rand::thread_rng())
}
/// Generates a random scalar using supplied RNG
#[cfg(any(test, feature = "rand"))]
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
pub fn random_custom<R: rand::Rng>(mut rng: R) -> Self {
let mut bytes = [0u8; 32];
loop {
rng.fill_bytes(&mut bytes);
// unlikely to go past MAX
if let Ok(scalar) = Scalar::from_be_bytes(bytes) {
break scalar;
}
}
}
/// Tries to deserialize from big endian bytes
///
/// **Security warning:** this function is not constant time!
/// Passing secret data is not recommended.
///
/// # Errors
///
/// Returns error when the value is above the curve order.
pub fn from_be_bytes(value: [u8; 32]) -> Result<Self, OutOfRangeError> {
// Lexicographic ordering of arrays of the same length is same as ordering of BE numbers
if value <= MAX_RAW {
Ok(Scalar(value))
} else {
Err(OutOfRangeError {})
}
}
/// Tries to deserialize from little endian bytes
///
/// **Security warning:** this function is not constant time!
/// Passing secret data is not recommended.
///
/// # Errors
///
/// Returns error when the value is above the curve order.
pub fn from_le_bytes(mut value: [u8; 32]) -> Result<Self, OutOfRangeError> {
value.reverse();
Self::from_be_bytes(value)
}
/// Serializes to big endian bytes
pub fn to_be_bytes(self) -> [u8; 32] {
self.0
}
/// Serializes to little endian bytes
pub fn to_le_bytes(self) -> [u8; 32] {
let mut res = self.0;
res.reverse();
res
}
// returns a reference to internal bytes
// non-public to not leak the internal representation
pub(crate) fn as_be_bytes(&self) -> &[u8; 32] {
&self.0
}
pub(crate) fn as_c_ptr(&self) -> *const u8 {
use secp256k1_sys::CPtr;
self.as_be_bytes().as_c_ptr()
}
}
impl From<crate::SecretKey> for Scalar {
fn from(value: crate::SecretKey) -> Self {
Scalar(value.secret_bytes())
}
}
/// Error returned when the value of scalar is invalid - larger than the curve order.
// Intentionally doesn't implement `Copy` to improve forward compatibility.
// Same reason for `non_exhaustive`.
#[allow(missing_copy_implementations)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[non_exhaustive]
pub struct OutOfRangeError {
}
impl fmt::Display for OutOfRangeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt("the value is not a member of secp256k1 field", f)
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for OutOfRangeError {}