Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 57 additions & 11 deletions src/key/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -845,12 +845,25 @@ impl str::FromStr for XOnlyPublicKey {
fn from_str(s: &str) -> Result<XOnlyPublicKey, Error> {
let mut res = [0u8; constants::SCHNORR_PUBLIC_KEY_SIZE];
match from_hex(s, &mut res) {
Ok(constants::SCHNORR_PUBLIC_KEY_SIZE) => XOnlyPublicKey::from_byte_array(res),
Ok(constants::SCHNORR_PUBLIC_KEY_SIZE) => XOnlyPublicKey::parse_byte_array(res),
_ => Err(Error::InvalidPublicKey),
}
}
}

impl TryFrom<[u8; constants::SCHNORR_PUBLIC_KEY_SIZE]> for XOnlyPublicKey {
type Error = Error;

/// Attempts to create an x-only public key from a 32-byte array.
///
/// # Errors
/// Returns [`Error::InvalidPublicKey`] if the bytes do not represent a valid
/// point on the secp256k1 curve.
fn try_from(data: [u8; constants::SCHNORR_PUBLIC_KEY_SIZE]) -> Result<Self, Self::Error> {
Self::parse_byte_array(data)
}
}

impl XOnlyPublicKey {
/// Returns the [`XOnlyPublicKey`] (and its [`Parity`]) for `keypair`.
#[inline]
Expand Down Expand Up @@ -882,19 +895,33 @@ impl XOnlyPublicKey {
#[inline]
pub fn from_slice(data: &[u8]) -> Result<XOnlyPublicKey, Error> {
match <[u8; constants::SCHNORR_PUBLIC_KEY_SIZE]>::try_from(data) {
Ok(data) => Self::from_byte_array(data),
Ok(data) => Self::parse_byte_array(data),
Err(_) => Err(InvalidPublicKey),
}
}

/// Creates a schnorr public key directly from a byte array.
/// Creates an x-only public key from a byte array.
///
/// # Errors
/// # Panics
///
/// Returns [`Error::InvalidPublicKey`] if the array does not represent a valid Secp256k1 point
/// x coordinate.
#[inline]
pub fn from_byte_array(
/// In debug builds, this function will panic if the input does not represent
/// a valid point on the curve. In release builds, it assumes the input is
/// valid. For untrusted input, use [`TryFrom`].
pub fn from_byte_array(data: [u8; constants::SCHNORR_PUBLIC_KEY_SIZE]) -> XOnlyPublicKey {
unsafe {
let mut pk = ffi::XOnlyPublicKey::new();
let ret = ffi::secp256k1_xonly_pubkey_parse(
ffi::secp256k1_context_no_precomp,
&mut pk,
data.as_c_ptr(),
);
debug_assert_eq!(ret, 1);
XOnlyPublicKey(pk)
}
}

/// Fallible version of `from_byte_array` for parsing untrusted input.
fn parse_byte_array(
data: [u8; constants::SCHNORR_PUBLIC_KEY_SIZE],
) -> Result<XOnlyPublicKey, Error> {
unsafe {
Expand All @@ -912,7 +939,26 @@ impl XOnlyPublicKey {
}
}

/// Serializes the `XOnlyPublicKey` into a 32-byte array.
///
/// This represents the x-coordinate of the point on the curve.
#[inline]
pub fn to_byte_array(&self) -> [u8; constants::SCHNORR_PUBLIC_KEY_SIZE] {
let mut ret = [0u8; constants::SCHNORR_PUBLIC_KEY_SIZE];

unsafe {
let err = ffi::secp256k1_xonly_pubkey_serialize(
ffi::secp256k1_context_no_precomp,
ret.as_mut_c_ptr(),
self.as_c_ptr(),
);
debug_assert_eq!(err, 1);
}
ret
}

#[inline]
#[deprecated(since = "TBD", note = "use XOnlyPublicKey::to_byte_array instead")]
/// Serializes the key as a byte-encoded x coordinate value (32 bytes).
pub fn serialize(&self) -> [u8; constants::SCHNORR_PUBLIC_KEY_SIZE] {
let mut ret = [0u8; constants::SCHNORR_PUBLIC_KEY_SIZE];
Expand Down Expand Up @@ -1255,7 +1301,7 @@ impl<'de> serde::Deserialize<'de> for XOnlyPublicKey {
} else {
let visitor = super::serde_util::Tuple32Visitor::new(
"raw 32 bytes schnorr public key",
XOnlyPublicKey::from_byte_array,
XOnlyPublicKey::parse_byte_array,
);
d.deserialize_tuple(constants::SCHNORR_PUBLIC_KEY_SIZE, visitor)
}
Expand Down Expand Up @@ -1348,7 +1394,7 @@ impl<'a> Arbitrary<'a> for XOnlyPublicKey {
}

u.fill_buffer(&mut bytes[..])?;
if let Ok(pk) = XOnlyPublicKey::from_byte_array(bytes) {
if let Ok(pk) = XOnlyPublicKey::parse_byte_array(bytes) {
return Ok(pk);
}
}
Expand Down Expand Up @@ -1998,7 +2044,7 @@ mod test {
let pk = PublicKey::from_slice(&pk_bytes).expect("failed to create pk from iterator");
let kp = Keypair::from_secret_key(&sk);
let xonly =
XOnlyPublicKey::from_byte_array(PK_BYTES).expect("failed to get xonly from slice");
XOnlyPublicKey::parse_byte_array(PK_BYTES).expect("failed to get xonly from slice");

(sk, pk, kp, xonly)
}
Expand Down
6 changes: 3 additions & 3 deletions src/schnorr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ mod tests {
let (pk, _parity) = kp.x_only_public_key();

let ser = pk.serialize();
let pubkey2 = XOnlyPublicKey::from_byte_array(ser).unwrap();
let pubkey2 = XOnlyPublicKey::try_from(ser).unwrap();
assert_eq!(pk, pubkey2);
}

Expand Down Expand Up @@ -514,7 +514,7 @@ mod tests {
170, 12, 208, 84, 74, 200, 135, 254, 145, 221, 209, 102,
];
static PK_STR: &str = "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166";
let pk = XOnlyPublicKey::from_byte_array(PK_BYTES).unwrap();
let pk = XOnlyPublicKey::try_from(PK_BYTES).unwrap();

assert_tokens(&sig.compact(), &[Token::BorrowedBytes(&SIG_BYTES[..])]);
assert_tokens(&sig.compact(), &[Token::Bytes(&SIG_BYTES[..])]);
Expand Down Expand Up @@ -733,7 +733,7 @@ mod tests {
assert_eq!(sig.to_byte_array(), signature);
}
let sig = Signature::from_byte_array(signature);
let is_verified = if let Ok(pubkey) = XOnlyPublicKey::from_byte_array(public_key) {
let is_verified = if let Ok(pubkey) = XOnlyPublicKey::try_from(public_key) {
verify(&sig, &message, &pubkey).is_ok()
} else {
false
Expand Down
2 changes: 1 addition & 1 deletion tests/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ macro_rules! bytes_rtt_test {

// Message is special because its to/from methods havve the name "digest" in them
// PublicKey is special because it has two serialization forms with different names (but maybe I should rename them?)
// FIXME XOnlyPublicKey should pass this
// ecdsa::Signature and SerializedSignature and RecoverableSignature are variable-length
// Scalar has to_be_bytes and to_le_bytes (and corresponding froms)
bytes_rtt_test!(rtt_c, XOnlyPublicKey);
bytes_rtt_test!(rtt_i, schnorr::Signature);
bytes_rtt_test!(rtt_g, ellswift::ElligatorSwift);

Expand Down
4 changes: 2 additions & 2 deletions tests/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ fn bincode_keypair() {

#[test]
fn bincode_x_only_public_key() {
let pk = XOnlyPublicKey::from_byte_array(XONLY_PK_BYTES)
.expect("failed to create xonly pk from slice");
let pk =
XOnlyPublicKey::try_from(XONLY_PK_BYTES).expect("failed to create xonly pk from slice");
let ser = bincode::serialize(&pk).unwrap();

assert_eq!(ser, XONLY_PK_BYTES);
Expand Down
Loading