tendermint/
public_key.rs

1//! Public keys used in Tendermint networks
2
3#[cfg(feature = "secp256k1")]
4pub use k256::ecdsa::VerifyingKey as Secp256k1;
5
6mod pub_key_request;
7mod pub_key_response;
8
9pub use pub_key_request::PubKeyRequest;
10pub use pub_key_response::PubKeyResponse;
11
12use core::{cmp::Ordering, fmt, str::FromStr};
13use serde::{de, ser, Deserialize, Deserializer, Serialize};
14use serde_json::Value;
15use subtle_encoding::{base64, bech32, hex};
16
17pub use crate::crypto::ed25519::VerificationKey as Ed25519;
18use crate::{error::Error, prelude::*};
19
20// Note:On the golang side this is generic in the sense that it could everything that implements
21// github.com/tendermint/tendermint/crypto.PubKey
22// While this is meant to be used with different key-types, it currently only uses a PubKeyEd25519
23// version.
24// TODO: make this more generic
25
26// Warning: the custom serialization implemented here does not use TryFrom<RawPublicKey>.
27//          it should only be used to read/write the priva_validator_key.json.
28//          All changes to the serialization should check both the JSON and protobuf conversions.
29// Todo: Merge JSON serialization with #[serde(try_from = "RawPublicKey", into = "RawPublicKey)]
30/// Public keys allowed in Tendermint protocols
31#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
32#[non_exhaustive]
33#[serde(tag = "type", content = "value")] // JSON custom serialization for priv_validator_key.json
34pub enum PublicKey {
35    /// Ed25519 keys
36    #[serde(
37        rename = "tendermint/PubKeyEd25519",
38        serialize_with = "serialize_ed25519_base64",
39        deserialize_with = "deserialize_ed25519_base64"
40    )]
41    Ed25519(Ed25519),
42
43    /// Secp256k1 keys
44    #[cfg(feature = "secp256k1")]
45    #[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
46    #[serde(
47        rename = "tendermint/PubKeySecp256k1",
48        serialize_with = "serialize_secp256k1_base64",
49        deserialize_with = "deserialize_secp256k1_base64"
50    )]
51    Secp256k1(Secp256k1),
52}
53
54// Internal thunk type to facilitate deserialization from the raw Protobuf data
55// structure's JSON representation.
56#[derive(Serialize, Deserialize)]
57struct ProtobufPublicKeyWrapper {
58    #[serde(rename = "Sum")]
59    sum: ProtobufPublicKey,
60}
61
62impl From<ProtobufPublicKeyWrapper> for PublicKey {
63    fn from(wrapper: ProtobufPublicKeyWrapper) -> Self {
64        match wrapper.sum {
65            ProtobufPublicKey::Ed25519 { ed25519 } => PublicKey::Ed25519(ed25519),
66            #[cfg(feature = "secp256k1")]
67            ProtobufPublicKey::Secp256k1 { secp256k1 } => PublicKey::Secp256k1(secp256k1),
68        }
69    }
70}
71
72#[derive(Serialize, Deserialize)]
73#[serde(tag = "type", content = "value")] // JSON custom serialization for priv_validator_key.json
74enum ProtobufPublicKey {
75    #[serde(rename = "tendermint.crypto.PublicKey_Ed25519")]
76    Ed25519 {
77        #[serde(
78            serialize_with = "serialize_ed25519_base64",
79            deserialize_with = "deserialize_ed25519_base64"
80        )]
81        ed25519: Ed25519,
82    },
83
84    #[cfg(feature = "secp256k1")]
85    #[serde(rename = "tendermint.crypto.PublicKey_Secp256K1")]
86    Secp256k1 {
87        #[serde(
88            serialize_with = "serialize_secp256k1_base64",
89            deserialize_with = "deserialize_secp256k1_base64"
90        )]
91        secp256k1: Secp256k1,
92    },
93}
94
95/// Custom deserialization for public keys to handle multiple potential JSON
96/// formats from Tendermint.
97///
98/// See <https://github.com/informalsystems/tendermint-rs/issues/1021> for
99/// context.
100// TODO(thane): Remove this once the serialization in Tendermint has been fixed.
101pub fn deserialize_public_key<'de, D>(deserializer: D) -> Result<PublicKey, D::Error>
102where
103    D: Deserializer<'de>,
104{
105    let v = Value::deserialize(deserializer)?;
106    if v.as_object()
107        .map(|obj| obj.contains_key("Sum"))
108        .unwrap_or(false)
109    {
110        serde_json::from_value::<ProtobufPublicKeyWrapper>(v).map(Into::into)
111    } else {
112        serde_json::from_value::<PublicKey>(v)
113    }
114    .map_err(de::Error::custom)
115}
116
117tendermint_pb_modules! {
118    use super::{PublicKey, Ed25519};
119    use pb::crypto::{PublicKey as RawPublicKey, public_key::Sum};
120    use crate::{prelude::*, Error};
121
122    impl Protobuf<RawPublicKey> for PublicKey {}
123
124    impl TryFrom<RawPublicKey> for PublicKey {
125        type Error = Error;
126
127        fn try_from(value: RawPublicKey) -> Result<Self, Self::Error> {
128            let sum = &value
129                .sum
130                .ok_or_else(|| Error::invalid_key("empty sum".to_string()))?;
131            if let Sum::Ed25519(b) = sum {
132                let key = Ed25519::try_from(&b[..])?;
133                return Ok(PublicKey::Ed25519(key));
134            }
135            #[cfg(feature = "secp256k1")]
136            if let Sum::Secp256k1(b) = sum {
137                return Self::from_raw_secp256k1(b)
138                    .ok_or_else(|| Error::invalid_key("malformed key".to_string()));
139            }
140            Err(Error::invalid_key("not an ed25519 key".to_string()))
141        }
142    }
143
144    impl From<PublicKey> for RawPublicKey {
145        fn from(value: PublicKey) -> Self {
146            match value {
147                PublicKey::Ed25519(ref pk) => RawPublicKey {
148                    sum: Some(Sum::Ed25519(
149                        pk.as_bytes().to_vec(),
150                    )),
151                },
152                #[cfg(feature = "secp256k1")]
153                PublicKey::Secp256k1(ref pk) => RawPublicKey {
154                    sum: Some(Sum::Secp256k1(
155                        pk.to_sec1_bytes().into(),
156                    )),
157                },
158            }
159        }
160    }
161}
162
163impl PublicKey {
164    /// From raw secp256k1 public key bytes
165    #[cfg(feature = "secp256k1")]
166    #[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
167    pub fn from_raw_secp256k1(bytes: &[u8]) -> Option<PublicKey> {
168        Secp256k1::from_sec1_bytes(bytes)
169            .ok()
170            .map(PublicKey::Secp256k1)
171    }
172
173    /// From raw Ed25519 public key bytes
174    pub fn from_raw_ed25519(bytes: &[u8]) -> Option<PublicKey> {
175        Ed25519::try_from(bytes).map(PublicKey::Ed25519).ok()
176    }
177
178    /// From an [`ed25519_consensus::VerificationKey`]
179    #[cfg(feature = "rust-crypto")]
180    pub fn from_ed25519_consensus(vk: ed25519_consensus::VerificationKey) -> Self {
181        Self::from(vk)
182    }
183
184    /// Get Ed25519 public key
185    pub fn ed25519(self) -> Option<Ed25519> {
186        #[allow(unreachable_patterns)]
187        match self {
188            PublicKey::Ed25519(pk) => Some(pk),
189            _ => None,
190        }
191    }
192
193    /// Get Secp256k1 public key
194    #[cfg(feature = "secp256k1")]
195    #[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
196    pub fn secp256k1(self) -> Option<Secp256k1> {
197        match self {
198            PublicKey::Secp256k1(pk) => Some(pk),
199            _ => None,
200        }
201    }
202
203    /// Serialize this key as a byte vector.
204    pub fn to_bytes(self) -> Vec<u8> {
205        match self {
206            PublicKey::Ed25519(pk) => pk.as_bytes().to_vec(),
207            #[cfg(feature = "secp256k1")]
208            PublicKey::Secp256k1(pk) => pk.to_sec1_bytes().into(),
209        }
210    }
211
212    /// Serialize this key as Bech32 with the given human readable prefix
213    pub fn to_bech32(self, hrp: &str) -> String {
214        let backward_compatible_amino_prefixed_pubkey = match self {
215            PublicKey::Ed25519(ref pk) => {
216                let mut key_bytes = vec![0x16, 0x24, 0xDE, 0x64, 0x20];
217                key_bytes.extend(pk.as_bytes());
218                key_bytes
219            },
220            #[cfg(feature = "secp256k1")]
221            PublicKey::Secp256k1(ref pk) => {
222                let mut key_bytes = vec![0xEB, 0x5A, 0xE9, 0x87, 0x21];
223                key_bytes.extend(pk.to_sec1_bytes().as_ref());
224                key_bytes
225            },
226        };
227        bech32::encode(hrp, backward_compatible_amino_prefixed_pubkey)
228    }
229
230    /// Serialize this key as hexadecimal
231    pub fn to_hex(self) -> String {
232        String::from_utf8(hex::encode_upper(self.to_bytes())).unwrap()
233    }
234}
235
236impl From<Ed25519> for PublicKey {
237    fn from(pk: Ed25519) -> PublicKey {
238        PublicKey::Ed25519(pk)
239    }
240}
241
242#[cfg(feature = "secp256k1")]
243impl From<Secp256k1> for PublicKey {
244    fn from(pk: Secp256k1) -> PublicKey {
245        PublicKey::Secp256k1(pk)
246    }
247}
248
249#[cfg(feature = "rust-crypto")]
250impl From<ed25519_consensus::VerificationKey> for PublicKey {
251    fn from(vk: ed25519_consensus::VerificationKey) -> PublicKey {
252        PublicKey::Ed25519(vk.into())
253    }
254}
255
256impl PartialOrd for PublicKey {
257    fn partial_cmp(&self, other: &PublicKey) -> Option<Ordering> {
258        Some(self.cmp(other))
259    }
260}
261
262impl Ord for PublicKey {
263    fn cmp(&self, other: &Self) -> Ordering {
264        match self {
265            PublicKey::Ed25519(a) => match other {
266                PublicKey::Ed25519(b) => a.as_bytes().cmp(b.as_bytes()),
267                #[cfg(feature = "secp256k1")]
268                PublicKey::Secp256k1(_) => Ordering::Less,
269            },
270            #[cfg(feature = "secp256k1")]
271            PublicKey::Secp256k1(a) => match other {
272                PublicKey::Ed25519(_) => Ordering::Greater,
273                #[cfg(feature = "secp256k1")]
274                PublicKey::Secp256k1(b) => a.cmp(b),
275            },
276        }
277    }
278}
279
280/// Public key roles used in Tendermint networks
281#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
282pub enum TendermintKey {
283    /// User signing keys used for interacting with accounts in the state machine
284    AccountKey(PublicKey),
285
286    /// Validator signing keys used for authenticating consensus protocol messages
287    ConsensusKey(PublicKey),
288}
289
290impl TendermintKey {
291    /// Create a new account key from a [`PublicKey`]
292    pub fn new_account_key(public_key: PublicKey) -> Result<TendermintKey, Error> {
293        match public_key {
294            PublicKey::Ed25519(_) => Ok(TendermintKey::AccountKey(public_key)),
295            #[cfg(feature = "secp256k1")]
296            PublicKey::Secp256k1(_) => Ok(TendermintKey::AccountKey(public_key)),
297        }
298    }
299
300    /// Create a new consensus key from a [`PublicKey`]
301    pub fn new_consensus_key(public_key: PublicKey) -> Result<TendermintKey, Error> {
302        #[allow(unreachable_patterns)]
303        match public_key {
304            PublicKey::Ed25519(_) => Ok(TendermintKey::AccountKey(public_key)),
305            #[cfg(feature = "secp256k1")]
306            PublicKey::Secp256k1(_) => Ok(TendermintKey::AccountKey(public_key)),
307
308            _ => Err(Error::invalid_key(
309                "only ed25519 or secp256k1 consensus keys are supported".to_string(),
310            )),
311        }
312    }
313
314    /// Get the [`PublicKey`] value for this [`TendermintKey`]
315    pub fn public_key(&self) -> &PublicKey {
316        match self {
317            TendermintKey::AccountKey(key) => key,
318            TendermintKey::ConsensusKey(key) => key,
319        }
320    }
321}
322
323/// Public key algorithms
324#[derive(Copy, Clone, Debug, Eq, PartialEq)]
325pub enum Algorithm {
326    /// ed25519
327    Ed25519,
328
329    /// secp256k1
330    Secp256k1,
331}
332
333impl Algorithm {
334    /// Get the string label for this algorithm
335    pub fn as_str(&self) -> &str {
336        match self {
337            Algorithm::Ed25519 => "ed25519",
338            Algorithm::Secp256k1 => "secp256k1",
339        }
340    }
341}
342
343impl fmt::Display for Algorithm {
344    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345        write!(f, "{}", self.as_str())
346    }
347}
348
349impl FromStr for Algorithm {
350    type Err = Error;
351
352    fn from_str(s: &str) -> Result<Self, Error> {
353        match s {
354            "ed25519" => Ok(Algorithm::Ed25519),
355            "secp256k1" => Ok(Algorithm::Secp256k1),
356            _ => Err(Error::parse(format!("invalid algorithm: {s}"))),
357        }
358    }
359}
360
361impl Serialize for Algorithm {
362    fn serialize<S: ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
363        self.as_str().serialize(serializer)
364    }
365}
366
367impl<'de> Deserialize<'de> for Algorithm {
368    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
369        use de::Error;
370        let s = String::deserialize(deserializer)?;
371        s.parse().map_err(D::Error::custom)
372    }
373}
374
375/// Serialize the bytes of an Ed25519 public key as Base64. Used for serializing JSON
376fn serialize_ed25519_base64<S>(pk: &Ed25519, serializer: S) -> Result<S::Ok, S::Error>
377where
378    S: ser::Serializer,
379{
380    String::from_utf8(base64::encode(pk.as_bytes()))
381        .unwrap()
382        .serialize(serializer)
383}
384
385/// Serialize the bytes of a secp256k1 ECDSA public key as Base64. Used for serializing JSON
386#[cfg(feature = "secp256k1")]
387fn serialize_secp256k1_base64<S>(pk: &Secp256k1, serializer: S) -> Result<S::Ok, S::Error>
388where
389    S: ser::Serializer,
390{
391    String::from_utf8(base64::encode(pk.to_sec1_bytes()))
392        .unwrap()
393        .serialize(serializer)
394}
395
396fn deserialize_ed25519_base64<'de, D>(deserializer: D) -> Result<Ed25519, D::Error>
397where
398    D: Deserializer<'de>,
399{
400    use de::Error;
401    let encoded = String::deserialize(deserializer)?;
402    let bytes = base64::decode(encoded).map_err(D::Error::custom)?;
403    Ed25519::try_from(&bytes[..]).map_err(|_| D::Error::custom("invalid Ed25519 key"))
404}
405
406#[cfg(feature = "secp256k1")]
407fn deserialize_secp256k1_base64<'de, D>(deserializer: D) -> Result<Secp256k1, D::Error>
408where
409    D: Deserializer<'de>,
410{
411    use de::Error;
412    let encoded = String::deserialize(deserializer)?;
413    let bytes = base64::decode(encoded).map_err(D::Error::custom)?;
414    Secp256k1::from_sec1_bytes(&bytes).map_err(|_| D::Error::custom("invalid secp256k1 key"))
415}
416
417#[cfg(test)]
418mod tests {
419    use subtle_encoding::hex;
420
421    use super::{PublicKey, TendermintKey};
422    use crate::{prelude::*, public_key::PubKeyResponse};
423
424    const EXAMPLE_CONSENSUS_KEY: &str =
425        "4A25C6640A1F72B9C975338294EF51B6D1C33158BB6ECBA69FBC3FB5A33C9DCE";
426
427    #[test]
428    fn test_consensus_serialization() {
429        let example_key = TendermintKey::ConsensusKey(
430            PublicKey::from_raw_ed25519(&hex::decode_upper(EXAMPLE_CONSENSUS_KEY).unwrap())
431                .unwrap(),
432        );
433        // Key created from:
434        // import (
435        // "encoding/hex"
436        // "fmt"
437        // "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
438        // "github.com/cosmos/cosmos-sdk/types"
439        // )
440        //
441        // func bech32conspub() {
442        // pubBz, _ :=
443        // hex.DecodeString("4A25C6640A1F72B9C975338294EF51B6D1C33158BB6ECBA69FBC3FB5A33C9DCE")
444        // pub := &ed25519.PubKey{Key: pubBz}
445        // mustBech32ConsPub := types.MustBech32ifyPubKey(types.Bech32PubKeyTypeConsPub, pub)
446        // fmt.Println(mustBech32ConsPub)
447        // }
448        assert_eq!(
449            example_key.public_key().to_bech32("cosmosvalconspub"),
450            "cosmosvalconspub1zcjduepqfgjuveq2raetnjt4xwpffm63kmguxv2chdhvhf5lhslmtgeunh8qmf7exk"
451        );
452    }
453
454    #[test]
455    #[cfg(feature = "secp256k1")]
456    fn test_account_serialization() {
457        const EXAMPLE_ACCOUNT_KEY: &str =
458            "02A1633CAFCC01EBFB6D78E39F687A1F0995C62FC95F51EAD10A02EE0BE551B5DC";
459        let example_key = TendermintKey::AccountKey(
460            PublicKey::from_raw_secp256k1(&hex::decode_upper(EXAMPLE_ACCOUNT_KEY).unwrap())
461                .unwrap(),
462        );
463        assert_eq!(
464            example_key.public_key().to_bech32("cosmospub"),
465            "cosmospub1addwnpepq2skx090esq7h7md0r3e76r6ruyet330e904r6k3pgpwuzl92x6actrt4uq"
466        );
467    }
468
469    #[test]
470    fn json_parsing() {
471        let json_string = "{\"type\":\"tendermint/PubKeyEd25519\",\"value\":\"RblzMO4is5L1hZz6wo4kPbptzOyue6LTk4+lPhD1FRk=\"}";
472        let pubkey: PublicKey = serde_json::from_str(json_string).unwrap();
473
474        assert_eq!(
475            pubkey.ed25519().unwrap().as_bytes(),
476            [
477                69, 185, 115, 48, 238, 34, 179, 146, 245, 133, 156, 250, 194, 142, 36, 61, 186,
478                109, 204, 236, 174, 123, 162, 211, 147, 143, 165, 62, 16, 245, 21, 25
479            ]
480        );
481
482        let reserialized_json = serde_json::to_string(&pubkey).unwrap();
483        assert_eq!(reserialized_json.as_str(), json_string);
484    }
485
486    tendermint_pb_modules! {
487        use super::*;
488        use pb::privval::PubKeyResponse as RawPubKeyResponse;
489
490        #[test]
491        fn test_ed25519_pubkey_msg() {
492            // test-vector generated from Go
493            // import (
494            // "fmt"
495            // "github.com/tendermint/tendermint/proto/tendermint/crypto"
496            // "github.com/tendermint/tendermint/proto/tendermint/privval"
497            // )
498            //
499            // func ed25519_key() {
500            // pkr := &privval.PubKeyResponse{
501            // PubKey: &crypto.PublicKey{
502            // Sum: &crypto.PublicKey_Ed25519{Ed25519: []byte{
503            // 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58,
504            // 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26,
505            // },
506            // },
507            // },
508            // Error: nil,
509            // }
510            // pbpk, _ := pkr.Marshal()
511            // fmt.Printf("%#v\n", pbpk)
512            //
513            // }
514            let encoded = vec![
515                0xa, 0x22, 0xa, 0x20, 0xd7, 0x5a, 0x98, 0x1, 0x82, 0xb1, 0xa, 0xb7, 0xd5, 0x4b, 0xfe,
516                0xd3, 0xc9, 0x64, 0x7, 0x3a, 0xe, 0xe1, 0x72, 0xf3, 0xda, 0xa6, 0x23, 0x25, 0xaf, 0x2,
517                0x1a, 0x68, 0xf7, 0x7, 0x51, 0x1a,
518            ];
519
520            let msg = PubKeyResponse {
521                pub_key: Some(
522                    PublicKey::from_raw_ed25519(&[
523                        215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, 14,
524                        225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26,
525                    ])
526                    .unwrap(),
527                ),
528                error: None,
529            };
530            let got = Protobuf::<RawPubKeyResponse>::encode_vec(msg.clone());
531
532            assert_eq!(got, encoded);
533            let decoded = <PubKeyResponse as Protobuf<RawPubKeyResponse>>::decode_vec(
534                &encoded
535            ).unwrap();
536            assert_eq!(decoded, msg);
537        }
538    }
539}