tendermint/
private_key.rs

1//! Cryptographic private keys
2
3pub use crate::crypto::ed25519::SigningKey as Ed25519;
4#[cfg(feature = "secp256k1")]
5pub use k256::ecdsa::SigningKey as Secp256k1;
6
7use crate::prelude::*;
8
9#[cfg(feature = "rust-crypto")]
10use crate::public_key::PublicKey;
11
12#[cfg(feature = "rust-crypto")]
13use serde::{de, ser, Deserialize, Serialize};
14#[cfg(feature = "rust-crypto")]
15use subtle_encoding::{Base64, Encoding};
16#[cfg(feature = "rust-crypto")]
17use zeroize::Zeroizing;
18
19pub const ED25519_KEYPAIR_SIZE: usize = 64;
20pub const SECP256K1_KEY_SIZE: usize = 32;
21
22/// Private keys as parsed from configuration files
23#[cfg_attr(feature = "rust-crypto", derive(Serialize, Deserialize))]
24#[cfg_attr(feature = "rust-crypto", serde(tag = "type", content = "value"))] // JSON custom serialization for priv_validator_key.json
25#[non_exhaustive]
26pub enum PrivateKey {
27    /// Ed25519 keys
28    #[cfg_attr(
29        feature = "rust-crypto",
30        serde(
31            rename = "tendermint/PrivKeyEd25519",
32            serialize_with = "serialize_ed25519_keypair",
33            deserialize_with = "deserialize_ed25519_keypair"
34        )
35    )]
36    Ed25519(Ed25519),
37
38    #[cfg(feature = "secp256k1")]
39    #[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
40    #[serde(
41        rename = "tendermint/PrivKeySecp256k1",
42        serialize_with = "serialize_secp256k1_privkey",
43        deserialize_with = "deserialize_secp256k1_privkey"
44    )]
45    Secp256k1(Secp256k1),
46}
47
48impl PrivateKey {
49    /// Get the public key associated with this private key
50    #[cfg(feature = "rust-crypto")]
51    pub fn public_key(&self) -> PublicKey {
52        match self {
53            PrivateKey::Ed25519(signing_key) => PublicKey::Ed25519(signing_key.verification_key()),
54
55            #[cfg(feature = "secp256k1")]
56            PrivateKey::Secp256k1(signing_key) => {
57                PublicKey::Secp256k1(*signing_key.verifying_key())
58            },
59        }
60    }
61
62    /// If applicable, borrow the Ed25519 keypair
63    pub fn ed25519_signing_key(&self) -> Option<&Ed25519> {
64        match self {
65            PrivateKey::Ed25519(signing_key) => Some(signing_key),
66
67            #[cfg(feature = "secp256k1")]
68            PrivateKey::Secp256k1(_signing_key) => None,
69        }
70    }
71
72    /// From an [`ed25519_consensus::SigningKey`]
73    #[cfg(feature = "rust-crypto")]
74    pub fn from_ed25519_consensus(sk: ed25519_consensus::SigningKey) -> Self {
75        Self::Ed25519(sk.into())
76    }
77}
78
79#[cfg(feature = "rust-crypto")]
80impl From<ed25519_consensus::SigningKey> for PrivateKey {
81    fn from(sk: ed25519_consensus::SigningKey) -> Self {
82        Self::Ed25519(sk.into())
83    }
84}
85
86/// Serialize a Secp256k1 privkey as Base64
87#[cfg(feature = "secp256k1")]
88fn serialize_secp256k1_privkey<S>(signing_key: &Secp256k1, serializer: S) -> Result<S::Ok, S::Error>
89where
90    S: ser::Serializer,
91{
92    Zeroizing::new(String::from_utf8(Base64::default().encode(signing_key.to_bytes())).unwrap())
93        .serialize(serializer)
94}
95
96/// Deserialize a Secp256k1 privkey from Base64
97#[cfg(feature = "secp256k1")]
98fn deserialize_secp256k1_privkey<'de, D>(deserializer: D) -> Result<Secp256k1, D::Error>
99where
100    D: de::Deserializer<'de>,
101{
102    use de::Error;
103    let string = Zeroizing::new(String::deserialize(deserializer)?);
104    let mut privkey_bytes = Zeroizing::new([0u8; SECP256K1_KEY_SIZE]);
105    let decoded_len = Base64::default()
106        .decode_to_slice(string.as_bytes(), &mut *privkey_bytes)
107        .map_err(D::Error::custom)?;
108
109    if decoded_len != SECP256K1_KEY_SIZE {
110        return Err(D::Error::custom("invalid sepc256k1 privkey size"));
111    }
112
113    let signing_key = Secp256k1::try_from(&privkey_bytes[0..SECP256K1_KEY_SIZE])
114        .map_err(|_| D::Error::custom("invalid signing key"))?;
115
116    Ok(signing_key)
117}
118
119/// Serialize an Ed25519 keypair as Base64
120#[cfg(feature = "rust-crypto")]
121fn serialize_ed25519_keypair<S>(signing_key: &Ed25519, serializer: S) -> Result<S::Ok, S::Error>
122where
123    S: ser::Serializer,
124{
125    // Tendermint uses a serialization format inherited from Go that includes
126    // a cached copy of the public key as the second half.
127    let mut keypair_bytes = Zeroizing::new([0u8; ED25519_KEYPAIR_SIZE]);
128    keypair_bytes[0..32].copy_from_slice(signing_key.as_bytes());
129    keypair_bytes[32..64].copy_from_slice(signing_key.verification_key().as_bytes());
130    Zeroizing::new(String::from_utf8(Base64::default().encode(&keypair_bytes[..])).unwrap())
131        .serialize(serializer)
132}
133
134/// Deserialize an Ed25519 keypair from Base64
135#[cfg(feature = "rust-crypto")]
136fn deserialize_ed25519_keypair<'de, D>(deserializer: D) -> Result<Ed25519, D::Error>
137where
138    D: de::Deserializer<'de>,
139{
140    use de::Error;
141    let string = Zeroizing::new(String::deserialize(deserializer)?);
142    let mut keypair_bytes = Zeroizing::new([0u8; ED25519_KEYPAIR_SIZE]);
143    let decoded_len = Base64::default()
144        .decode_to_slice(string.as_bytes(), &mut *keypair_bytes)
145        .map_err(D::Error::custom)?;
146
147    if decoded_len != ED25519_KEYPAIR_SIZE {
148        return Err(D::Error::custom("invalid Ed25519 keypair size"));
149    }
150
151    // Tendermint uses a serialization format inherited from Go that includes a
152    // cached copy of the public key as the second half.  This is somewhat
153    // dangerous, since there's no validation that the two parts are consistent
154    // with each other, so we ignore the second half and just check consistency
155    // with the re-derived data.
156    let signing_key = Ed25519::try_from(&keypair_bytes[0..32])
157        .map_err(|_| D::Error::custom("invalid signing key"))?;
158    let verification_key = signing_key.verification_key();
159    if &keypair_bytes[32..64] != verification_key.as_bytes() {
160        return Err(D::Error::custom("keypair mismatch"));
161    }
162
163    Ok(signing_key)
164}