Skip to content

Commit

Permalink
Add convenience methods for keys
Browse files Browse the repository at this point in the history
We have a bunch of `from_<key>` methods for converting between key types.
To improve the API and make it more ergonomic to use we can add methods
that do the same but can be called on the initial key instead of on the
resulting key's type. 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`.
  • Loading branch information
tcharding committed Mar 29, 2022
1 parent 69dbaf1 commit 0be9a1f
Show file tree
Hide file tree
Showing 2 changed files with 256 additions and 15 deletions.
257 changes: 248 additions & 9 deletions src/key.rs
Expand Up @@ -292,6 +292,31 @@ impl SecretKey {
pub fn sign_ecdsa(&self, msg: Message) -> ecdsa::Signature {
SECP256K1.sign_ecdsa(&msg, self)
}

/// Returns the [`KeyPair`] for this [`SecretKey`].
///
/// This is equivalent to using [`KeyPair::from_secret_key`].
#[inline]
pub fn keypair<C: Signing>(&self, secp: &Secp256k1<C>) -> KeyPair {
KeyPair::from_secret_key(secp, self)
}

/// Returns the [`PublicKey`] for this [`SecretKey`].
///
/// This is equivalent to using [`PublicKey::from_secret_key`].
#[inline]
pub fn public_key<C: Signing>(&self, secp: &Secp256k1<C>) -> PublicKey {
PublicKey::from_secret_key(secp, self)
}

/// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this [`SecretKey`].
///
/// This is equivalent to `XOnlyPublicKey::from_keypair(self.keypair(secp))`.
#[inline]
pub fn x_only_public_key<C: Signing>(&self, secp: &Secp256k1<C>) -> Result<(XOnlyPublicKey, Parity), Error> {
let kp = self.keypair(secp);
XOnlyPublicKey::from_keypair(&kp)
}
}

#[cfg(feature = "serde")]
Expand Down Expand Up @@ -418,6 +443,19 @@ impl PublicKey {
}
}

/// Creates a [`PublicKey`] using the key material from `xonly` combined with `parity`.
pub fn from_x_only_public_key_and_parity(xonly: XOnlyPublicKey, parity: Parity) -> Result<PublicKey, Error> {
let mut buf = [0u8; 33];

// First byte of a compressed key should be `0x02 AND party`.
buf[0] = match parity {
Parity::Even => 2u8,
Parity::Odd => 3u8,
};
buf[1..].clone_from_slice(&xonly.serialize());
PublicKey::from_slice(&buf)
}

#[inline]
/// Serializes the key as a byte-encoded pair of values. In compressed form the y-coordinate is
/// represented by only a single bit, as x determines it up to one bit.
Expand Down Expand Up @@ -589,6 +627,25 @@ impl PublicKey {
}
}
}

/// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this [`PublicKey`].
#[inline]
pub fn x_only_public_key<C: Signing>(&self, secp: &Secp256k1<C>) -> Result<(XOnlyPublicKey, Parity), Error> {
let mut pk_parity = 0;
unsafe {
let mut xonly_pk = ffi::XOnlyPublicKey::new();
let ret = ffi::secp256k1_xonly_pubkey_from_pubkey(
secp.ctx,
&mut xonly_pk,
&mut pk_parity,
self.as_ptr(),
);
debug_assert_eq!(ret, 1);
let parity = Parity::from_i32(pk_parity)?;

Ok((XOnlyPublicKey(xonly_pk), parity))
}
}
}

impl CPtr for PublicKey {
Expand Down Expand Up @@ -866,9 +923,27 @@ impl KeyPair {
}
}

/// Gets the [XOnlyPublicKey] for this [KeyPair].
/// Returns the [`SecretKey`] for this [`KeyPair`].
///
/// This is equivalent to using [`SecretKey::from_keypair`].
#[inline]
pub fn public_key(&self) -> XOnlyPublicKey {
pub fn secret_key(&self) -> SecretKey {
SecretKey::from_keypair(self)
}

/// Returns the [`PublicKey`] for this [`KeyPair`].
///
/// This is equivalent to using [`PublicKey::from_keypair`].
#[inline]
pub fn public_key(&self) -> PublicKey {
PublicKey::from_keypair(self)
}

/// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this [`KeyPair`].
///
/// This is equivalent to using [`XOnlyPublicKey::from_keypair`].
#[inline]
pub fn x_only_public_key(&self) -> Result<(XOnlyPublicKey, Parity), Error> {
XOnlyPublicKey::from_keypair(self)
}

Expand Down Expand Up @@ -1016,7 +1091,7 @@ impl XOnlyPublicKey {

/// Creates a new Schnorr public key from a Schnorr key pair.
#[inline]
pub fn from_keypair(keypair: &KeyPair) -> XOnlyPublicKey {
pub fn from_keypair(keypair: &KeyPair) -> Result<(XOnlyPublicKey, Parity), Error> {
let mut pk_parity = 0;
unsafe {
let mut xonly_pk = ffi::XOnlyPublicKey::new();
Expand All @@ -1027,7 +1102,9 @@ impl XOnlyPublicKey {
keypair.as_ptr(),
);
debug_assert_eq!(ret, 1);
XOnlyPublicKey(xonly_pk)
let parity = Parity::from_i32(pk_parity)?;

Ok((XOnlyPublicKey(xonly_pk), parity))
}
}

Expand Down Expand Up @@ -1098,7 +1175,7 @@ impl XOnlyPublicKey {
/// thread_rng().fill_bytes(&mut tweak);
///
/// let mut key_pair = KeyPair::new(&secp, &mut thread_rng());
/// let mut public_key = key_pair.public_key();
/// let mut public_key = key_pair.x_only_public_key();
/// public_key.tweak_add_assign(&secp, &tweak).expect("Improbable to fail with a randomly generated tweak");
/// # }
/// ```
Expand Down Expand Up @@ -1163,7 +1240,7 @@ impl XOnlyPublicKey {
/// thread_rng().fill_bytes(&mut tweak);
///
/// let mut key_pair = KeyPair::new(&secp, &mut thread_rng());
/// let mut public_key = key_pair.public_key();
/// let mut public_key = key_pair.x_only_public_key();
/// let original = public_key;
/// let parity = public_key.tweak_add_assign(&secp, &tweak).expect("Improbable to fail with a randomly generated tweak");
/// assert!(original.tweak_add_check(&secp, &public_key, parity, tweak));
Expand All @@ -1189,6 +1266,14 @@ impl XOnlyPublicKey {
err == 1
}
}

/// Returns the [`PublicKey`] for this [`XOnlyPublicKey`].
///
/// This is equivalent to using [`PublicKey::from_x_only_public_key_and_parity(self, parity)`].
#[inline]
pub fn public_key(&self, parity: Parity) -> Result<PublicKey, Error> {
PublicKey::from_x_only_public_key_and_parity(*self, parity)
}
}

/// Represents the parity passed between FFI function calls.
Expand Down Expand Up @@ -1426,7 +1511,7 @@ pub mod serde_keypair {

Ok(KeyPair::from_secret_key(
&::SECP256K1,
secret_key,
&secret_key,
))
}
}
Expand Down Expand Up @@ -1978,12 +2063,15 @@ mod test {
thread_rng().fill_bytes(&mut tweak);

let mut kp = KeyPair::new(&s, &mut thread_rng());
let mut pk = kp.public_key();
let (mut pk, _parity) = kp.x_only_public_key().expect("failed to get xonly pubkey");

let orig_pk = pk;
kp.tweak_add_assign(&s, &tweak).expect("Tweak error");
let parity = pk.tweak_add_assign(&s, &tweak).expect("Tweak error");
assert_eq!(XOnlyPublicKey::from_keypair(&kp), pk);

let (back, _) = XOnlyPublicKey::from_keypair(&kp).expect("failed to get xonly pubkey from keypair");

assert_eq!(back, pk);
assert!(orig_pk.tweak_add_check(&s, &pk, parity, tweak));
}
}
Expand Down Expand Up @@ -2052,4 +2140,155 @@ mod test {
assert_tokens(&sk.readable(), &[Token::Str(SK_STR)]);
assert_tokens(&sk.readable(), &[Token::String(SK_STR)]);
}

#[cfg(any(feature = "alloc", feature = "std"))]
fn keys() -> (SecretKey, PublicKey, KeyPair, XOnlyPublicKey) {
let secp = Secp256k1::new();

static SK_BYTES: [u8; 32] = [
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
];

static PK_BYTES: [u8; 32] = [
0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f,
0x1c, 0x97, 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d,
0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, 0xd0, 0x54,
0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66
];

let mut pk_bytes = [0u8; 33];
pk_bytes[0] = 0x02; // Use positive Y co-ordinate.
pk_bytes[1..].clone_from_slice(&PK_BYTES);

let sk = SecretKey::from_slice(&SK_BYTES).expect("failed to parse sk bytes");
let pk = PublicKey::from_slice(&pk_bytes).expect("failed to create pk from iterator");
let kp = KeyPair::from_secret_key(&secp, &sk);
let xonly = XOnlyPublicKey::from_slice(&PK_BYTES).expect("failed to get xonly from slice");

(sk, pk, kp, xonly)
}

#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn convert_public_key_to_xonly_public_key() {
let secp = Secp256k1::new();

let (_sk, pk, _kp, want) = keys();
let (got, parity) = pk.x_only_public_key(&secp).unwrap();

assert_eq!(parity, Parity::Even);
assert_eq!(got, want)
}

#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn convert_secret_key_to_public_key() {
let secp = Secp256k1::new();

let (sk, want, _kp, _xonly) = keys();
let got = sk.public_key(&secp);

assert_eq!(got, want)
}

#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn convert_secret_key_to_xonly_public_key() {
let secp = Secp256k1::new();

let (sk, _pk, _kp, want) = keys();
let (got, parity) = sk.x_only_public_key(&secp).unwrap();

assert_eq!(parity, Parity::Even);
assert_eq!(got, want)
}

#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn convert_keypair_to_public_key() {
let (_sk, want, kp, _xonly) = keys();
let got = kp.public_key();

assert_eq!(got, want)
}

#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn convert_keypair_to_xonly_public_key() {
let (_sk, _pk, kp, want) = keys();
let (got, parity) = kp.x_only_public_key().unwrap();

assert_eq!(parity, Parity::Even);
assert_eq!(got, want)
}

// SecretKey -> KeyPair -> SecretKey
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn roundtrip_secret_key_keypair() {
let secp = Secp256k1::new();
let (sk, _pk, _kp, _xonly) = keys();

let kp = sk.keypair(&secp);
let back = kp.secret_key();

assert_eq!(back, sk)
}

// KeyPair -> SecretKey -> KeyPair
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn roundtrip_keypair_secret_key() {
let secp = Secp256k1::new();
let (_sk, _pk, kp, _xonly) = keys();

let sk = kp.secret_key();
let back = sk.keypair(&secp);

assert_eq!(back, kp)
}

// XOnlyPublicKey -> PublicKey -> XOnlyPublicKey
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn roundtrip_xonly_public_key() {
let secp = Secp256k1::new();
let (_sk, _pk, _kp, xonly) = keys();

let pk = xonly.public_key(Parity::Even).expect("failed to get pubkey");
let (back, parity) = pk.x_only_public_key(&secp).expect("failed to get xonly pubkey");

assert_eq!(parity, Parity::Even);
assert_eq!(back, xonly)
}

// PublicKey -> XOnlyPublicKey -> PublicKey
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn roundtrip_public_key_xonly() {
let secp = Secp256k1::new();

let (_sk, pk, _kp, _xonly) = keys();

let (xonly, parity) = pk.x_only_public_key(&secp).expect("failed to get xonly pubkey");
let back = xonly.public_key(parity).expect("failed to get pubkey");

assert_eq!(back, pk)
}

#[test]
fn public_key_from_xonly_public_key_and_odd_parity() {
let s = "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166";
let mut want = String::from("03");
want.push_str(s);

let xonly = XOnlyPublicKey::from_str(s).expect("failed to parse xonly pubkey string");
let pk = xonly.public_key(Parity::Odd).expect("failed to get pubkey");
let got = format!("{}", pk);

assert_eq!(got, want)
}
}
14 changes: 8 additions & 6 deletions src/schnorr.rs
Expand Up @@ -268,7 +268,8 @@ impl <C: Signing> Secp256k1<C> {
rng: &mut R,
) -> (KeyPair, XOnlyPublicKey) {
let sk = KeyPair::new(self, rng);
let pubkey = XOnlyPublicKey::from_keypair(&sk);
// Only panics if conversion of the i32 parity returned by ffi layer is invalid.
let (pubkey, _parity) = XOnlyPublicKey::from_keypair(&sk).expect("failed to convert parity");
(sk, pubkey)
}
}
Expand Down Expand Up @@ -344,7 +345,7 @@ mod tests {

let mut rng = thread_rng();
let kp = KeyPair::new(&secp, &mut rng);
let pk = kp.public_key();
let (pk, _parity) = kp.x_only_public_key().expect("failed to get xonly pubkey");

let mut msg = [0u8; 32];

Expand Down Expand Up @@ -414,7 +415,7 @@ mod tests {
fn test_pubkey_serialize_roundtrip() {
let secp = Secp256k1::new();
let kp = KeyPair::new(&secp, &mut thread_rng());
let pk = kp.public_key();
let (pk, _parity) = kp.x_only_public_key().expect("failed to get xonly pubkey");

let ser = pk.serialize();
let pubkey2 = XOnlyPublicKey::from_slice(&ser).unwrap();
Expand All @@ -431,7 +432,7 @@ mod tests {
assert_eq!(SecretKey::from_str(sk_str).unwrap(), sk);
let pk = ::key::PublicKey::from_keypair(&keypair);
assert_eq!(::key::PublicKey::from_secret_key(&secp, &sk), pk);
let xpk = keypair.public_key();
let (xpk, _) = keypair.x_only_public_key().expect("failed to get xonly pubkey");
assert_eq!(XOnlyPublicKey::from(pk), xpk);
}

Expand Down Expand Up @@ -478,7 +479,8 @@ mod tests {

// In fuzzing mode secret->public key derivation is different, so
// hard-code the expected result.
kp.public_key()
let (pk, _parity) = kp.x_only_public_key().expect("failed to get xonly pubkey");
pk
};
#[cfg(fuzzing)]
let pk = XOnlyPublicKey::from_slice(&[0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f, 0x1c, 0x97, 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d, 0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, 0xd0, 0x54, 0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66]).expect("pk");
Expand Down Expand Up @@ -544,7 +546,7 @@ mod tests {

let secp = Secp256k1::new();
let kp = KeyPair::new(&secp, &mut DumbRng(0));
let pk = kp.public_key();
let (pk, _parity) = kp.x_only_public_key().expect("failed to get xonly pubkey");
assert_eq!(
&pk.serialize()[..],
&[
Expand Down

0 comments on commit 0be9a1f

Please sign in to comment.