|
17 | 17 |
|
18 | 18 | #[cfg(any(test, feature = "rand"))] use rand::Rng;
|
19 | 19 |
|
20 |
| -use core::{fmt, str}; |
| 20 | +use core::{fmt, ptr, str}; |
21 | 21 |
|
22 | 22 | use super::{from_hex, Secp256k1};
|
23 | 23 | use super::Error::{self, InvalidPublicKey, InvalidPublicKeySum, InvalidSecretKey};
|
@@ -674,6 +674,251 @@ impl<'a> From<&'a KeyPair> for PublicKey {
|
674 | 674 | }
|
675 | 675 | }
|
676 | 676 |
|
| 677 | +/// A x-only public key, used for verification of Schnorr signatures and serialized according to BIP-340. |
| 678 | +#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)] |
| 679 | +pub struct XOnlyPublicKey(ffi::XOnlyPublicKey); |
| 680 | + |
| 681 | +impl fmt::LowerHex for XOnlyPublicKey { |
| 682 | + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 683 | + let ser = self.serialize(); |
| 684 | + for ch in &ser[..] { |
| 685 | + write!(f, "{:02x}", *ch)?; |
| 686 | + } |
| 687 | + Ok(()) |
| 688 | + } |
| 689 | +} |
| 690 | + |
| 691 | +impl fmt::Display for XOnlyPublicKey { |
| 692 | + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 693 | + fmt::LowerHex::fmt(self, f) |
| 694 | + } |
| 695 | +} |
| 696 | + |
| 697 | +impl str::FromStr for XOnlyPublicKey { |
| 698 | + type Err = Error; |
| 699 | + fn from_str(s: &str) -> Result<XOnlyPublicKey, Error> { |
| 700 | + let mut res = [0u8; constants::SCHNORRSIG_PUBLIC_KEY_SIZE]; |
| 701 | + match from_hex(s, &mut res) { |
| 702 | + Ok(constants::SCHNORRSIG_PUBLIC_KEY_SIZE) => { |
| 703 | + XOnlyPublicKey::from_slice(&res[0..constants::SCHNORRSIG_PUBLIC_KEY_SIZE]) |
| 704 | + } |
| 705 | + _ => Err(Error::InvalidPublicKey), |
| 706 | + } |
| 707 | + } |
| 708 | +} |
| 709 | + |
| 710 | +impl XOnlyPublicKey { |
| 711 | + /// Obtains a raw const pointer suitable for use with FFI functions |
| 712 | + #[inline] |
| 713 | + pub fn as_ptr(&self) -> *const ffi::XOnlyPublicKey { |
| 714 | + &self.0 |
| 715 | + } |
| 716 | + |
| 717 | + /// Obtains a raw mutable pointer suitable for use with FFI functions |
| 718 | + #[inline] |
| 719 | + pub fn as_mut_ptr(&mut self) -> *mut ffi::XOnlyPublicKey { |
| 720 | + &mut self.0 |
| 721 | + } |
| 722 | + |
| 723 | + /// Creates a new Schnorr public key from a Schnorr key pair. |
| 724 | + #[inline] |
| 725 | + pub fn from_keypair<C: Signing>(secp: &Secp256k1<C>, keypair: &KeyPair) -> XOnlyPublicKey { |
| 726 | + let mut pk_parity = 0; |
| 727 | + unsafe { |
| 728 | + let mut xonly_pk = ffi::XOnlyPublicKey::new(); |
| 729 | + let ret = ffi::secp256k1_keypair_xonly_pub( |
| 730 | + secp.ctx, |
| 731 | + &mut xonly_pk, |
| 732 | + &mut pk_parity, |
| 733 | + keypair.as_ptr(), |
| 734 | + ); |
| 735 | + debug_assert_eq!(ret, 1); |
| 736 | + XOnlyPublicKey(xonly_pk) |
| 737 | + } |
| 738 | + } |
| 739 | + |
| 740 | + /// Creates a Schnorr public key directly from a slice |
| 741 | + /// |
| 742 | + /// # Errors |
| 743 | + /// |
| 744 | + /// Returns [`Error::InvalidPublicKey`] if the length of the data slice is not 32 bytes or the |
| 745 | + /// slice does not represent a valid Secp256k1 point x coordinate |
| 746 | + #[inline] |
| 747 | + pub fn from_slice(data: &[u8]) -> Result<XOnlyPublicKey, Error> { |
| 748 | + if data.is_empty() || data.len() != constants::SCHNORRSIG_PUBLIC_KEY_SIZE { |
| 749 | + return Err(Error::InvalidPublicKey); |
| 750 | + } |
| 751 | + |
| 752 | + unsafe { |
| 753 | + let mut pk = ffi::XOnlyPublicKey::new(); |
| 754 | + if ffi::secp256k1_xonly_pubkey_parse( |
| 755 | + ffi::secp256k1_context_no_precomp, |
| 756 | + &mut pk, |
| 757 | + data.as_c_ptr(), |
| 758 | + ) == 1 |
| 759 | + { |
| 760 | + Ok(XOnlyPublicKey(pk)) |
| 761 | + } else { |
| 762 | + Err(Error::InvalidPublicKey) |
| 763 | + } |
| 764 | + } |
| 765 | + } |
| 766 | + |
| 767 | + #[inline] |
| 768 | + /// Serialize the key as a byte-encoded x coordinate value (32 bytes). |
| 769 | + pub fn serialize(&self) -> [u8; constants::SCHNORRSIG_PUBLIC_KEY_SIZE] { |
| 770 | + let mut ret = [0u8; constants::SCHNORRSIG_PUBLIC_KEY_SIZE]; |
| 771 | + |
| 772 | + unsafe { |
| 773 | + let err = ffi::secp256k1_xonly_pubkey_serialize( |
| 774 | + ffi::secp256k1_context_no_precomp, |
| 775 | + ret.as_mut_c_ptr(), |
| 776 | + self.as_c_ptr(), |
| 777 | + ); |
| 778 | + debug_assert_eq!(err, 1); |
| 779 | + } |
| 780 | + ret |
| 781 | + } |
| 782 | + |
| 783 | + /// Tweak an x-only PublicKey by adding the generator multiplied with the given tweak to it. |
| 784 | + /// |
| 785 | + /// Returns a boolean representing the parity of the tweaked key, which can be provided to |
| 786 | + /// `tweak_add_check` which can be used to verify a tweak more efficiently than regenerating |
| 787 | + /// it and checking equality. Will return an error if the resulting key would be invalid or |
| 788 | + /// if the tweak was not a 32-byte length slice. |
| 789 | + pub fn tweak_add_assign<V: Verification>( |
| 790 | + &mut self, |
| 791 | + secp: &Secp256k1<V>, |
| 792 | + tweak: &[u8], |
| 793 | + ) -> Result<bool, Error> { |
| 794 | + if tweak.len() != 32 { |
| 795 | + return Err(Error::InvalidTweak); |
| 796 | + } |
| 797 | + |
| 798 | + unsafe { |
| 799 | + let mut pubkey = ffi::PublicKey::new(); |
| 800 | + let mut err = ffi::secp256k1_xonly_pubkey_tweak_add( |
| 801 | + secp.ctx, |
| 802 | + &mut pubkey, |
| 803 | + self.as_c_ptr(), |
| 804 | + tweak.as_c_ptr(), |
| 805 | + ); |
| 806 | + |
| 807 | + if err != 1 { |
| 808 | + return Err(Error::InvalidTweak); |
| 809 | + } |
| 810 | + |
| 811 | + let mut parity: ::secp256k1_sys::types::c_int = 0; |
| 812 | + err = ffi::secp256k1_xonly_pubkey_from_pubkey( |
| 813 | + secp.ctx, |
| 814 | + &mut self.0, |
| 815 | + &mut parity, |
| 816 | + &pubkey, |
| 817 | + ); |
| 818 | + |
| 819 | + if err == 0 { |
| 820 | + Err(Error::InvalidPublicKey) |
| 821 | + } else { |
| 822 | + Ok(parity != 0) |
| 823 | + } |
| 824 | + } |
| 825 | + } |
| 826 | + |
| 827 | + /// Verify that a tweak produced by `tweak_add_assign` was computed correctly |
| 828 | + /// |
| 829 | + /// Should be called on the original untweaked key. Takes the tweaked key and |
| 830 | + /// output parity from `tweak_add_assign` as input. |
| 831 | + /// |
| 832 | + /// Currently this is not much more efficient than just recomputing the tweak |
| 833 | + /// and checking equality. However, in future this API will support batch |
| 834 | + /// verification, which is significantly faster, so it is wise to design |
| 835 | + /// protocols with this in mind. |
| 836 | + pub fn tweak_add_check<V: Verification>( |
| 837 | + &self, |
| 838 | + secp: &Secp256k1<V>, |
| 839 | + tweaked_key: &Self, |
| 840 | + tweaked_parity: bool, |
| 841 | + tweak: [u8; 32], |
| 842 | + ) -> bool { |
| 843 | + let tweaked_ser = tweaked_key.serialize(); |
| 844 | + unsafe { |
| 845 | + let err = ffi::secp256k1_xonly_pubkey_tweak_add_check( |
| 846 | + secp.ctx, |
| 847 | + tweaked_ser.as_c_ptr(), |
| 848 | + if tweaked_parity { 1 } else { 0 }, |
| 849 | + &self.0, |
| 850 | + tweak.as_c_ptr(), |
| 851 | + ); |
| 852 | + |
| 853 | + err == 1 |
| 854 | + } |
| 855 | + } |
| 856 | +} |
| 857 | + |
| 858 | +impl CPtr for XOnlyPublicKey { |
| 859 | + type Target = ffi::XOnlyPublicKey; |
| 860 | + fn as_c_ptr(&self) -> *const Self::Target { |
| 861 | + self.as_ptr() |
| 862 | + } |
| 863 | + |
| 864 | + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { |
| 865 | + self.as_mut_ptr() |
| 866 | + } |
| 867 | +} |
| 868 | + |
| 869 | +/// Creates a new Schnorr public key from a FFI x-only public key |
| 870 | +impl From<ffi::XOnlyPublicKey> for XOnlyPublicKey { |
| 871 | + #[inline] |
| 872 | + fn from(pk: ffi::XOnlyPublicKey) -> XOnlyPublicKey { |
| 873 | + XOnlyPublicKey(pk) |
| 874 | + } |
| 875 | +} |
| 876 | + |
| 877 | +impl From<::key::PublicKey> for XOnlyPublicKey { |
| 878 | + fn from(src: ::key::PublicKey) -> XOnlyPublicKey { |
| 879 | + unsafe { |
| 880 | + let mut pk = ffi::XOnlyPublicKey::new(); |
| 881 | + assert_eq!( |
| 882 | + 1, |
| 883 | + ffi::secp256k1_xonly_pubkey_from_pubkey( |
| 884 | + ffi::secp256k1_context_no_precomp, |
| 885 | + &mut pk, |
| 886 | + ptr::null_mut(), |
| 887 | + src.as_c_ptr(), |
| 888 | + ) |
| 889 | + ); |
| 890 | + XOnlyPublicKey(pk) |
| 891 | + } |
| 892 | + } |
| 893 | +} |
| 894 | + |
| 895 | +#[cfg(feature = "serde")] |
| 896 | +impl ::serde::Serialize for XOnlyPublicKey { |
| 897 | + fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { |
| 898 | + if s.is_human_readable() { |
| 899 | + s.collect_str(self) |
| 900 | + } else { |
| 901 | + s.serialize_bytes(&self.serialize()) |
| 902 | + } |
| 903 | + } |
| 904 | +} |
| 905 | + |
| 906 | +#[cfg(feature = "serde")] |
| 907 | +impl<'de> ::serde::Deserialize<'de> for XOnlyPublicKey { |
| 908 | + fn deserialize<D: ::serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> { |
| 909 | + if d.is_human_readable() { |
| 910 | + d.deserialize_str(super::serde_util::FromStrVisitor::new( |
| 911 | + "a hex string representing 32 byte schnorr public key" |
| 912 | + )) |
| 913 | + } else { |
| 914 | + d.deserialize_bytes(super::serde_util::BytesVisitor::new( |
| 915 | + "raw 32 bytes schnorr public key", |
| 916 | + XOnlyPublicKey::from_slice |
| 917 | + )) |
| 918 | + } |
| 919 | + } |
| 920 | +} |
| 921 | + |
677 | 922 | #[cfg(test)]
|
678 | 923 | mod test {
|
679 | 924 | use Secp256k1;
|
|
0 commit comments