nostr_types/types/
public_key.rs

1use crate::{Error, PrivateKey, Signature};
2use derive_more::{AsMut, AsRef, Deref, Display, From, FromStr, Into};
3pub use secp256k1::XOnlyPublicKey;
4use secp256k1::SECP256K1;
5use serde::de::{Deserializer, Visitor};
6use serde::ser::Serializer;
7use serde::{Deserialize, Serialize};
8#[cfg(feature = "speedy")]
9use speedy::{Context, Readable, Reader, Writable, Writer};
10use std::fmt;
11
12/// This is a public key, which identifies an actor (usually a person) and is shared.
13#[derive(AsMut, AsRef, Copy, Clone, Debug, Deref, Eq, From, Into, PartialEq, PartialOrd, Ord)]
14pub struct PublicKey([u8; 32]);
15
16impl PublicKey {
17    /// Render into a hexadecimal string
18    ///
19    /// Consider converting `.into()` a `PublicKeyHex` which is a wrapped type rather than a naked `String`
20    pub fn as_hex_string(&self) -> String {
21        hex::encode(self.0)
22    }
23
24    /// Create from a hexadecimal string
25    ///
26    /// If verify is true, will verify that it works as a XOnlyPublicKey. This
27    /// has a performance cost.
28    pub fn try_from_hex_string(v: &str, verify: bool) -> Result<PublicKey, Error> {
29        let vec: Vec<u8> = hex::decode(v)?;
30        // if it's not 32 bytes, dont even try
31        if vec.len() != 32 {
32            Err(Error::InvalidPublicKey)
33        } else {
34            if verify {
35                let _ = XOnlyPublicKey::from_slice(&vec)?;
36            }
37            Ok(PublicKey(vec.try_into().unwrap()))
38        }
39    }
40
41    /// Export as a bech32 encoded string
42    pub fn as_bech32_string(&self) -> String {
43        bech32::encode::<bech32::Bech32>(*crate::HRP_NPUB, self.0.as_slice()).unwrap()
44    }
45
46    /// Export as XOnlyPublicKey
47    pub fn as_xonly_public_key(&self) -> XOnlyPublicKey {
48        XOnlyPublicKey::from_slice(&self.0).unwrap()
49    }
50
51    /// Import from a bech32 encoded string
52    ///
53    /// If verify is true, will verify that it works as a XOnlyPublicKey. This
54    /// has a performance cost.
55    pub fn try_from_bech32_string(s: &str, verify: bool) -> Result<PublicKey, Error> {
56        let data = bech32::decode(s)?;
57        if data.0 != *crate::HRP_NPUB {
58            Err(Error::WrongBech32(
59                crate::HRP_NPUB.to_lowercase(),
60                data.0.to_lowercase(),
61            ))
62        } else if data.1.len() != 32 {
63            Err(Error::InvalidPublicKey)
64        } else {
65            if verify {
66                let _ = XOnlyPublicKey::from_slice(&data.1)?;
67            }
68            Ok(PublicKey(data.1.try_into().unwrap()))
69        }
70    }
71
72    /// Import from raw bytes
73    pub fn from_bytes(bytes: &[u8], verify: bool) -> Result<PublicKey, Error> {
74        if bytes.len() != 32 {
75            Err(Error::InvalidPublicKey)
76        } else {
77            if verify {
78                let _ = XOnlyPublicKey::from_slice(bytes)?;
79            }
80            Ok(PublicKey(bytes.try_into().unwrap()))
81        }
82    }
83
84    /// Export as raw bytes
85    #[inline]
86    pub fn as_bytes(&self) -> &[u8] {
87        self.0.as_slice()
88    }
89
90    /// Export as raw bytes
91    #[inline]
92    pub fn to_bytes(&self) -> Vec<u8> {
93        self.0.as_slice().to_vec()
94    }
95
96    /// Verify a signed message
97    pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Error> {
98        use secp256k1::hashes::{sha256, Hash};
99        let pk = XOnlyPublicKey::from_slice(self.0.as_slice())?;
100        let hash = sha256::Hash::hash(message).to_byte_array();
101        let message = secp256k1::Message::from_digest(hash);
102        Ok(SECP256K1.verify_schnorr(&signature.0, &message, &pk)?)
103    }
104
105    /// This provides a u32 hash input for HyperLogLog that is based on
106    /// NIP-45 (pr #1561).  This works with the original HyperLogLog
107    pub fn hyperloglog_hash32(&self, offset: usize) -> u32 {
108        if offset + 3 >= 32 {
109            panic!("hyperloglog_hash32 offset cannot be greater than 28");
110        }
111        u32::from_ne_bytes(self.0[offset..offset + 3].try_into().unwrap())
112    }
113
114    /// This provides a u64 hash input for HyperLogLog++ that is based on
115    /// NIP-45 (pr #1561).  This works with Google's HyperLogLog++
116    pub fn hyperloglog_hash64(&self, offset: usize) -> u64 {
117        if offset + 7 >= 32 {
118            panic!("hyperloglog_hash64 offset cannot be greater than 24");
119        }
120        u64::from_ne_bytes(self.0[offset..offset + 7].try_into().unwrap())
121    }
122
123    // Mock data for testing
124    #[allow(dead_code)]
125    pub(crate) fn mock() -> PublicKey {
126        PrivateKey::generate().public_key()
127    }
128
129    #[allow(dead_code)]
130    pub(crate) fn mock_deterministic() -> PublicKey {
131        PublicKey::try_from_hex_string(
132            "ee11a5dff40c19a555f41fe42b48f00e618c91225622ae37b6c2bb67b76c4e49",
133            true,
134        )
135        .unwrap()
136    }
137}
138
139impl Serialize for PublicKey {
140    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
141    where
142        S: Serializer,
143    {
144        serializer.serialize_str(&self.as_hex_string())
145    }
146}
147
148impl<'de> Deserialize<'de> for PublicKey {
149    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
150    where
151        D: Deserializer<'de>,
152    {
153        deserializer.deserialize_str(PublicKeyVisitor)
154    }
155}
156
157struct PublicKeyVisitor;
158
159impl Visitor<'_> for PublicKeyVisitor {
160    type Value = PublicKey;
161
162    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
163        write!(f, "a lowercase hexadecimal string representing 32 bytes")
164    }
165
166    fn visit_str<E>(self, v: &str) -> Result<PublicKey, E>
167    where
168        E: serde::de::Error,
169    {
170        let vec: Vec<u8> = hex::decode(v).map_err(|e| serde::de::Error::custom(format!("{e}")))?;
171
172        if vec.len() != 32 {
173            return Err(serde::de::Error::custom("Public key is not 32 bytes long"));
174        }
175
176        Ok(PublicKey(vec.try_into().unwrap()))
177    }
178}
179
180impl std::hash::Hash for PublicKey {
181    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
182        self.as_hex_string().hash(state);
183    }
184}
185
186#[cfg(feature = "speedy")]
187impl<'a, C: Context> Readable<'a, C> for PublicKey {
188    #[inline]
189    fn read_from<R: Reader<'a, C>>(reader: &mut R) -> Result<Self, C::Error> {
190        let bytes: Vec<u8> = reader.read_vec(32)?;
191        Ok(PublicKey(bytes.try_into().unwrap()))
192    }
193
194    #[inline]
195    fn minimum_bytes_needed() -> usize {
196        32
197    }
198}
199
200#[cfg(feature = "speedy")]
201impl<C: Context> Writable<C> for PublicKey {
202    #[inline]
203    fn write_to<T: ?Sized + Writer<C>>(&self, writer: &mut T) -> Result<(), C::Error> {
204        writer.write_bytes(self.as_slice())
205    }
206
207    #[inline]
208    fn bytes_needed(&self) -> Result<usize, C::Error> {
209        Ok(32)
210    }
211}
212
213/// This is a public key, which identifies an actor (usually a person) and is shared, as a hex string
214///
215/// You can convert from a `PublicKey` into this with `From`/`Into`.  You can convert this back to a `PublicKey` with `TryFrom`/`TryInto`.
216#[derive(
217    AsMut,
218    AsRef,
219    Clone,
220    Debug,
221    Deref,
222    Display,
223    Eq,
224    From,
225    FromStr,
226    Hash,
227    Into,
228    PartialEq,
229    PartialOrd,
230    Ord,
231)]
232#[cfg_attr(feature = "speedy", derive(Readable, Writable))]
233pub struct PublicKeyHex(String);
234
235impl PublicKeyHex {
236    // Mock data for testing
237    #[allow(dead_code)]
238    pub(crate) fn mock() -> PublicKeyHex {
239        From::from(PublicKey::mock())
240    }
241
242    // Mock data for testing
243    #[allow(dead_code)]
244    pub(crate) fn mock_deterministic() -> PublicKeyHex {
245        PublicKey::mock_deterministic().into()
246    }
247
248    /// Export as a bech32 encoded string
249    pub fn as_bech32_string(&self) -> String {
250        let vec: Vec<u8> = hex::decode(&self.0).unwrap();
251        bech32::encode::<bech32::Bech32>(*crate::HRP_NPUB, &vec).unwrap()
252    }
253
254    /// Try from &str
255    pub fn try_from_str(s: &str) -> Result<PublicKeyHex, Error> {
256        Self::try_from_string(s.to_owned())
257    }
258
259    /// Try from String
260    pub fn try_from_string(s: String) -> Result<PublicKeyHex, Error> {
261        if s.len() != 64 {
262            return Err(Error::InvalidPublicKey);
263        }
264        let vec: Vec<u8> = hex::decode(&s)?;
265        if vec.len() != 32 {
266            return Err(Error::InvalidPublicKey);
267        }
268        Ok(PublicKeyHex(s))
269    }
270
271    /// As &str
272    pub fn as_str(&self) -> &str {
273        &self.0
274    }
275
276    /// Into String
277    pub fn into_string(self) -> String {
278        self.0
279    }
280}
281
282impl TryFrom<&str> for PublicKeyHex {
283    type Error = Error;
284
285    fn try_from(s: &str) -> Result<PublicKeyHex, Error> {
286        PublicKeyHex::try_from_str(s)
287    }
288}
289
290impl From<&PublicKey> for PublicKeyHex {
291    fn from(pk: &PublicKey) -> PublicKeyHex {
292        PublicKeyHex(pk.as_hex_string())
293    }
294}
295
296impl From<PublicKey> for PublicKeyHex {
297    fn from(pk: PublicKey) -> PublicKeyHex {
298        PublicKeyHex(pk.as_hex_string())
299    }
300}
301
302impl TryFrom<&PublicKeyHex> for PublicKey {
303    type Error = Error;
304
305    fn try_from(pkh: &PublicKeyHex) -> Result<PublicKey, Error> {
306        PublicKey::try_from_hex_string(&pkh.0, true)
307    }
308}
309
310impl TryFrom<PublicKeyHex> for PublicKey {
311    type Error = Error;
312
313    fn try_from(pkh: PublicKeyHex) -> Result<PublicKey, Error> {
314        PublicKey::try_from_hex_string(&pkh.0, true)
315    }
316}
317
318impl Serialize for PublicKeyHex {
319    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
320    where
321        S: Serializer,
322    {
323        serializer.serialize_str(&self.0)
324    }
325}
326
327impl<'de> Deserialize<'de> for PublicKeyHex {
328    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
329    where
330        D: Deserializer<'de>,
331    {
332        deserializer.deserialize_str(PublicKeyHexVisitor)
333    }
334}
335
336struct PublicKeyHexVisitor;
337
338impl Visitor<'_> for PublicKeyHexVisitor {
339    type Value = PublicKeyHex;
340
341    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
342        write!(f, "a lowercase hexadecimal string representing 32 bytes")
343    }
344
345    fn visit_str<E>(self, v: &str) -> Result<PublicKeyHex, E>
346    where
347        E: serde::de::Error,
348    {
349        if v.len() != 64 {
350            return Err(serde::de::Error::custom(
351                "PublicKeyHex is not 64 characters long",
352            ));
353        }
354
355        let vec: Vec<u8> = hex::decode(v).map_err(|e| serde::de::Error::custom(format!("{e}")))?;
356        if vec.len() != 32 {
357            return Err(serde::de::Error::custom("Invalid PublicKeyHex"));
358        }
359
360        Ok(PublicKeyHex(v.to_owned()))
361    }
362}
363
364#[cfg(test)]
365mod test {
366    use super::*;
367
368    test_serde! {PublicKey, test_public_key_serde}
369    test_serde! {PublicKeyHex, test_public_key_hex_serde}
370
371    #[test]
372    fn test_pubkey_bech32() {
373        let pk = PublicKey::mock();
374
375        let encoded = pk.as_bech32_string();
376        println!("bech32: {encoded}");
377
378        let decoded = PublicKey::try_from_bech32_string(&encoded, true).unwrap();
379
380        assert_eq!(pk, decoded);
381    }
382
383    #[cfg(feature = "speedy")]
384    #[test]
385    fn test_speedy_public_key() {
386        let pk = PublicKey::mock();
387        let bytes = pk.write_to_vec().unwrap();
388        let pk2 = PublicKey::read_from_buffer(&bytes).unwrap();
389        assert_eq!(pk, pk2);
390    }
391}