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:
parent
4f7f138797
commit
5a0332463d
93
src/key.rs
93
src/key.rs
|
@ -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();
|
||||
|
|
|
@ -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")))]
|
||||
|
|
|
@ -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 {}
|
Loading…
Reference in New Issue