From 1661f57d84140b0800552c10d10454d2ad99632e Mon Sep 17 00:00:00 2001 From: "Jamil Lambert, PhD" Date: Tue, 10 Sep 2024 20:49:21 +0100 Subject: [PATCH 1/2] Add `from_byte_array` functions Functions have been added to PrivateKey, PublicKey and XOnlyPublicKey to allow the creation of a key directly from a byte array. --- src/key.rs | 90 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 18 deletions(-) diff --git a/src/key.rs b/src/key.rs index 9b2ddbf3f..56a9faebe 100644 --- a/src/key.rs +++ b/src/key.rs @@ -203,7 +203,7 @@ impl SecretKey { SecretKey(data) } - /// Converts a `SECRET_KEY_SIZE`-byte slice to a secret key. + /// Converts a 32-byte slice to a secret key. /// /// # Examples /// @@ -214,22 +214,31 @@ impl SecretKey { #[inline] pub fn from_slice(data: &[u8]) -> Result { match <[u8; constants::SECRET_KEY_SIZE]>::try_from(data) { - Ok(data) => { - unsafe { - if ffi::secp256k1_ec_seckey_verify( - ffi::secp256k1_context_no_precomp, - data.as_c_ptr(), - ) == 0 - { - return Err(InvalidSecretKey); - } - } - Ok(SecretKey(data)) - } + Ok(data) => Self::from_byte_array(&data), Err(_) => Err(InvalidSecretKey), } } + /// Converts a 32-byte array to a secret key. + /// + /// # Examples + /// + /// ``` + /// use secp256k1::SecretKey; + /// let sk = SecretKey::from_byte_array(&[0xcd; 32]).expect("32 bytes, within curve order"); + /// ``` + #[inline] + pub fn from_byte_array(data: &[u8; constants::SECRET_KEY_SIZE]) -> Result { + unsafe { + if ffi::secp256k1_ec_seckey_verify(ffi::secp256k1_context_no_precomp, data.as_c_ptr()) + == 0 + { + return Err(InvalidSecretKey); + } + } + Ok(SecretKey(*data)) + } + /// Creates a new secret key using data from BIP-340 [`Keypair`]. /// /// # Examples @@ -442,17 +451,50 @@ impl PublicKey { /// Creates a public key directly from a slice. #[inline] pub fn from_slice(data: &[u8]) -> Result { - if data.is_empty() { - return Err(Error::InvalidPublicKey); + match data.len() { + constants::PUBLIC_KEY_SIZE => PublicKey::from_byte_array_compressed( + &<[u8; constants::PUBLIC_KEY_SIZE]>::try_from(data).unwrap(), + ), + constants::UNCOMPRESSED_PUBLIC_KEY_SIZE => PublicKey::from_byte_array_uncompressed( + &<[u8; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE]>::try_from(data).unwrap(), + ), + _ => Err(InvalidPublicKey), + } + } + + /// Creates a public key from a serialized array in compressed format. + #[inline] + pub fn from_byte_array_compressed( + data: &[u8; constants::PUBLIC_KEY_SIZE], + ) -> Result { + unsafe { + let mut pk = ffi::PublicKey::new(); + if ffi::secp256k1_ec_pubkey_parse( + ffi::secp256k1_context_no_precomp, + &mut pk, + data.as_c_ptr(), + constants::PUBLIC_KEY_SIZE, + ) == 1 + { + Ok(PublicKey(pk)) + } else { + Err(InvalidPublicKey) + } } + } + /// Creates a public key from a serialized array in uncompressed format. + #[inline] + pub fn from_byte_array_uncompressed( + data: &[u8; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE], + ) -> Result { unsafe { let mut pk = ffi::PublicKey::new(); if ffi::secp256k1_ec_pubkey_parse( ffi::secp256k1_context_no_precomp, &mut pk, data.as_c_ptr(), - data.len(), + constants::UNCOMPRESSED_PUBLIC_KEY_SIZE, ) == 1 { Ok(PublicKey(pk)) @@ -1163,10 +1205,22 @@ impl XOnlyPublicKey { /// slice does not represent a valid Secp256k1 point x coordinate. #[inline] pub fn from_slice(data: &[u8]) -> Result { - if data.is_empty() || data.len() != constants::SCHNORR_PUBLIC_KEY_SIZE { - return Err(Error::InvalidPublicKey); + match <[u8; constants::SCHNORR_PUBLIC_KEY_SIZE]>::try_from(data) { + Ok(data) => Self::from_byte_array(&data), + Err(_) => Err(InvalidPublicKey), } + } + /// Creates a schnorr public key directly from a byte array. + /// + /// # Errors + /// + /// Returns [`Error::InvalidPublicKey`] if the array does not represent a valid Secp256k1 point + /// x coordinate. + #[inline] + pub fn from_byte_array( + data: &[u8; constants::SCHNORR_PUBLIC_KEY_SIZE], + ) -> Result { unsafe { let mut pk = ffi::XOnlyPublicKey::new(); if ffi::secp256k1_xonly_pubkey_parse( From 537b85b793862096c6a335e0fd14315f64ef1995 Mon Sep 17 00:00:00 2001 From: "Jamil Lambert, PhD" Date: Tue, 10 Sep 2024 20:51:23 +0100 Subject: [PATCH 2/2] Deprecate and replace `from_slice` The PrivateKey and XOnlyPublicKey from_slice functions have been deprecated and calls to them from within the crate have been changed to use the equivalent array function. --- src/ecdh.rs | 1 + src/key.rs | 23 ++++++++++++++--------- src/schnorr.rs | 1 + 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/ecdh.rs b/src/ecdh.rs index f897beff0..382b3e440 100644 --- a/src/ecdh.rs +++ b/src/ecdh.rs @@ -64,6 +64,7 @@ impl SharedSecret { pub fn from_bytes(bytes: [u8; SHARED_SECRET_SIZE]) -> SharedSecret { SharedSecret(bytes) } /// Creates a shared secret from `bytes` slice. + #[deprecated(since = "TBD", note = "Use `from_bytes` instead.")] #[inline] pub fn from_slice(bytes: &[u8]) -> Result { match bytes.len() { diff --git a/src/key.rs b/src/key.rs index 56a9faebe..463ae40a3 100644 --- a/src/key.rs +++ b/src/key.rs @@ -115,7 +115,7 @@ impl str::FromStr for SecretKey { fn from_str(s: &str) -> Result { let mut res = [0u8; constants::SECRET_KEY_SIZE]; match from_hex(s, &mut res) { - Ok(constants::SECRET_KEY_SIZE) => SecretKey::from_slice(&res), + Ok(constants::SECRET_KEY_SIZE) => SecretKey::from_byte_array(&res), _ => Err(Error::InvalidSecretKey), } } @@ -138,7 +138,7 @@ impl str::FromStr for SecretKey { /// use secp256k1::{SecretKey, Secp256k1, PublicKey}; /// /// let secp = Secp256k1::new(); -/// let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order"); +/// let secret_key = SecretKey::from_byte_array(&[0xcd; 32]).expect("32 bytes, within curve order"); /// let public_key = PublicKey::from_secret_key(&secp, &secret_key); /// # } /// ``` @@ -168,9 +168,13 @@ impl str::FromStr for PublicKey { fn from_str(s: &str) -> Result { let mut res = [0u8; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE]; match from_hex(s, &mut res) { - Ok(constants::PUBLIC_KEY_SIZE) => - PublicKey::from_slice(&res[0..constants::PUBLIC_KEY_SIZE]), - Ok(constants::UNCOMPRESSED_PUBLIC_KEY_SIZE) => PublicKey::from_slice(&res), + Ok(constants::PUBLIC_KEY_SIZE) => { + let bytes: [u8; constants::PUBLIC_KEY_SIZE] = + res[0..constants::PUBLIC_KEY_SIZE].try_into().unwrap(); + PublicKey::from_byte_array_compressed(&bytes) + } + Ok(constants::UNCOMPRESSED_PUBLIC_KEY_SIZE) => + PublicKey::from_byte_array_uncompressed(&res), _ => Err(Error::InvalidPublicKey), } } @@ -211,6 +215,7 @@ impl SecretKey { /// use secp256k1::SecretKey; /// let sk = SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order"); /// ``` + #[deprecated(since = "TBD", note = "Use `from_byte_array` instead.")] #[inline] pub fn from_slice(data: &[u8]) -> Result { match <[u8; constants::SECRET_KEY_SIZE]>::try_from(data) { @@ -362,7 +367,7 @@ impl SecretKey { impl From for SecretKey { /// Converts a 32-byte hash directly to a secret key without error paths. fn from(t: T) -> SecretKey { - SecretKey::from_slice(&t.into_32()).expect("failed to create secret key") + SecretKey::from_byte_array(&t.into_32()).expect("failed to create secret key") } } @@ -542,7 +547,7 @@ impl PublicKey { }; buf[1..].clone_from_slice(&pk.serialize()); - PublicKey::from_slice(&buf).expect("we know the buffer is valid") + PublicKey::from_byte_array_compressed(&buf).expect("we know the buffer is valid") } #[inline] @@ -1156,8 +1161,7 @@ impl str::FromStr for XOnlyPublicKey { fn from_str(s: &str) -> Result { let mut res = [0u8; constants::SCHNORR_PUBLIC_KEY_SIZE]; match from_hex(s, &mut res) { - Ok(constants::SCHNORR_PUBLIC_KEY_SIZE) => - XOnlyPublicKey::from_slice(&res[0..constants::SCHNORR_PUBLIC_KEY_SIZE]), + Ok(constants::SCHNORR_PUBLIC_KEY_SIZE) => XOnlyPublicKey::from_byte_array(&res), _ => Err(Error::InvalidPublicKey), } } @@ -1203,6 +1207,7 @@ impl XOnlyPublicKey { /// /// Returns [`Error::InvalidPublicKey`] if the length of the data slice is not 32 bytes or the /// slice does not represent a valid Secp256k1 point x coordinate. + #[deprecated(since = "TBD", note = "Use `from_byte_array` instead.")] #[inline] pub fn from_slice(data: &[u8]) -> Result { match <[u8; constants::SCHNORR_PUBLIC_KEY_SIZE]>::try_from(data) { diff --git a/src/schnorr.rs b/src/schnorr.rs index 55e94c25b..e83bd8fec 100644 --- a/src/schnorr.rs +++ b/src/schnorr.rs @@ -78,6 +78,7 @@ impl Signature { pub fn from_byte_array(sig: [u8; constants::SCHNORR_SIGNATURE_SIZE]) -> Self { Self(sig) } /// Creates a `Signature` directly from a slice. + #[deprecated(since = "TBD", note = "Use `from_byte_array` instead.")] #[inline] pub fn from_slice(data: &[u8]) -> Result { match data.len() {