Skip to content

Commit aa6bdaf

Browse files
committed
Creating SecretKey and PublicKey from BIP-340 KeyPair
1 parent 4652ab6 commit aa6bdaf

File tree

3 files changed

+65
-4
lines changed

3 files changed

+65
-4
lines changed

secp256k1-sys/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,20 @@ extern "C" {
518518
internal_pubkey: *const XOnlyPublicKey,
519519
tweak32: *const c_uchar,
520520
) -> c_int;
521+
522+
#[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_4_1_keypair_sec")]
523+
pub fn secp256k1_keypair_sec(
524+
cx: *const Context,
525+
output_seckey: *mut c_uchar,
526+
keypair: *const KeyPair
527+
) -> c_int;
528+
529+
#[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_4_1_keypair_pub")]
530+
pub fn secp256k1_keypair_pub(
531+
cx: *const Context,
532+
output_pubkey: *mut PublicKey,
533+
keypair: *const KeyPair
534+
) -> c_int;
521535
}
522536

523537
/// A reimplementation of the C function `secp256k1_context_create` in rust.

src/key.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use constants;
2727
use ffi::{self, CPtr};
2828

2929
/// Secret 256-bit key used as `x` in an ECDSA signature
30-
pub struct SecretKey([u8; constants::SECRET_KEY_SIZE]);
30+
pub struct SecretKey(pub(crate) [u8; constants::SECRET_KEY_SIZE]);
3131
impl_array_newtype!(SecretKey, u8, constants::SECRET_KEY_SIZE);
3232
impl_pretty_debug!(SecretKey);
3333

@@ -66,7 +66,7 @@ pub const ONE_KEY: SecretKey = SecretKey([0, 0, 0, 0, 0, 0, 0, 0,
6666
/// A Secp256k1 public key, used for verification of signatures
6767
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
6868
#[repr(transparent)]
69-
pub struct PublicKey(ffi::PublicKey);
69+
pub struct PublicKey(pub(crate) ffi::PublicKey);
7070

7171
impl fmt::LowerHex for PublicKey {
7272
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

src/schnorrsig.rs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use core::{fmt, ptr, str};
1313
use ffi::{self, CPtr};
1414
use {constants, Secp256k1};
1515
use {Message, Signing, Verification};
16+
use SecretKey;
1617

1718
/// Represents a Schnorr signature.
1819
pub struct Signature([u8; constants::SCHNORRSIG_SIGNATURE_SIZE]);
@@ -449,6 +450,38 @@ impl<'de> ::serde::Deserialize<'de> for PublicKey {
449450
}
450451
}
451452

453+
impl SecretKey {
454+
/// Creates a new secret key using data from BIP-340 [`KeyPair`]
455+
pub fn from_keypair<V: Verification>(secp: &Secp256k1<V>, keypair: &KeyPair) -> Self {
456+
let mut sk = [0; constants::SECRET_KEY_SIZE];
457+
unsafe {
458+
let ret = ffi::secp256k1_keypair_sec(
459+
secp.ctx,
460+
sk.as_mut_c_ptr(),
461+
keypair.as_ptr()
462+
);
463+
debug_assert_eq!(ret, 1);
464+
}
465+
SecretKey(sk)
466+
}
467+
}
468+
469+
impl ::key::PublicKey {
470+
/// Creates a new compressed public key key using data from BIP-340 [`KeyPair`]
471+
pub fn from_keypair<C: Signing>(secp: &Secp256k1<C>, keypair: &KeyPair) -> Self {
472+
unsafe {
473+
let mut pk = ffi::PublicKey::new();
474+
let ret = ffi::secp256k1_keypair_pub(
475+
secp.ctx,
476+
&mut pk,
477+
keypair.as_ptr()
478+
);
479+
debug_assert_eq!(ret, 1);
480+
::key::PublicKey(pk)
481+
}
482+
}
483+
}
484+
452485
impl<C: Signing> Secp256k1<C> {
453486
fn schnorrsig_sign_helper(
454487
&self,
@@ -573,6 +606,7 @@ mod tests {
573606

574607
#[cfg(target_arch = "wasm32")]
575608
use wasm_bindgen_test::wasm_bindgen_test as test;
609+
use SecretKey;
576610

577611
macro_rules! hex_32 {
578612
($hex:expr) => {{
@@ -669,7 +703,7 @@ mod tests {
669703
}
670704

671705
#[test]
672-
fn pubkey_from_slice() {
706+
fn test_pubkey_from_slice() {
673707
assert_eq!(PublicKey::from_slice(&[]), Err(InvalidPublicKey));
674708
assert_eq!(PublicKey::from_slice(&[1, 2, 3]), Err(InvalidPublicKey));
675709
let pk = PublicKey::from_slice(&[
@@ -681,14 +715,27 @@ mod tests {
681715
}
682716

683717
#[test]
684-
fn pubkey_serialize_roundtrip() {
718+
fn test_pubkey_serialize_roundtrip() {
685719
let secp = Secp256k1::new();
686720
let (_, pubkey) = secp.generate_schnorrsig_keypair(&mut thread_rng());
687721
let ser = pubkey.serialize();
688722
let pubkey2 = PublicKey::from_slice(&ser).unwrap();
689723
assert_eq!(pubkey, pubkey2);
690724
}
691725

726+
#[test]
727+
fn test_xonly_key_extraction() {
728+
let secp = Secp256k1::new();
729+
let sk_str = "688C77BC2D5AAFF5491CF309D4753B732135470D05B7B2CD21ADD0744FE97BEF";
730+
let keypair = KeyPair::from_seckey_str(&secp, sk_str).unwrap();
731+
let sk = SecretKey::from_keypair(&secp, &keypair);
732+
assert_eq!(SecretKey::from_str(sk_str).unwrap(), sk);
733+
let pk = ::key::PublicKey::from_keypair(&secp, &keypair);
734+
assert_eq!(::key::PublicKey::from_secret_key(&secp, &sk), pk);
735+
let xpk = PublicKey::from_keypair(&secp, &keypair);
736+
assert_eq!(PublicKey::from(pk), xpk);
737+
}
738+
692739
#[test]
693740
fn test_pubkey_from_bad_slice() {
694741
// Bad sizes

0 commit comments

Comments
 (0)