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