cometbft_p2p/
public_key.rs

1//! Secret Connection peer identity public keys.
2
3use crate::{Error, IdentitySecret, PeerId, Result, ed25519, proto};
4use ed25519_dalek::Verifier;
5use prost::DecodeError;
6use sha2::{Sha256, digest::Digest};
7use std::fmt::{self, Debug, Display};
8
9/// Secret Connection peer identity public keys (signing, presently Ed25519-only)
10#[derive(Clone, Copy, Eq, Hash, PartialEq)]
11pub enum PublicKey {
12    /// Ed25519 Secret Connection Keys
13    Ed25519(ed25519::VerifyingKey),
14}
15
16impl PublicKey {
17    /// From raw Ed25519 public key bytes
18    ///
19    /// # Errors
20    /// - if the bytes given are invalid
21    pub fn from_raw_ed25519(bytes: &[u8]) -> Result<Self> {
22        Ok(ed25519::VerifyingKey::try_from(bytes).map(Self::Ed25519)?)
23    }
24
25    /// Get Ed25519 public key.
26    #[must_use]
27    pub fn ed25519(self) -> Option<ed25519::VerifyingKey> {
28        match self {
29            Self::Ed25519(pk) => Some(pk),
30        }
31    }
32
33    /// Get the remote [`PeerId`].
34    ///
35    /// This is a 20-byte fingerprint of the public key.
36    #[must_use]
37    pub fn peer_id(self) -> PeerId {
38        match self {
39            Self::Ed25519(pk) => {
40                let digest = Sha256::digest(pk.as_bytes());
41                PeerId(digest[..20].try_into().expect("should be 20 bytes"))
42            }
43        }
44    }
45
46    /// Convert this [`PublicKey`] into a protobuf equivalent.
47    pub fn to_proto(&self) -> proto::crypto::PublicKey {
48        let pk = match self {
49            Self::Ed25519(pk) => proto::crypto::public_key::Sum::Ed25519(pk.as_ref().to_vec()),
50        };
51
52        proto::crypto::PublicKey { sum: Some(pk) }
53    }
54
55    /// Verify the given message and signature using this public key.
56    pub(crate) fn verify(&self, msg: &[u8], sig: &[u8]) -> Result<()> {
57        match self {
58            Self::Ed25519(ed25519_vk) => {
59                let sig = ed25519::Signature::try_from(sig)?;
60                Ok(ed25519_vk.verify(msg, &sig)?)
61            }
62        }
63    }
64}
65
66impl Display for PublicKey {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        match self {
69            Self::Ed25519(ed25519_key) => {
70                write!(f, "PublicKey::Ed25519(")?;
71                for byte in ed25519_key.to_bytes() {
72                    write!(f, "{byte:02x}")?;
73                }
74                write!(f, ")")?;
75            }
76        }
77
78        Ok(())
79    }
80}
81
82impl Debug for PublicKey {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        match self {
85            PublicKey::Ed25519(_) => write!(f, "PublicKey::Ed25519({self})"),
86        }
87    }
88}
89
90impl From<ed25519::VerifyingKey> for PublicKey {
91    fn from(pk: ed25519::VerifyingKey) -> Self {
92        Self::Ed25519(pk)
93    }
94}
95
96impl From<&ed25519::VerifyingKey> for PublicKey {
97    fn from(pk: &ed25519::VerifyingKey) -> Self {
98        Self::from(*pk)
99    }
100}
101
102impl From<&IdentitySecret> for PublicKey {
103    fn from(sk: &IdentitySecret) -> Self {
104        Self::Ed25519(sk.verifying_key())
105    }
106}
107
108impl From<PublicKey> for proto::crypto::PublicKey {
109    fn from(pk: PublicKey) -> Self {
110        pk.to_proto()
111    }
112}
113
114impl From<&PublicKey> for proto::crypto::PublicKey {
115    fn from(pk: &PublicKey) -> Self {
116        pk.to_proto()
117    }
118}
119
120impl TryFrom<proto::crypto::PublicKey> for PublicKey {
121    type Error = Error;
122
123    fn try_from(pk: proto::crypto::PublicKey) -> Result<Self> {
124        Self::try_from(&pk)
125    }
126}
127
128impl TryFrom<&proto::crypto::PublicKey> for PublicKey {
129    type Error = Error;
130
131    fn try_from(pk: &proto::crypto::PublicKey) -> Result<Self> {
132        match &pk.sum {
133            Some(proto::crypto::public_key::Sum::Ed25519(bytes)) => {
134                ed25519::VerifyingKey::try_from(&bytes[..])
135                    .map(Self::Ed25519)
136                    .map_err(|_| {
137                        DecodeError::new("malformed PublicKey proto with invalid Ed25519 key")
138                            .into()
139                    })
140            }
141            _ => Err(DecodeError::new(
142                "malformed PublicKey proto or unsupported public key algorithm",
143            )
144            .into()),
145        }
146    }
147}