ln_types/
node_pubkey.rs

1//! Newtype over `PublicKey` and corresponding error types.
2//!
3//! This module makes working with node public key easier by providing `NodePubkey` newtype.
4//! This makes it less likely that the node public key will be mistaken for other public key.
5//! Further, it provides convenient parsing, serialization and signature verification methods
6//! along with strong error types.
7
8use core::convert::{TryFrom, TryInto};
9use core::str::FromStr;
10use core::fmt;
11use secp256k1::{PublicKey, Secp256k1, SecretKey};
12use crate::NodeId;
13
14#[cfg(feature = "alloc")]
15use alloc::{boxed::Box, string::String, vec::Vec};
16
17/// Newtype over `secp256k1::PublicKey` representing a deserialized key identifying an LN node.
18///
19/// This can be considered similar to `NodeId` with these differences:
20///
21/// * `NodeId` is more performant for non-cryptographic operations.
22/// * `NodeId` can not perform any cryptographic operations itself.
23/// * `NodePubkey`, despite its field being public, maintains more invariants.
24///    *In this library*, a valid `NodeId` is **not** guaranteed to be a valid `NodePubkey`
25///
26/// ## Example
27///
28/// ```
29/// let marvin_str = "029ef8ee0ba895e2807ac1df1987a7888116c468e70f42e7b089e06811b0e45482";
30/// let marvin = marvin_str.parse::<ln_types::NodePubkey>().unwrap();
31/// assert_eq!(marvin.to_string(), marvin_str);
32/// ```
33#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
34pub struct NodePubkey(
35    /// The underlying public key used for cryptographic operations.
36    pub PublicKey
37);
38
39impl NodePubkey {
40    /// Verify a message signed by this key.
41    ///
42    /// This is a convenience method that simply uses slices.
43    /// While this could be seen as regression from strongly-typed [`secp256k1`] library, it should
44    /// be a good tradeoff here. The reason is we know that LN signatures are **not** DER-encoded
45    /// and there shouldn't be a reason to need to keep message hash around.
46    ///
47    /// If you need anything advanced, you can still use the raw [`secp256k1::PublicKey`].
48    ///
49    /// ## Example
50    ///
51    /// ```
52    /// use ln_types::secp256k1::hashes::{sha256d, Hash};
53    ///
54    /// let marvin_str = "029ef8ee0ba895e2807ac1df1987a7888116c468e70f42e7b089e06811b0e45482";
55    /// let marvin = marvin_str.parse::<ln_types::NodePubkey>().unwrap();
56    /// let message = "Lightning Signed Message:I am the author of `ln-types` Rust crate.";
57    /// let signature = &[0x29, 0x79, 0x4d, 0x9a, 0x6a, 0x48, 0x68, 0x0f, 0x9b, 0x8d, 0x60, 0x97, 0xa6, 0xd8, 0xef, 0x1d, 0x5c, 0xf9, 0xdc, 0x27, 0xcd, 0x76, 0x9a, 0x86, 0x58, 0xd6, 0x94, 0x00, 0x1c, 0x12, 0xb8, 0xdd, 0x49, 0xaf, 0x2b, 0xca, 0x0a, 0x24, 0xd8, 0xf4, 0x5a, 0x3b, 0x3c, 0xc7, 0x87, 0xf0, 0x48, 0x60, 0x63, 0x23, 0xf4, 0x24, 0xba, 0xa8, 0x0f, 0x5e, 0xe6, 0x05, 0x79, 0x81, 0xe2, 0x29, 0x6f, 0x0d];
58    /// let secp = ln_types::secp256k1::Secp256k1::verification_only();
59    /// let message = sha256d::Hash::hash(message.as_bytes());
60    /// let message = secp256k1::Message::from_digest(message.to_byte_array());
61    ///
62    /// marvin.verify(&secp, message, signature).unwrap();
63    /// ```
64    #[cfg(feature = "node_pubkey_verify")]
65    pub fn verify<M: Into<secp256k1::Message>, C: secp256k1::Verification>(&self, secp: &Secp256k1<C>, message: M, signature: &[u8]) -> Result<(), secp256k1::Error> {
66        use secp256k1::ecdsa::Signature;
67
68        let signature = Signature::from_compact(signature)?;
69        let message = message.into();
70
71        secp.verify_ecdsa(&message, &signature, &self.0)
72    }
73
74    /// Verifies a message signed by `signmessage` Eclair/LND RPC.
75    ///
76    /// The signatures of messages returned by node RPCs are not so simple.
77    /// They prefix messages with `Lightning Signed Message:`, use double sha256 and recovery.
78    /// It is the reason why this function requires the `recovery` feature of [`secp256k1`].
79    ///
80    /// This function takes care of all that complexity for you so that you can verify the messages
81    /// conveniently.
82    ///
83    /// ## Example
84    ///
85    /// ```
86    /// let marvin_str = "029ef8ee0ba895e2807ac1df1987a7888116c468e70f42e7b089e06811b0e45482";
87    /// let marvin = marvin_str.parse::<ln_types::NodePubkey>().unwrap();
88    /// let message = "I am the author of `ln-types` Rust crate.";
89    /// let signature = &[0x1f, 0x29, 0x79, 0x4d, 0x9a, 0x6a, 0x48, 0x68, 0x0f, 0x9b, 0x8d, 0x60, 0x97, 0xa6, 0xd8, 0xef, 0x1d, 0x5c, 0xf9, 0xdc, 0x27, 0xcd, 0x76, 0x9a, 0x86, 0x58, 0xd6, 0x94, 0x00, 0x1c, 0x12, 0xb8, 0xdd, 0x49, 0xaf, 0x2b, 0xca, 0x0a, 0x24, 0xd8, 0xf4, 0x5a, 0x3b, 0x3c, 0xc7, 0x87, 0xf0, 0x48, 0x60, 0x63, 0x23, 0xf4, 0x24, 0xba, 0xa8, 0x0f, 0x5e, 0xe6, 0x05, 0x79, 0x81, 0xe2, 0x29, 0x6f, 0x0d];
90    /// let secp = ln_types::secp256k1::Secp256k1::verification_only();
91    ///
92    /// marvin.verify_lightning_message(&secp, message.as_bytes(), signature).unwrap();
93    /// ```
94    #[cfg(feature = "node_pubkey_recovery")]
95    pub fn verify_lightning_message<C: secp256k1::Verification>(&self, secp: &Secp256k1<C>, message: &[u8], signature: &[u8]) -> Result<(), secp256k1::Error> {
96        use secp256k1::Message;
97        use secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
98        use secp256k1::hashes::{sha256, sha256d, HashEngine, Hash};
99
100        let (recovery_id, signature) = signature
101            .split_first()
102            .ok_or(secp256k1::Error::InvalidSignature)?;
103
104        let recovery_id = recovery_id
105            .checked_sub(0x1f)
106            .ok_or(secp256k1::Error::InvalidSignature)?;
107
108        let recovery_id = RecoveryId::from_i32(recovery_id.into())?;
109        let signature = RecoverableSignature::from_compact(signature, recovery_id)?;
110        let mut hasher = sha256::HashEngine::default();
111        hasher.input(b"Lightning Signed Message:");
112        hasher.input(message);
113        let hash = sha256d::Hash::from_engine(hasher);
114        let message = Message::from_digest(hash.to_byte_array());
115
116        let pubkey = secp.recover_ecdsa(&message, &signature)?;
117        if pubkey == self.0 {
118            Ok(())
119        } else {
120            Err(secp256k1::Error::IncorrectSignature)
121        }
122    }
123
124    /// Generic wrapper for parsing that is used to implement parsing from multiple types.
125    #[cfg(feature = "alloc")]
126    fn internal_parse<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, ParseError> {
127        match NodeId::parse_raw(s.as_ref()) {
128            Ok(node_id) => {
129                node_id.try_into()
130                    .map_err(|error| ParseError {
131                        input: s.into(),
132                        reason: ParseErrorInner::Pubkey(error),
133                    })
134            },
135            Err(error) => {
136                Err(ParseError {
137                    input: s.into(),
138                    reason: ParseErrorInner::NodeId(error),
139                })
140            }
141        }
142    }
143
144    /// Generic wrapper for parsing that is used to implement parsing from multiple types.
145    #[cfg(not(feature = "alloc"))]
146    fn internal_parse<S: AsRef<str>>(s: S) -> Result<Self, ParseError> {
147        match NodeId::parse_raw(s.as_ref()) {
148            Ok(node_id) => {
149                node_id.try_into()
150                    .map_err(|error| ParseError {
151                        reason: ParseErrorInner::Pubkey(error),
152                    })
153            },
154            Err(error) => {
155                Err(ParseError {
156                    reason: ParseErrorInner::NodeId(error),
157                })
158            }
159        }
160    }
161
162    /// Convenience conversion method.
163    ///
164    /// This is more readable and less prone to inference problems than `Into::into`.
165    pub fn to_node_id(&self) -> NodeId {
166        NodeId::from_raw_bytes(self.0.serialize()).expect("secp256k1 always creates correct node ID")
167    }
168
169    /// Computes public key from a secret key and stores it as `NodePubkey`.
170    pub fn from_secret_key<C: secp256k1::Signing>(secp: &Secp256k1<C>, sk: &SecretKey) -> Self {
171        NodePubkey(PublicKey::from_secret_key(secp, sk))
172    }
173}
174
175/// Shows `NodePubkey` as hex
176impl fmt::Display for NodePubkey {
177    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178        fmt::Display::fmt(&self.to_node_id(), f)
179    }
180}
181
182impl fmt::Debug for NodePubkey {
183    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
184        fmt::Debug::fmt(&self.to_node_id(), f)
185    }
186}
187
188impl fmt::LowerHex for NodePubkey {
189    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
190        fmt::LowerHex::fmt(&self.to_node_id(), f)
191    }
192}
193
194impl fmt::UpperHex for NodePubkey {
195    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
196        fmt::UpperHex::fmt(&self.to_node_id(), f)
197    }
198}
199
200impl TryFrom<NodeId> for NodePubkey {
201    type Error = secp256k1::Error;
202
203    fn try_from(value: NodeId) -> Result<Self, Self::Error> {
204        Ok(NodePubkey(PublicKey::from_slice(value.as_ref())?))
205    }
206}
207
208impl From<NodePubkey> for NodeId {
209    fn from(value: NodePubkey) -> Self {
210        value.to_node_id()
211    }
212}
213
214impl<'a> From<&'a NodePubkey> for NodeId {
215    fn from(value: &'a NodePubkey) -> Self {
216        value.to_node_id()
217    }
218}
219
220impl AsRef<PublicKey> for NodePubkey {
221    fn as_ref(&self) -> &PublicKey {
222        &self.0
223    }
224}
225
226impl AsMut<PublicKey> for NodePubkey {
227    fn as_mut(&mut self) -> &mut PublicKey {
228        &mut self.0
229    }
230}
231
232impl core::borrow::Borrow<PublicKey> for NodePubkey {
233    fn borrow(&self) -> &PublicKey {
234        &self.0
235    }
236}
237
238impl core::borrow::BorrowMut<PublicKey> for NodePubkey {
239    fn borrow_mut(&mut self) -> &mut PublicKey {
240        &mut self.0
241    }
242}
243
244/// Expects hex representation
245impl FromStr for NodePubkey {
246    type Err = ParseError;
247
248    #[inline]
249    fn from_str(s: &str) -> Result<Self, Self::Err> {
250        Self::internal_parse(s)
251    }
252}
253
254/// Expects hex representation
255impl<'a> TryFrom<&'a str> for NodePubkey {
256    type Error = ParseError;
257
258    #[inline]
259    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
260        Self::internal_parse(s)
261    }
262}
263
264/// Expects hex representation
265#[cfg(feature = "alloc")]
266impl TryFrom<String> for NodePubkey {
267    type Error = ParseError;
268
269    #[inline]
270    fn try_from(s: String) -> Result<Self, Self::Error> {
271        Self::internal_parse(s)
272    }
273}
274
275/// Expects hex representation
276#[cfg(feature = "alloc")]
277impl TryFrom<Box<str>> for NodePubkey {
278    type Error = ParseError;
279
280    #[inline]
281    fn try_from(s: Box<str>) -> Result<Self, Self::Error> {
282        Self::internal_parse(s)
283    }
284}
285
286impl<'a> TryFrom<&'a [u8]> for NodePubkey {
287    type Error = secp256k1::Error;
288
289    #[inline]
290    fn try_from(slice: &'a [u8]) -> Result<Self, Self::Error> {
291        Ok(NodePubkey(PublicKey::from_slice(slice)?))
292    }
293}
294
295#[cfg(feature = "alloc")]
296impl TryFrom<Vec<u8>> for NodePubkey {
297    type Error = secp256k1::Error;
298
299    #[inline]
300    fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
301        (*vec).try_into()
302    }
303}
304
305#[cfg(feature = "alloc")]
306impl TryFrom<Box<[u8]>> for NodePubkey {
307    type Error = secp256k1::Error;
308
309    #[inline]
310    fn try_from(slice: Box<[u8]>) -> Result<Self, Self::Error> {
311        (*slice).try_into()
312    }
313}
314
315impl From<NodePubkey> for [u8; 33] {
316    fn from(value: NodePubkey) -> Self {
317        value.to_node_id().into()
318    }
319}
320
321/// Error returned when parsing text representation fails.
322///
323/// **Important: consumer code MUST NOT match on this using `ParseError { .. }` syntax.
324#[derive(Debug, Clone)]
325pub struct ParseError {
326    /// The string that was attempted to be parsed
327    #[cfg(feature = "alloc")]
328    input: String,
329    /// Information about what exactly went wrong
330    reason: ParseErrorInner,
331}
332
333/// **Behaves strangely if `std` is enabled without `secp256k1_std`!**
334///
335/// Specifically pubkey error is displayed inline instead of as a source.
336impl fmt::Display for ParseError {
337    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
338        write_err!(f, "failed to parse{} Lightning Network node public key", opt_fmt!("alloc", format_args!(" '{}' as", &self.input)); &self.reason)
339    }
340}
341
342/// **Behaves strangely if `std` is enabled without `secp256k1_std`!**
343///
344/// Specifically pubkey error is displayed inline instead of as a source.
345#[cfg(feature = "std")]
346impl std::error::Error for ParseError {
347    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
348        Some(&self.reason)
349    }
350}
351
352/// Details about the error.
353///
354/// This is private to avoid committing to a representation.
355#[derive(Debug, Clone)]
356enum ParseErrorInner {
357    /// Length != 66 chars
358    NodeId(crate::node_id::ParseErrorInner),
359    Pubkey(secp256k1::Error),
360}
361
362impl fmt::Display for ParseErrorInner {
363    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
364        match self {
365            ParseErrorInner::NodeId(error) => write_err!(f, "invalid node ID"; error),
366            ParseErrorInner::Pubkey(error) => write_err_ext!("secp256k1_std", f, "invalid public key"; error),
367        }
368    }
369}
370
371#[cfg(feature = "std")]
372impl std::error::Error for ParseErrorInner {
373    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
374        match &self {
375            ParseErrorInner::NodeId(error) => Some(error),
376            #[cfg(feature = "secp256k1_std")]
377            ParseErrorInner::Pubkey(error) => Some(error),
378            #[cfg(not(feature = "secp256k1_std"))]
379            ParseErrorInner::Pubkey(_) => None,
380        }
381    }
382}
383
384/// Implementation of `parse_arg::ParseArg` trait
385#[cfg(feature = "parse_arg")]
386mod parse_arg_impl {
387    use core::fmt;
388    use super::NodePubkey;
389
390    impl parse_arg::ParseArgFromStr for NodePubkey {
391        fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result {
392            writer.write_str("a hex-encoded LN node ID (66 hex digits/33 bytes)")
393        }
394    }
395}
396
397#[cfg(all(feature = "serde", feature = "secp256k1/serde"))]
398mod serde_impls {
399    use serde::{Serialize, Deserialize, Serializer, Deserializer};
400    use super::NodePubkey;
401    use secp256k1::PublicKey;
402
403    /// `NodePubkey` is transparently serialized `secp256k1::PublicKey`
404    #[cfg_attr(all(feature = "serde", feature = "secp256k1/serde"))]
405    impl Serialize for NodePubkey {
406        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
407            Seialize::serialize(&self.0, serializer)
408        }
409    }
410
411    /// `NodePubkey` is transparently deserialized `secp256k1::PublicKey`
412    #[cfg_attr(all(feature = "serde", feature = "secp256k1/serde"))]
413    impl<'de> Deserialize<'de> for NodePubkey {
414        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
415            Ok(PublicKey::deserialize(deserializer)?)
416        }
417    }
418}
419
420/// Implementations of `postgres-types` traits
421#[cfg(feature = "postgres-types")]
422mod postgres_impl {
423    use alloc::boxed::Box;
424    use super::NodePubkey;
425    use crate::NodeId;
426    use postgres_types::{ToSql, FromSql, IsNull, Type};
427    use bytes::BytesMut;
428    use std::error::Error;
429    use core::convert::TryInto;
430
431    /// Supports `BYTEA`, `TEXT`, and `VARCHAR`.
432    ///
433    /// Stored as bytes if `BYTEA` is used, as hex string otherwise.
434    impl ToSql for NodePubkey {
435        fn to_sql(&self, ty: &Type, out: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Send + Sync + 'static>> {
436            self.to_node_id().to_sql(ty, out)
437        }
438
439        fn accepts(ty: &Type) -> bool {
440            <NodeId as ToSql>::accepts(ty)
441        }
442
443        postgres_types::to_sql_checked!();
444    }
445
446    /// Supports `BYTEA`, `TEXT`, and `VARCHAR`.
447    ///
448    /// Decoded as bytes if `BYTEA` is used, as hex string otherwise.
449    impl<'a> FromSql<'a> for NodePubkey {
450        fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Send + Sync + 'static>> {
451            NodeId::from_sql(ty, raw)?.try_into().map_err(|error| Box::new(error) as _)
452        }
453
454        fn accepts(ty: &Type) -> bool {
455            <NodeId as FromSql>::accepts(ty)
456        }
457    }
458}
459
460/// Implementations of `slog` traits
461#[cfg(feature = "slog")]
462mod slog_impl {
463    use super::NodePubkey;
464    use slog::{Key, Value, Record, Serializer};
465
466    /// Currently uses `Display` but may use `emit_bytes` if/when it's implemented.
467    impl Value for NodePubkey {
468        fn serialize(&self, _rec: &Record, key: Key, serializer: &mut dyn Serializer) -> slog::Result {
469            serializer.emit_arguments(key, &format_args!("{}", self))
470        }
471    }
472
473    impl_error_value!(super::ParseError);
474}
475
476#[cfg(test)]
477mod tests {
478    use super::NodePubkey;
479
480    #[test]
481    fn empty() {
482        let error = "".parse::<NodePubkey>().unwrap_err();
483        #[cfg(feature = "alloc")]
484        assert_eq!(error.input, "");
485        match &error.reason {
486            super::ParseErrorInner::NodeId(_) => (),
487            super::ParseErrorInner::Pubkey(error) => panic!("unexpected error variant: Pubkey({:?})", error),
488        }
489    }
490
491    chk_err_impl! {
492        parse_node_pubkey_error_empty, "", NodePubkey, [
493            "failed to parse '' as Lightning Network node public key",
494            "invalid node ID",
495            "invalid length (must be 66 chars)",
496        ], [
497            "failed to parse Lightning Network node public key",
498            "invalid node ID",
499            "invalid length (must be 66 chars)",
500        ];
501    }
502
503    #[cfg(any(not(feature = "std"), feature = "secp256k1_std"))]
504    chk_err_impl! {
505        parse_node_pubkey_error_invalid_pubkey, "020000000000000000000000000000000000000000000000000000000000000000", NodePubkey, [
506            "failed to parse '020000000000000000000000000000000000000000000000000000000000000000' as Lightning Network node public key",
507            "invalid public key",
508            "malformed public key",
509        ], [
510            "failed to parse Lightning Network node public key",
511            "invalid public key",
512            "malformed public key",
513        ];
514    }
515
516    #[cfg(all(feature = "std", not(feature = "secp256k1_std")))]
517    chk_err_impl! {
518        parse_node_pubkey_error_invalid_pubkey, "020000000000000000000000000000000000000000000000000000000000000000", NodePubkey, [
519            "failed to parse '020000000000000000000000000000000000000000000000000000000000000000' as Lightning Network node public key",
520            "invalid public key: malformed public key",
521        ], [
522            "irrelevant, we definitely have std and thus alloc",
523        ];
524    }
525}