Skip to content

Commit

Permalink
feat: KeyPair::from_seckey_byte_array
Browse files Browse the repository at this point in the history
Construct KeyPair directly from [u8; 32].
Deprecate KeyPair::from_seckey_slice
and replace all of its calls with the new method.
  • Loading branch information
uncomputable committed Feb 15, 2025
1 parent b991ef2 commit 9b66c2a
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 15 deletions.
34 changes: 23 additions & 11 deletions src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -859,16 +859,29 @@ impl Keypair {
/// # Errors
///
/// [`Error::InvalidSecretKey`] if the slice is not exactly 32 bytes long,
/// or if the encoded number exceeds the Secp256k1 field `p` value.
/// or if the encoded number is an invalid scalar.
#[deprecated(since = "TBD", note = "Use `from_seckey_byte_array` instead.")]
#[inline]
pub fn from_seckey_slice<C: Signing>(
secp: &Secp256k1<C>,
data: &[u8],
) -> Result<Keypair, Error> {
if data.is_empty() || data.len() != constants::SECRET_KEY_SIZE {
return Err(Error::InvalidSecretKey);
match <[u8; constants::SECRET_KEY_SIZE]>::try_from(data) {
Ok(data) => Self::from_seckey_byte_array(secp, data),
Err(_) => Err(Error::InvalidSecretKey),
}
}

/// Creates a [`Keypair`] directly from a secret key byte array.
///
/// # Errors
///
/// [`Error::InvalidSecretKey`] if the encoded number is an invalid scalar.
#[inline]
pub fn from_seckey_byte_array<C: Signing>(
secp: &Secp256k1<C>,
data: [u8; constants::SECRET_KEY_SIZE],
) -> Result<Keypair, Error> {
unsafe {
let mut kp = ffi::Keypair::new();
if ffi::secp256k1_keypair_create(secp.ctx.as_ptr(), &mut kp, data.as_c_ptr()) == 1 {
Expand All @@ -884,13 +897,12 @@ impl Keypair {
/// # Errors
///
/// [`Error::InvalidSecretKey`] if the string does not consist of exactly 64 hex characters,
/// or if the encoded number exceeds the Secp256k1 field `p` value.
/// or if the encoded number is an invalid scalar.
#[inline]
pub fn from_seckey_str<C: Signing>(secp: &Secp256k1<C>, s: &str) -> Result<Keypair, Error> {
let mut res = [0u8; constants::SECRET_KEY_SIZE];
match from_hex(s, &mut res) {
Ok(constants::SECRET_KEY_SIZE) =>
Keypair::from_seckey_slice(secp, &res[0..constants::SECRET_KEY_SIZE]),
Ok(constants::SECRET_KEY_SIZE) => Keypair::from_seckey_byte_array(secp, res),
_ => Err(Error::InvalidSecretKey),
}
}
Expand All @@ -900,7 +912,7 @@ impl Keypair {
/// # Errors
///
/// [`Error::InvalidSecretKey`] if the string does not consist of exactly 64 hex characters,
/// or if the encoded number exceeds the Secp256k1 field `p` value.
/// or if the encoded number is an invalid scalar.
#[inline]
#[cfg(feature = "global-context")]
pub fn from_seckey_str_global(s: &str) -> Result<Keypair, Error> {
Expand Down Expand Up @@ -1117,7 +1129,7 @@ impl<'de> serde::Deserialize<'de> for Keypair {
let ctx = Secp256k1::signing_only();

#[allow(clippy::needless_borrow)]
Keypair::from_seckey_slice(&ctx, &data)
Keypair::from_seckey_byte_array(&ctx, data)
});
d.deserialize_tuple(constants::SECRET_KEY_SIZE, visitor)
}
Expand Down Expand Up @@ -1665,7 +1677,7 @@ mod test {
#[cfg(all(feature = "std", not(secp256k1_fuzz)))]
fn erased_keypair_is_valid() {
let s = Secp256k1::new();
let kp = Keypair::from_seckey_slice(&s, &[1u8; constants::SECRET_KEY_SIZE])
let kp = Keypair::from_seckey_byte_array(&s, [1u8; constants::SECRET_KEY_SIZE])
.expect("valid secret key");
let mut kp2 = kp;
kp2.non_secure_erase();
Expand Down Expand Up @@ -2272,7 +2284,7 @@ mod test {
];
static SK_STR: &str = "01010101010101010001020304050607ffff0000ffff00006363636363636363";

let sk = Keypair::from_seckey_slice(SECP256K1, &SK_BYTES).unwrap();
let sk = Keypair::from_seckey_byte_array(SECP256K1, SK_BYTES).unwrap();
#[rustfmt::skip]
assert_tokens(&sk.compact(), &[
Token::Tuple{ len: 32 },
Expand Down Expand Up @@ -2452,7 +2464,7 @@ mod test {

static PK_STR: &str = "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166";

let kp = Keypair::from_seckey_slice(crate::SECP256K1, &SK_BYTES).unwrap();
let kp = Keypair::from_seckey_byte_array(crate::SECP256K1, SK_BYTES).unwrap();
let (pk, _parity) = XOnlyPublicKey::from_keypair(&kp);

#[rustfmt::skip]
Expand Down
6 changes: 3 additions & 3 deletions src/schnorr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ mod tests {
0x63, 0x63, 0x63, 0x63,
];

let kp = Keypair::from_seckey_slice(&secp, &SK_BYTES).expect("sk");
let kp = Keypair::from_seckey_byte_array(&secp, SK_BYTES).expect("sk");

// In fuzzing mode secret->public key derivation is different, so
// hard-code the expected result.
Expand Down Expand Up @@ -473,7 +473,7 @@ mod tests {
let s = Secp256k1::new();

let msg = [1; 32];
let keypair = Keypair::from_seckey_slice(&s, &[2; 32]).unwrap();
let keypair = Keypair::from_seckey_byte_array(&s, [2; 32]).unwrap();
let aux = [3u8; 32];
let sig = s.sign_schnorr_with_aux_rand(&msg, &keypair, &aux);
static SIG_BYTES: [u8; constants::SCHNORR_SIGNATURE_SIZE] = [
Expand Down Expand Up @@ -706,7 +706,7 @@ mod tests {
} in vectors
{
if let (Some(secret_key), Some(aux_rand)) = (secret_key, aux_rand) {
let keypair = Keypair::from_seckey_slice(&secp, &secret_key).unwrap();
let keypair = Keypair::from_seckey_byte_array(&secp, secret_key).unwrap();
assert_eq!(keypair.x_only_public_key().0.serialize(), public_key);
let sig = secp.sign_schnorr_with_aux_rand(&message, &keypair, &aux_rand);
assert_eq!(sig.to_byte_array(), signature);
Expand Down
2 changes: 1 addition & 1 deletion tests/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ fn bincode_public_key() {
#[cfg(feature = "global-context")]
fn bincode_keypair() {
let secp = Secp256k1::new();
let kp = Keypair::from_seckey_slice(&secp, &SK_BYTES).expect("failed to create keypair");
let kp = Keypair::from_seckey_byte_array(&secp, SK_BYTES).expect("failed to create keypair");
let ser = bincode::serialize(&kp).unwrap();

assert_eq!(ser, SK_BYTES);
Expand Down

0 comments on commit 9b66c2a

Please sign in to comment.