exocore_core/sec/
keys.rs

1use libp2p_identity::{
2    ed25519 as libp2p_ed25519, secp256k1 as libp2p_secp256k1, Keypair as libp2p_Keypair,
3    PublicKey as libp2p_PublicKey,
4};
5use rand::SeedableRng;
6
7const ENCODE_KEYPAIR_CODE: u8 = b'a';
8const ENCODE_PUBLIC_KEY_CODE: u8 = b'p';
9
10/// Private and public keypair used for nodes and cells.
11#[derive(Clone)]
12pub struct Keypair {
13    keypair: libp2p_Keypair,
14    keypair_type: KeypairType,
15}
16
17#[derive(Clone)]
18enum KeypairType {
19    Ed25519 { keypair: libp2p_ed25519::Keypair },
20    Secp256k1 { _keypair: libp2p_secp256k1::Keypair },
21}
22
23impl Keypair {
24    pub fn from_libp2p(keypair: libp2p_Keypair) -> Keypair {
25        let keypair_type = match keypair.key_type() {
26            libp2p_identity::KeyType::Ed25519 => KeypairType::Ed25519 {
27                keypair: keypair.clone().try_into_ed25519().unwrap(),
28            },
29            libp2p_identity::KeyType::Secp256k1 => KeypairType::Secp256k1 {
30                _keypair: keypair.clone().try_into_secp256k1().unwrap(),
31            },
32            _ => unimplemented!(),
33        };
34
35        Keypair {
36            keypair,
37            keypair_type,
38        }
39    }
40
41    pub fn generate_ed25519() -> Keypair {
42        Self::from_libp2p(libp2p_Keypair::generate_ed25519())
43    }
44
45    pub fn public(&self) -> PublicKey {
46        PublicKey::from_libp2p(self.keypair.public())
47    }
48
49    pub fn algorithm(&self) -> Algorithm {
50        match self.keypair_type {
51            KeypairType::Ed25519 { keypair: _ } => Algorithm::Ed25519,
52            KeypairType::Secp256k1 { _keypair: _ } => Algorithm::Secp256k1,
53        }
54    }
55
56    pub fn to_libp2p(&self) -> &libp2p_Keypair {
57        &self.keypair
58    }
59
60    /// Sign given message with the keypair.
61    /// The `verify` method on the public key can be used to validate signature.
62    pub fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, Error> {
63        self.keypair
64            .sign(msg)
65            .map_err(|err| Error::Libp2pSigning(err.to_string()))
66    }
67
68    /// Encode the keypair into a bytes representation.
69    pub fn encode(&self) -> Vec<u8> {
70        match &self.keypair_type {
71            KeypairType::Ed25519 { keypair } => {
72                let mut vec = vec![0; 66];
73                vec[0] = ENCODE_KEYPAIR_CODE;
74                vec[1] = Algorithm::Ed25519.to_code();
75                vec[2..].copy_from_slice(&keypair.to_bytes());
76                vec
77            }
78            _ => unimplemented!(),
79        }
80    }
81
82    /// Encode the keypair into a base58 representation
83    pub fn encode_base58_string(&self) -> String {
84        encode_base58(&self.encode())
85    }
86
87    /// Decodes given bytes into a keypair.
88    /// The method takes a mutable slice since libp2p zeroize it afterward.
89    pub fn decode(bytes: &mut [u8]) -> Result<Keypair, Error> {
90        if bytes.len() < 3 {
91            return Err(Error::DecodeInvalidSize);
92        }
93
94        if bytes[0] != ENCODE_KEYPAIR_CODE {
95            return Err(Error::DecodeExpectedPair);
96        }
97
98        match Algorithm::from_code(bytes[1])? {
99            Algorithm::Ed25519 => {
100                let keypair = libp2p_ed25519::Keypair::try_from_bytes(&mut bytes[2..])
101                    .map_err(|err| Error::Libp2pDecode(err.to_string()))?;
102
103                Ok(Self::from_libp2p(keypair.into()))
104            }
105            _ => unimplemented!(),
106        }
107    }
108
109    /// Decode given a base58 represented string into a keypair.
110    pub fn decode_base58_string(input: &str) -> Result<Keypair, Error> {
111        let mut bytes = decode_base58(input)?;
112        Self::decode(&mut bytes)
113    }
114}
115
116/// Public key
117#[derive(Clone, Debug, PartialEq, Eq)]
118pub struct PublicKey {
119    key: libp2p_PublicKey,
120    key_type: PublicKeyType,
121}
122
123#[derive(Clone, Debug, PartialEq, Eq)]
124enum PublicKeyType {
125    Ed25519(libp2p_ed25519::PublicKey),
126    Secp256k1(libp2p_secp256k1::PublicKey),
127}
128
129impl PublicKey {
130    pub fn from_libp2p(key: libp2p_PublicKey) -> PublicKey {
131        let key_type = match key.key_type() {
132            libp2p_identity::KeyType::Ed25519 => {
133                PublicKeyType::Ed25519(key.clone().try_into_ed25519().unwrap())
134            }
135            libp2p_identity::KeyType::Secp256k1 => {
136                PublicKeyType::Secp256k1(key.clone().try_into_secp256k1().unwrap())
137            }
138            _ => unimplemented!(),
139        };
140
141        PublicKey { key, key_type }
142    }
143
144    pub fn to_libp2p(&self) -> &libp2p_PublicKey {
145        &self.key
146    }
147
148    /// Verify the message for authenticity (signed by key) and integrity (not
149    /// tampered with).
150    pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
151        self.key.verify(msg, sig)
152    }
153
154    /// Encode the public key into a bytes representation.
155    pub fn encode(&self) -> Vec<u8> {
156        match &self.key_type {
157            PublicKeyType::Ed25519(pk) => {
158                let mut vec = vec![0; 34];
159                vec[0] = ENCODE_PUBLIC_KEY_CODE;
160                vec[1] = Algorithm::Ed25519.to_code();
161                vec[2..].copy_from_slice(&pk.to_bytes());
162                vec
163            }
164            _ => unimplemented!(),
165        }
166    }
167
168    /// Encode the public key into a base58 representation
169    pub fn encode_base58_string(&self) -> String {
170        encode_base58(&self.encode())
171    }
172
173    /// Decodes given bytes into a public key.
174    pub fn decode(bytes: &[u8]) -> Result<PublicKey, Error> {
175        if bytes.len() < 3 {
176            return Err(Error::DecodeInvalidSize);
177        }
178
179        if bytes[0] != ENCODE_PUBLIC_KEY_CODE {
180            return Err(Error::DecodeExpectedPublic);
181        }
182
183        match Algorithm::from_code(bytes[1])? {
184            Algorithm::Ed25519 => {
185                let pk = libp2p_ed25519::PublicKey::try_from_bytes(&bytes[2..])
186                    .map_err(|err| Error::Libp2pDecode(err.to_string()))?;
187
188                Ok(PublicKey::from_libp2p(pk.into()))
189            }
190            _ => unimplemented!(),
191        }
192    }
193
194    /// Decode given a base58 represented string into a public key.
195    pub fn decode_base58_string(input: &str) -> Result<PublicKey, Error> {
196        let bytes = decode_base58(input)?;
197        Self::decode(&bytes)
198    }
199
200    /// Generates a deterministic random name from this public key
201    pub fn generate_name(&self) -> String {
202        let bytes = self.encode();
203        let bytes_len = bytes.len();
204
205        let mut rng = rand::prelude::StdRng::seed_from_u64(u64::from_le_bytes([
206            bytes[bytes_len - 1],
207            bytes[bytes_len - 2],
208            bytes[bytes_len - 3],
209            bytes[bytes_len - 4],
210            bytes[bytes_len - 5],
211            bytes[bytes_len - 6],
212            bytes[bytes_len - 7],
213            bytes[bytes_len - 8],
214        ]));
215        petname::Petnames::default().generate(&mut rng, 3, "-")
216    }
217}
218
219/// Convert key to base58 representation
220fn encode_base58(bytes: &[u8]) -> String {
221    format!(
222        "{}{}{}",
223        char::from(bytes[0]),
224        char::from(bytes[1]),
225        bs58::encode(&bytes[2..]).into_string()
226    )
227}
228
229/// Convert base58 key representation to bytes
230fn decode_base58(input: &str) -> Result<Vec<u8>, Error> {
231    let input_bytes = input.as_bytes();
232    if input_bytes.len() < 3 {
233        return Err(Error::DecodeInvalidSize);
234    }
235
236    // see `bs58::decode::into_vec()`
237    let mut output = vec![0; (input_bytes.len() / 8 + 1) * 6];
238    output[0..2].copy_from_slice(&input_bytes[0..2]);
239
240    let len = bs58::decode(&input[2..]).onto(&mut output[2..])?;
241
242    output.truncate(len + 2);
243
244    Ok(output)
245}
246
247/// Encryption / signature algorithm type
248#[derive(Clone, Copy, PartialEq, Eq, Debug)]
249pub enum Algorithm {
250    Ed25519,
251    Secp256k1,
252}
253
254impl Algorithm {
255    fn to_code(self) -> u8 {
256        match self {
257            Algorithm::Ed25519 => b'e',
258            Algorithm::Secp256k1 => b'c',
259        }
260    }
261
262    fn from_code(code: u8) -> Result<Algorithm, Error> {
263        match code {
264            b'e' => Ok(Algorithm::Ed25519),
265            b'c' => Ok(Algorithm::Secp256k1),
266            _ => Err(Error::InvalidAlgorithmCode(code)),
267        }
268    }
269}
270
271/// Cryptographic keys related error
272#[derive(Debug, thiserror::Error)]
273pub enum Error {
274    #[error("Given bytes to decode doesn't have the right size")]
275    DecodeInvalidSize,
276
277    #[error("Given bytes to decode wasn't a keypair")]
278    DecodeExpectedPair,
279
280    #[error("Given bytes to decode wasn't a public key")]
281    DecodeExpectedPublic,
282
283    #[error("Given bytes couldn't be decoded by libp2p: {0}")]
284    Libp2pDecode(String),
285
286    #[error("Couldn't decode base58 string into bytes: {0}")]
287    Base58Decode(#[from] bs58::decode::Error),
288
289    #[error("Algorithm code is invalid: {0}")]
290    InvalidAlgorithmCode(u8),
291
292    #[error("Libp2p signing error: {0}")]
293    Libp2pSigning(String),
294}
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299
300    #[test]
301    pub fn ed25519_keypair_encode_decode() -> anyhow::Result<()> {
302        let keypair = Keypair::generate_ed25519();
303
304        let mut encoded = keypair.encode();
305        let keypair_decoded = Keypair::decode(&mut encoded)?;
306        assert_eq!(keypair.public(), keypair_decoded.public());
307
308        assert!(Keypair::decode(&mut []).is_err());
309        assert!(Keypair::decode(&mut [0]).is_err());
310        assert!(Keypair::decode(&mut [0, 0]).is_err());
311        assert!(Keypair::decode(&mut [0, 0, 0]).is_err());
312
313        Ok(())
314    }
315
316    #[test]
317    pub fn ed25519_keypair_base58_encode_decode() -> anyhow::Result<()> {
318        let keypair = Keypair::generate_ed25519();
319
320        let encoded_bytes = keypair.encode();
321        let encoded_base58 = encode_base58(&encoded_bytes);
322        let decoded_base58 = decode_base58(&encoded_base58)?;
323        assert_eq!(encoded_bytes, decoded_base58);
324
325        let encoded = keypair.encode_base58_string();
326        let keypair_decoded = Keypair::decode_base58_string(&encoded)?;
327        assert_eq!(keypair.public(), keypair_decoded.public());
328
329        assert!(Keypair::decode_base58_string("").is_err());
330        assert!(Keypair::decode_base58_string("a").is_err());
331        assert!(Keypair::decode_base58_string("ae").is_err());
332        assert!(Keypair::decode_base58_string("aeb").is_err());
333
334        Ok(())
335    }
336
337    #[test]
338    pub fn ed25519_public_key_encode_decode() -> anyhow::Result<()> {
339        let keypair = Keypair::generate_ed25519();
340
341        let encoded = keypair.public().encode();
342        let public_decoded = PublicKey::decode(&encoded)?;
343
344        assert_eq!(keypair.public(), public_decoded);
345
346        assert!(PublicKey::decode(&[]).is_err());
347        assert!(PublicKey::decode(&[0]).is_err());
348        assert!(PublicKey::decode(&[0, 0]).is_err());
349        assert!(PublicKey::decode(&[0, 0, 0]).is_err());
350
351        Ok(())
352    }
353
354    #[test]
355    pub fn ed25519_public_key_base58_encode_decode() -> anyhow::Result<()> {
356        let keypair = Keypair::generate_ed25519();
357
358        let encoded_bytes = keypair.public().encode();
359        let encoded_base58 = encode_base58(&encoded_bytes);
360        let decoded_base58 = decode_base58(&encoded_base58)?;
361        assert_eq!(encoded_bytes, decoded_base58);
362
363        let encoded = keypair.public().encode_base58_string();
364        let public_decoded = PublicKey::decode_base58_string(&encoded)?;
365        assert_eq!(keypair.public(), public_decoded);
366
367        assert!(PublicKey::decode_base58_string("").is_err());
368        assert!(PublicKey::decode_base58_string("p").is_err());
369        assert!(PublicKey::decode_base58_string("pe").is_err());
370        assert!(PublicKey::decode_base58_string("peb").is_err());
371
372        Ok(())
373    }
374
375    #[test]
376    pub fn signature_and_verification() -> anyhow::Result<()> {
377        let keypair = Keypair::generate_ed25519();
378
379        let msg = String::from("hello world").into_bytes();
380        let sig = keypair.sign(&msg)?;
381
382        let pk = keypair.public();
383        assert!(pk.verify(&msg, &sig));
384
385        let mut invalid_sig = sig.clone();
386        invalid_sig[5] = b'c';
387        invalid_sig[6] = b'd';
388        invalid_sig[7] = b'd';
389
390        assert!(!pk.verify(&msg, &invalid_sig));
391
392        let tampered_msg = String::from("h4x0r").into_bytes();
393        assert!(!pk.verify(&tampered_msg, &sig));
394        Ok(())
395    }
396}