Skip to content

Commit a30e9bb

Browse files
committed
Merge #430: Add convenience methods for keys
f08276a Add convenience methods for keys (Tobin Harding) b4c7fa0 Let the compiler work out int size (Tobin Harding) c612130 Borrow secret key (Tobin Harding) Pull request description: We have a bunch of `from_<key>` methods for converting between key types. To make the API more ergonomic to use we can add methods that do the same but called on a variable e.g., once applied the following are equivalent: - `let pk = PublicKey::from_keypair(kp)` - `let pk = kp.public_key()` Do this for `SecretKey`, `PublicKey`, `KeyPair`, and `XOnlyKeyPair`. Fixes: #428 ### Note to reviewers - `XOnlyPublicKey` -> `PublicKey` logic is made up by me, I could not work out how to get `libsecp256k1` to do this. - Please review the tests carefully, they include assumptions based on my current understanding of the cryptography :) ACKs for top commit: sanket1729: ACK f08276a. Thanks for going through all the iterations. apoelstra: ACK f08276a Tree-SHA512: 1503a6e570a3958110c6f24cd6d075fe5694b3b32b91a7a9d332c63aa0806198ff10bdd95e7f9de0cf73cbf4e3655c6826bd04e5044d1b019f551471b187c8ea
2 parents f02ff65 + f08276a commit a30e9bb

File tree

3 files changed

+258
-22
lines changed

3 files changed

+258
-22
lines changed

src/key.rs

Lines changed: 250 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,31 @@ impl SecretKey {
292292
pub fn sign_ecdsa(&self, msg: Message) -> ecdsa::Signature {
293293
SECP256K1.sign_ecdsa(&msg, self)
294294
}
295+
296+
/// Returns the [`KeyPair`] for this [`SecretKey`].
297+
///
298+
/// This is equivalent to using [`KeyPair::from_secret_key`].
299+
#[inline]
300+
pub fn keypair<C: Signing>(&self, secp: &Secp256k1<C>) -> KeyPair {
301+
KeyPair::from_secret_key(secp, self)
302+
}
303+
304+
/// Returns the [`PublicKey`] for this [`SecretKey`].
305+
///
306+
/// This is equivalent to using [`PublicKey::from_secret_key`].
307+
#[inline]
308+
pub fn public_key<C: Signing>(&self, secp: &Secp256k1<C>) -> PublicKey {
309+
PublicKey::from_secret_key(secp, self)
310+
}
311+
312+
/// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this [`SecretKey`].
313+
///
314+
/// This is equivalent to `XOnlyPublicKey::from_keypair(self.keypair(secp))`.
315+
#[inline]
316+
pub fn x_only_public_key<C: Signing>(&self, secp: &Secp256k1<C>) -> (XOnlyPublicKey, Parity) {
317+
let kp = self.keypair(secp);
318+
XOnlyPublicKey::from_keypair(&kp)
319+
}
295320
}
296321

297322
#[cfg(feature = "serde")]
@@ -418,6 +443,20 @@ impl PublicKey {
418443
}
419444
}
420445

446+
/// Creates a [`PublicKey`] using the key material from `pk` combined with the `parity`.
447+
pub fn from_x_only_public_key(pk: XOnlyPublicKey, parity: Parity) -> PublicKey {
448+
let mut buf = [0u8; 33];
449+
450+
// First byte of a compressed key should be `0x02 AND parity`.
451+
buf[0] = match parity {
452+
Parity::Even => 0x02,
453+
Parity::Odd => 0x03,
454+
};
455+
buf[1..].clone_from_slice(&pk.serialize());
456+
457+
PublicKey::from_slice(&buf).expect("we know the buffer is valid")
458+
}
459+
421460
#[inline]
422461
/// Serializes the key as a byte-encoded pair of values. In compressed form the y-coordinate is
423462
/// represented by only a single bit, as x determines it up to one bit.
@@ -589,6 +628,25 @@ impl PublicKey {
589628
}
590629
}
591630
}
631+
632+
/// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this [`PublicKey`].
633+
#[inline]
634+
pub fn x_only_public_key(&self) -> (XOnlyPublicKey, Parity) {
635+
let mut pk_parity = 0;
636+
unsafe {
637+
let mut xonly_pk = ffi::XOnlyPublicKey::new();
638+
let ret = ffi::secp256k1_xonly_pubkey_from_pubkey(
639+
ffi::secp256k1_context_no_precomp,
640+
&mut xonly_pk,
641+
&mut pk_parity,
642+
self.as_ptr(),
643+
);
644+
debug_assert_eq!(ret, 1);
645+
let parity = Parity::from_i32(pk_parity).expect("should not panic, pk_parity is 0 or 1");
646+
647+
(XOnlyPublicKey(xonly_pk), parity)
648+
}
649+
}
592650
}
593651

594652
impl CPtr for PublicKey {
@@ -674,7 +732,7 @@ impl Ord for PublicKey {
674732
///
675733
/// let secp = Secp256k1::new();
676734
/// let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng());
677-
/// let key_pair = KeyPair::from_secret_key(&secp, secret_key);
735+
/// let key_pair = KeyPair::from_secret_key(&secp, &secret_key);
678736
/// # }
679737
/// ```
680738
/// [`Deserialize`]: serde::Deserialize
@@ -700,7 +758,7 @@ impl KeyPair {
700758
#[inline]
701759
pub fn from_secret_key<C: Signing>(
702760
secp: &Secp256k1<C>,
703-
sk: SecretKey,
761+
sk: &SecretKey,
704762
) -> KeyPair {
705763
unsafe {
706764
let mut kp = ffi::KeyPair::new();
@@ -860,9 +918,27 @@ impl KeyPair {
860918
}
861919
}
862920

863-
/// Gets the [XOnlyPublicKey] for this [KeyPair].
921+
/// Returns the [`SecretKey`] for this [`KeyPair`].
922+
///
923+
/// This is equivalent to using [`SecretKey::from_keypair`].
924+
#[inline]
925+
pub fn secret_key(&self) -> SecretKey {
926+
SecretKey::from_keypair(self)
927+
}
928+
929+
/// Returns the [`PublicKey`] for this [`KeyPair`].
930+
///
931+
/// This is equivalent to using [`PublicKey::from_keypair`].
932+
#[inline]
933+
pub fn public_key(&self) -> PublicKey {
934+
PublicKey::from_keypair(self)
935+
}
936+
937+
/// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this [`KeyPair`].
938+
///
939+
/// This is equivalent to using [`XOnlyPublicKey::from_keypair`].
864940
#[inline]
865-
pub fn public_key(&self) -> XOnlyPublicKey {
941+
pub fn x_only_public_key(&self) -> (XOnlyPublicKey, Parity) {
866942
XOnlyPublicKey::from_keypair(self)
867943
}
868944

@@ -1008,9 +1084,9 @@ impl XOnlyPublicKey {
10081084
&mut self.0
10091085
}
10101086

1011-
/// Creates a new Schnorr public key from a Schnorr key pair.
1087+
/// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for `keypair`.
10121088
#[inline]
1013-
pub fn from_keypair(keypair: &KeyPair) -> XOnlyPublicKey {
1089+
pub fn from_keypair(keypair: &KeyPair) -> (XOnlyPublicKey, Parity) {
10141090
let mut pk_parity = 0;
10151091
unsafe {
10161092
let mut xonly_pk = ffi::XOnlyPublicKey::new();
@@ -1021,7 +1097,9 @@ impl XOnlyPublicKey {
10211097
keypair.as_ptr(),
10221098
);
10231099
debug_assert_eq!(ret, 1);
1024-
XOnlyPublicKey(xonly_pk)
1100+
let parity = Parity::from_i32(pk_parity).expect("should not panic, pk_parity is 0 or 1");
1101+
1102+
(XOnlyPublicKey(xonly_pk), parity)
10251103
}
10261104
}
10271105

@@ -1092,7 +1170,7 @@ impl XOnlyPublicKey {
10921170
/// thread_rng().fill_bytes(&mut tweak);
10931171
///
10941172
/// let mut key_pair = KeyPair::new(&secp, &mut thread_rng());
1095-
/// let mut public_key = key_pair.public_key();
1173+
/// let (mut public_key, _parity) = key_pair.x_only_public_key();
10961174
/// public_key.tweak_add_assign(&secp, &tweak).expect("Improbable to fail with a randomly generated tweak");
10971175
/// # }
10981176
/// ```
@@ -1105,6 +1183,7 @@ impl XOnlyPublicKey {
11051183
return Err(Error::InvalidTweak);
11061184
}
11071185

1186+
let mut pk_parity = 0;
11081187
unsafe {
11091188
let mut pubkey = ffi::PublicKey::new();
11101189
let mut err = ffi::secp256k1_xonly_pubkey_tweak_add(
@@ -1117,18 +1196,17 @@ impl XOnlyPublicKey {
11171196
return Err(Error::InvalidTweak);
11181197
}
11191198

1120-
let mut parity: ::secp256k1_sys::types::c_int = 0;
11211199
err = ffi::secp256k1_xonly_pubkey_from_pubkey(
11221200
secp.ctx,
11231201
&mut self.0,
1124-
&mut parity,
1202+
&mut pk_parity,
11251203
&pubkey,
11261204
);
11271205
if err == 0 {
11281206
return Err(Error::InvalidPublicKey);
11291207
}
11301208

1131-
Parity::from_i32(parity).map_err(Into::into)
1209+
Parity::from_i32(pk_parity).map_err(Into::into)
11321210
}
11331211
}
11341212

@@ -1157,7 +1235,7 @@ impl XOnlyPublicKey {
11571235
/// thread_rng().fill_bytes(&mut tweak);
11581236
///
11591237
/// let mut key_pair = KeyPair::new(&secp, &mut thread_rng());
1160-
/// let mut public_key = key_pair.public_key();
1238+
/// let (mut public_key, _) = key_pair.x_only_public_key();
11611239
/// let original = public_key;
11621240
/// let parity = public_key.tweak_add_assign(&secp, &tweak).expect("Improbable to fail with a randomly generated tweak");
11631241
/// assert!(original.tweak_add_check(&secp, &public_key, parity, tweak));
@@ -1183,6 +1261,14 @@ impl XOnlyPublicKey {
11831261
err == 1
11841262
}
11851263
}
1264+
1265+
/// Returns the [`PublicKey`] for this [`XOnlyPublicKey`].
1266+
///
1267+
/// This is equivalent to using [`PublicKey::from_xonly_and_parity(self, parity)`].
1268+
#[inline]
1269+
pub fn public_key(&self, parity: Parity) -> PublicKey {
1270+
PublicKey::from_x_only_public_key(*self, parity)
1271+
}
11861272
}
11871273

11881274
/// Represents the parity passed between FFI function calls.
@@ -1420,7 +1506,7 @@ pub mod serde_keypair {
14201506

14211507
Ok(KeyPair::from_secret_key(
14221508
&::SECP256K1,
1423-
secret_key,
1509+
&secret_key,
14241510
))
14251511
}
14261512
}
@@ -1972,12 +2058,15 @@ mod test {
19722058
thread_rng().fill_bytes(&mut tweak);
19732059

19742060
let mut kp = KeyPair::new(&s, &mut thread_rng());
1975-
let mut pk = kp.public_key();
2061+
let (mut pk, _parity) = kp.x_only_public_key();
19762062

19772063
let orig_pk = pk;
19782064
kp.tweak_add_assign(&s, &tweak).expect("Tweak error");
19792065
let parity = pk.tweak_add_assign(&s, &tweak).expect("Tweak error");
1980-
assert_eq!(XOnlyPublicKey::from_keypair(&kp), pk);
2066+
2067+
let (back, _) = XOnlyPublicKey::from_keypair(&kp);
2068+
2069+
assert_eq!(back, pk);
19812070
assert!(orig_pk.tweak_add_check(&s, &pk, parity, tweak));
19822071
}
19832072
}
@@ -2046,4 +2135,150 @@ mod test {
20462135
assert_tokens(&sk.readable(), &[Token::Str(SK_STR)]);
20472136
assert_tokens(&sk.readable(), &[Token::String(SK_STR)]);
20482137
}
2138+
2139+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2140+
fn keys() -> (SecretKey, PublicKey, KeyPair, XOnlyPublicKey) {
2141+
let secp = Secp256k1::new();
2142+
2143+
static SK_BYTES: [u8; 32] = [
2144+
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
2145+
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
2146+
0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
2147+
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
2148+
];
2149+
2150+
static PK_BYTES: [u8; 32] = [
2151+
0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f,
2152+
0x1c, 0x97, 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d,
2153+
0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, 0xd0, 0x54,
2154+
0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66
2155+
];
2156+
2157+
let mut pk_bytes = [0u8; 33];
2158+
pk_bytes[0] = 0x02; // Use positive Y co-ordinate.
2159+
pk_bytes[1..].clone_from_slice(&PK_BYTES);
2160+
2161+
let sk = SecretKey::from_slice(&SK_BYTES).expect("failed to parse sk bytes");
2162+
let pk = PublicKey::from_slice(&pk_bytes).expect("failed to create pk from iterator");
2163+
let kp = KeyPair::from_secret_key(&secp, &sk);
2164+
let xonly = XOnlyPublicKey::from_slice(&PK_BYTES).expect("failed to get xonly from slice");
2165+
2166+
(sk, pk, kp, xonly)
2167+
}
2168+
2169+
#[test]
2170+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2171+
fn convert_public_key_to_xonly_public_key() {
2172+
let (_sk, pk, _kp, want) = keys();
2173+
let (got, parity) = pk.x_only_public_key();
2174+
2175+
assert_eq!(parity, Parity::Even);
2176+
assert_eq!(got, want)
2177+
}
2178+
2179+
#[test]
2180+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2181+
fn convert_secret_key_to_public_key() {
2182+
let secp = Secp256k1::new();
2183+
2184+
let (sk, want, _kp, _xonly) = keys();
2185+
let got = sk.public_key(&secp);
2186+
2187+
assert_eq!(got, want)
2188+
}
2189+
2190+
#[test]
2191+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2192+
fn convert_secret_key_to_x_only_public_key() {
2193+
let secp = Secp256k1::new();
2194+
2195+
let (sk, _pk, _kp, want) = keys();
2196+
let (got, parity) = sk.x_only_public_key(&secp);
2197+
2198+
assert_eq!(parity, Parity::Even);
2199+
assert_eq!(got, want)
2200+
}
2201+
2202+
#[test]
2203+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2204+
fn convert_keypair_to_public_key() {
2205+
let (_sk, want, kp, _xonly) = keys();
2206+
let got = kp.public_key();
2207+
2208+
assert_eq!(got, want)
2209+
}
2210+
2211+
#[test]
2212+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2213+
fn convert_keypair_to_x_only_public_key() {
2214+
let (_sk, _pk, kp, want) = keys();
2215+
let (got, parity) = kp.x_only_public_key();
2216+
2217+
assert_eq!(parity, Parity::Even);
2218+
assert_eq!(got, want)
2219+
}
2220+
2221+
// SecretKey -> KeyPair -> SecretKey
2222+
#[test]
2223+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2224+
fn roundtrip_secret_key_via_keypair() {
2225+
let secp = Secp256k1::new();
2226+
let (sk, _pk, _kp, _xonly) = keys();
2227+
2228+
let kp = sk.keypair(&secp);
2229+
let back = kp.secret_key();
2230+
2231+
assert_eq!(back, sk)
2232+
}
2233+
2234+
// KeyPair -> SecretKey -> KeyPair
2235+
#[test]
2236+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2237+
fn roundtrip_keypair_via_secret_key() {
2238+
let secp = Secp256k1::new();
2239+
let (_sk, _pk, kp, _xonly) = keys();
2240+
2241+
let sk = kp.secret_key();
2242+
let back = sk.keypair(&secp);
2243+
2244+
assert_eq!(back, kp)
2245+
}
2246+
2247+
// XOnlyPublicKey -> PublicKey -> XOnlyPublicKey
2248+
#[test]
2249+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2250+
fn roundtrip_x_only_public_key_via_public_key() {
2251+
let (_sk, _pk, _kp, xonly) = keys();
2252+
2253+
let pk = xonly.public_key(Parity::Even);
2254+
let (back, parity) = pk.x_only_public_key();
2255+
2256+
assert_eq!(parity, Parity::Even);
2257+
assert_eq!(back, xonly)
2258+
}
2259+
2260+
// PublicKey -> XOnlyPublicKey -> PublicKey
2261+
#[test]
2262+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2263+
fn roundtrip_public_key_via_x_only_public_key() {
2264+
let (_sk, pk, _kp, _xonly) = keys();
2265+
2266+
let (xonly, parity) = pk.x_only_public_key();
2267+
let back = xonly.public_key(parity);
2268+
2269+
assert_eq!(back, pk)
2270+
}
2271+
2272+
#[test]
2273+
fn public_key_from_x_only_public_key_and_odd_parity() {
2274+
let s = "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166";
2275+
let mut want = String::from("03");
2276+
want.push_str(s);
2277+
2278+
let xonly = XOnlyPublicKey::from_str(s).expect("failed to parse xonly pubkey string");
2279+
let pk = xonly.public_key(Parity::Odd);
2280+
let got = format!("{}", pk);
2281+
2282+
assert_eq!(got, want)
2283+
}
20492284
}

0 commit comments

Comments
 (0)