stellar_baselib/
keypair.rs

1//! `Keypair` represents public (and secret) keys of the account.
2//!
3//! Currently `Keypair` only supports ed25519 but in the future, this class can be an abstraction layer for other
4//! public-key signature systems.
5use crate::hashing::HashingBehavior;
6use crate::hashing::Sha256Hasher;
7use crate::signing::{generate, sign, verify};
8use crate::xdr;
9use crate::xdr::WriteXdr;
10use hex::FromHex;
11use rand_core::TryRngCore;
12use rand_core::{OsRng, RngCore};
13use sha2::Sha512;
14use std::str;
15use std::{error::Error, str::FromStr};
16use stellar_strkey::{
17    ed25519::{PrivateKey, PublicKey},
18    Strkey,
19};
20
21#[derive(Debug, Clone)]
22pub struct Keypair {
23    public_key: Vec<u8>,
24    secret_key: Option<Vec<u8>>,
25    secret_seed: Option<Vec<u8>>,
26}
27
28pub trait KeypairBehavior {
29    // Creates a new keypair given optional public and secret keys
30    fn new(
31        public_key: Option<[u8; 32]>,
32        secret_key: Option<[u8; 32]>,
33    ) -> Result<Self, Box<dyn Error>>
34    where
35        Self: Sized;
36
37    // Creates a keypair from a secret seed
38    fn new_from_secret_key(secret_seed: Vec<u8>) -> Result<Self, Box<dyn Error>>
39    where
40        Self: Sized;
41
42    // Creates a keypair from a public key
43    fn new_from_public_key(public_key: Vec<u8>) -> Result<Self, Box<dyn Error>>
44    where
45        Self: Sized;
46
47    // Creates a keypair from a secret string
48    fn from_secret(secret: &str) -> Result<Self, Box<dyn Error>>
49    where
50        Self: Sized;
51
52    // Creates a keypair from a public key string
53    fn from_public_key(public_key: &str) -> Result<Self, Box<dyn Error>>
54    where
55        Self: Sized;
56
57    // Creates a keypair from a raw Ed25519 seed
58    fn from_raw_ed25519_seed(seed: &[u8]) -> Result<Self, Box<dyn Error>>
59    where
60        Self: Sized;
61
62    // Returns the raw secret key
63    fn raw_secret_key(&self) -> Option<Vec<u8>>;
64
65    // Returns the raw public key
66    fn raw_public_key(&self) -> &Vec<u8>;
67
68    // Returns the secret key as a string
69    fn secret_key(&self) -> Result<String, Box<dyn Error>>;
70
71    // Returns the public key as a string
72    fn public_key(&self) -> String;
73
74    // Checks if the keypair can sign
75    fn can_sign(&self) -> bool;
76
77    // Signs the data using the keypair
78    fn sign(&self, data: &[u8]) -> Result<Vec<u8>, Box<dyn Error>>;
79
80    // Verifies if signature for the data is valid
81    fn verify(&self, data: &[u8], signature: &[u8]) -> bool;
82
83    // Creates a random Keypair
84    fn random() -> Result<Self, Box<dyn Error>>
85    where
86        Self: Sized;
87
88    // Returns keypair object which is the network master key
89    fn master(network_passphrase: Option<&str>) -> Result<Self, Box<dyn Error>>
90    where
91        Self: Sized;
92
93    // XDR representation of the account id
94    fn xdr_account_id(&self) -> xdr::AccountId;
95
96    // XDR representation of the public key
97    fn xdr_public_key(&self) -> xdr::PublicKey;
98
99    // XDR representation of the muxed account id
100    fn xdr_muxed_account_id(&self, id: &str) -> xdr::MuxedAccount;
101
102    // Returns the raw public key array
103    fn raw_pubkey(&self) -> [u8; 32];
104
105    // Part of the decorated signature
106    fn signature_hint(&self) -> Option<Vec<u8>>;
107
108    // Returns the decorated signature (hint+sig) for arbitrary data
109    fn sign_decorated(&self, data: &[u8]) -> xdr::DecoratedSignature;
110
111    // Returns the raw decorated signature (hint+sig) for a signed payload signer
112    fn sign_payload_decorated(&self, data: &[u8]) -> xdr::DecoratedSignature;
113}
114
115impl KeypairBehavior for Keypair {
116    /// Creates new keypair obj
117    fn new(
118        public_key: Option<[u8; 32]>,
119        secret_key: Option<[u8; 32]>,
120    ) -> Result<Self, Box<dyn Error>> {
121        if let Some(secret_key) = secret_key {
122            let sec_seed = secret_key;
123            let public_key_gen = generate(&sec_seed);
124            let mut secret_key = Vec::new();
125            secret_key.extend_from_slice(&sec_seed);
126            secret_key.extend_from_slice(&public_key_gen);
127
128            if let Some(public_key_arg) = public_key {
129                if public_key_arg != public_key_gen {
130                    return Err("secretKey does not match publicKey".into());
131                }
132            }
133
134            Ok(Self {
135                secret_seed: Some(sec_seed.to_vec()),
136                public_key: public_key_gen.to_vec(),
137                secret_key: Some(secret_key),
138            })
139        } else {
140            Ok(Self {
141                secret_seed: None,
142                public_key: public_key.unwrap().to_vec(),
143                secret_key: None,
144            })
145        }
146    }
147
148    /// Creates a keypair obj from secret seed
149    fn new_from_secret_key(secret_seed: Vec<u8>) -> Result<Self, Box<dyn Error>> {
150        if secret_seed.len() != 32 {
151            return Err("secret_key length is invalid".into());
152        }
153
154        let mut cloned_secret_key = secret_seed.clone();
155        let pkey = generate(&secret_seed);
156        let mut pk = pkey.clone().to_vec();
157
158        let mut secret_key = Vec::new();
159        secret_key.append(&mut cloned_secret_key);
160        secret_key.append(&mut pk);
161
162        Ok(Self {
163            secret_seed: Some(secret_seed),
164            public_key: pkey.to_vec(),
165            secret_key: Some(secret_key),
166        })
167    }
168
169    /// Creates a keypair obj from public key
170    fn new_from_public_key(public_key: Vec<u8>) -> Result<Self, Box<dyn Error>> {
171        if public_key.len() != 32 {
172            return Err("public_key length is invalid".into());
173        }
174
175        Ok(Self {
176            public_key,
177            secret_key: None,
178            secret_seed: None,
179        })
180    }
181
182    /// Create Keypair obj from secret key
183    fn from_secret(secret: &str) -> Result<Self, Box<dyn Error>> {
184        let raw_secret = PrivateKey::from_str(secret).unwrap().0;
185        Keypair::from_raw_ed25519_seed(&raw_secret)
186    }
187
188    /// Create Keypair obj from given public key
189    fn from_public_key(public_key: &str) -> Result<Self, Box<dyn Error>> {
190        let decoded = PublicKey::from_str(public_key)?;
191        if decoded.0.len() != 32 {
192            return Err("Invalid Stellar public key".into());
193        }
194
195        Ok(Self {
196            public_key: decoded.0.to_vec(),
197            secret_seed: None,
198            secret_key: None,
199        })
200    }
201
202    /// Create keypair obj from seed value
203    fn from_raw_ed25519_seed(seed: &[u8]) -> Result<Self, Box<dyn Error>> {
204        if seed.len() >= 33 {
205            return Err("Invalid seed length".into());
206        }
207        Self::new_from_secret_key(seed.to_vec())
208    }
209
210    /// Return the raw secret key
211    fn raw_secret_key(&self) -> Option<Vec<u8>> {
212        self.secret_seed.clone()
213    }
214
215    /// Return the public key
216    fn raw_public_key(&self) -> &Vec<u8> {
217        &self.public_key
218    }
219
220    /// Return the secret key string
221    fn secret_key(&self) -> Result<String, Box<dyn Error>> {
222        match &self.secret_seed {
223            None => Err("no secret_key available".into()),
224            Some(s) => Ok(PrivateKey::from_payload(s).unwrap().clone().to_string()),
225        }
226    }
227
228    /// Return the public key string
229    fn public_key(&self) -> String {
230        PublicKey::from_payload(&self.public_key)
231            .unwrap()
232            .to_string()
233    }
234
235    /// returns `true` if the keypair obj contains secret key and can sign.
236    fn can_sign(&self) -> bool {
237        self.secret_key.is_some()
238    }
239
240    /// Able to sign the data using the keypair obj
241    fn sign(&self, data: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
242        if !self.can_sign() {
243            return Err("cannot sign, no secret_key available".into());
244        }
245
246        if let Some(s) = &self.secret_key {
247            return Ok(sign(data, s).to_vec());
248        }
249
250        Err("error while signing".into())
251    }
252
253    /// verifies if signature for the data is valid
254    fn verify(&self, data: &[u8], signature: &[u8]) -> bool {
255        verify(data, signature, self.public_key.as_slice())
256    }
257
258    /// Creates a Random Keypair
259    fn random() -> Result<Self, Box<dyn Error>> {
260        let mut secret_seed = [0u8; 32];
261        let mut rng = OsRng;
262        rng.try_fill_bytes(&mut secret_seed);
263        Self::new_from_secret_key(secret_seed.to_vec())
264    }
265
266    /// Returns keypair obj which is the network master key
267    fn master(network_passphrase: Option<&str>) -> Result<Self, Box<dyn Error>> {
268        if let Some(passphrase) = network_passphrase {
269            Ok(Self::from_raw_ed25519_seed(&Sha256Hasher::hash(passphrase)).unwrap())
270        } else {
271            Err("No network selected. Please pass a network argument, e.g. `Keypair::master(Some(Networks::PUBLIC))`.".into())
272        }
273    }
274    /// xdr representation of the account id
275    fn xdr_account_id(&self) -> xdr::AccountId {
276        xdr::AccountId(xdr::PublicKey::PublicKeyTypeEd25519(xdr::Uint256(
277            PublicKey::from_payload(&self.public_key).unwrap().0,
278        )))
279    }
280
281    /// xdr representation of the public key
282    fn xdr_public_key(&self) -> xdr::PublicKey {
283        xdr::PublicKey::PublicKeyTypeEd25519(xdr::Uint256(
284            PublicKey::from_payload(&self.public_key).unwrap().0,
285        ))
286    }
287
288    /// xdr representation of the public key
289    fn xdr_muxed_account_id(&self, id: &str) -> xdr::MuxedAccount {
290        xdr::MuxedAccount::MuxedEd25519(xdr::MuxedAccountMed25519 {
291            id: xdr::Uint64::from_str(id).unwrap(),
292            ed25519: xdr::Uint256(PublicKey::from_payload(&self.public_key).unwrap().0),
293        })
294    }
295
296    fn raw_pubkey(&self) -> [u8; 32] {
297        let mut array: [u8; 32] = [0; 32];
298
299        for (i, &value) in self.public_key.iter().enumerate() {
300            array[i] = value;
301        }
302
303        array
304    }
305
306    /// part of the decorated signature
307    fn signature_hint(&self) -> Option<Vec<u8>> {
308        let a = Self::xdr_account_id(self)
309            .to_xdr(xdr::Limits::none())
310            .unwrap();
311        if a.len() >= 4 {
312            let start_index = a.len() - 4;
313            Some(a[start_index..].to_vec())
314        } else {
315            None
316        }
317    }
318
319    /// Returns the decorated signature (hint+sig) for arbitrary data.
320    fn sign_decorated(&self, data: &[u8]) -> xdr::DecoratedSignature {
321        let signature = Self::sign(self, data).unwrap();
322        let hint = Self::signature_hint(self).unwrap();
323        let mut hint_u8: [u8; 4] = [0; 4];
324        hint_u8.copy_from_slice(&hint[..4]);
325        let val = xdr::SignatureHint::from(hint_u8);
326        let signature_xdr = xdr::Signature::try_from(signature).unwrap();
327        xdr::DecoratedSignature {
328            hint: val,
329            signature: signature_xdr,
330        }
331    }
332
333    /// Returns the raw decorated signature (hint+sig) for a signed payload signer.
334    fn sign_payload_decorated(&self, data: &[u8]) -> xdr::DecoratedSignature {
335        let signature = Self::sign(self, data).unwrap();
336        let hint = Self::signature_hint(self).unwrap();
337        let mut key_hint_u8: [u8; 4] = [0; 4];
338        key_hint_u8.copy_from_slice(&hint[..4]);
339        let val = xdr::SignatureHint::from(key_hint_u8);
340        let signature_xdr = xdr::Signature::try_from(signature).unwrap();
341        let mut hint: [u8; 4] = [0; 4];
342
343        if data.len() >= 4 {
344            hint.copy_from_slice(&data[data.len() - 4..]);
345        } else {
346            hint[..data.len()].copy_from_slice(data);
347            #[allow(clippy::needless_range_loop)]
348            for i in data.len()..4 {
349                hint[i] = 0;
350            }
351        }
352
353        for i in 0..4 {
354            hint[i] ^= key_hint_u8[i];
355        }
356
357        let val = xdr::SignatureHint::from(hint);
358
359        xdr::DecoratedSignature {
360            hint: val,
361            signature: signature_xdr,
362        }
363    }
364}
365
366#[cfg(test)]
367mod tests {
368
369    use hex_literal::hex;
370    use sha2::digest::crypto_common::Key;
371
372    use super::*;
373
374    #[test]
375    fn keypair_constructor_fails_when_secret_key_does_not_match_public_key() {
376        let secret = "SD7X7LEHBNMUIKQGKPARG5TDJNBHKC346OUARHGZL5ITC6IJPXHILY36";
377        let kp = Keypair::from_secret(secret).unwrap();
378        let mut secret_key = kp.raw_secret_key().unwrap();
379        let c = secret_key.as_slice();
380        let mut public_key = PublicKey::from_str(kp.public_key().as_str()).unwrap().0;
381        public_key[0] = 0; // Make public key invalid
382        let keypair = Keypair::new(Some(public_key), Some(c.try_into().unwrap()));
383        assert!(keypair.is_err());
384        assert_eq!(
385            keypair.err().unwrap().to_string(),
386            "secretKey does not match publicKey"
387        )
388    }
389
390    #[test]
391    fn test_create_keypair_from_secret() {
392        let secret = "SD7X7LEHBNMUIKQGKPARG5TDJNBHKC346OUARHGZL5ITC6IJPXHILY36";
393        let expected_public_key = "GDFQVQCYYB7GKCGSCUSIQYXTPLV5YJ3XWDMWGQMDNM4EAXAL7LITIBQ7";
394        let keypair = Keypair::from_secret(secret).unwrap();
395        assert_eq!(keypair.public_key().as_str(), expected_public_key);
396        assert_eq!(keypair.secret_key().unwrap().as_str(), secret);
397    }
398
399    #[test]
400    #[should_panic]
401    fn test_create_keypair_from_invalid_secret() {
402        let invalid_secrets = [
403            "hel0",
404            "SBWUBZ3SIPLLF5CCXLWUB2Z6UBTYAW34KVXOLRQ5HDAZG4ZY7MHNBWJ1",
405            "masterpassphrasemasterpassphrase",
406            "gsYRSEQhTffqA9opPepAENCr2WG6z5iBHHubxxbRzWaHf8FBWcu",
407        ];
408        Keypair::from_secret(invalid_secrets[0]).unwrap();
409        Keypair::from_secret(invalid_secrets[1]).unwrap();
410        Keypair::from_secret(invalid_secrets[2]).unwrap();
411        Keypair::from_secret(invalid_secrets[3]).unwrap();
412    }
413
414    #[test]
415    fn test_create_keypair_from_raw_ed25519_seed() {
416        let seed = "masterpassphrasemasterpassphrase";
417        let expected_public_key = "GAXDYNIBA5E4DXR5TJN522RRYESFQ5UNUXHIPTFGVLLD5O5K552DF5ZH";
418        let expected_secret = "SBWWC43UMVZHAYLTONYGQ4TBONSW2YLTORSXE4DBONZXA2DSMFZWLP2R";
419        let expected_raw_public_key =
420            hex!("2e3c35010749c1de3d9a5bdd6a31c12458768da5ce87cca6aad63ebbaaef7432");
421        let keypair = Keypair::from_raw_ed25519_seed(seed.as_bytes()).unwrap();
422
423        assert_eq!(keypair.public_key(), expected_public_key);
424        assert_eq!(keypair.secret_key().unwrap().as_str(), expected_secret);
425        assert_eq!(keypair.raw_public_key().as_slice(), expected_raw_public_key);
426    }
427
428    #[test]
429    fn test_create_keypair_invalid_raw_ed25519_seed() {
430        Keypair::from_raw_ed25519_seed(b"masterpassphrasemasterpassphras").is_err();
431        Keypair::from_raw_ed25519_seed(b"masterpassphrasemasterpassphrase1").is_err();
432        Keypair::from_raw_ed25519_seed(b"").is_err();
433        Keypair::from_raw_ed25519_seed(b"\0").is_err();
434    }
435
436    #[test]
437    fn test_create_keypair_from_public_key() {
438        let public_key = "GAXDYNIBA5E4DXR5TJN522RRYESFQ5UNUXHIPTFGVLLD5O5K552DF5ZH";
439        let expected_public_key = "GAXDYNIBA5E4DXR5TJN522RRYESFQ5UNUXHIPTFGVLLD5O5K552DF5ZH";
440        let expected_raw_public_key =
441            hex!("2e3c35010749c1de3d9a5bdd6a31c12458768da5ce87cca6aad63ebbaaef7432");
442
443        let keypair = Keypair::from_public_key(public_key).unwrap();
444
445        assert_eq!(keypair.public_key().as_str(), expected_public_key);
446        assert_eq!(keypair.raw_public_key().as_slice(), expected_raw_public_key);
447    }
448
449    #[test]
450    fn test_create_keypair_from_invalid_public_key() {
451        let invalid_public_keys = [
452            "hel0",
453            "masterpassphrasemasterpassphrase",
454            "sfyjodTxbwLtRToZvi6yQ1KnpZriwTJ7n6nrASFR6goRviCU3Ff",
455        ];
456
457        Keypair::from_public_key(invalid_public_keys[0]).is_err();
458        Keypair::from_public_key(invalid_public_keys[1]).is_err();
459        Keypair::from_public_key(invalid_public_keys[2]).is_err();
460    }
461
462    #[test]
463    fn test_create_random_keypair() {
464        let keypair = Keypair::random().unwrap();
465    }
466
467    #[test]
468    fn test_xdr_muxed_account_with_ed25519_key_type() {
469        let public_key = "GAXDYNIBA5E4DXR5TJN522RRYESFQ5UNUXHIPTFGVLLD5O5K552DF5ZH";
470        let keypair = Keypair::from_public_key(public_key).unwrap();
471        let muxed = keypair.xdr_muxed_account_id("1");
472    }
473
474    #[test]
475    fn test_sign_decorated() {
476        let the_secret = "SD7X7LEHBNMUIKQGKPARG5TDJNBHKC346OUARHGZL5ITC6IJPXHILY36";
477        let kp = Keypair::from_secret(the_secret).unwrap();
478        let message = "test post please ignore".as_bytes();
479        let sign: xdr::DecoratedSignature = kp.sign_decorated(message);
480        assert_eq!(sign.hint.0.to_vec(), vec![0x0B, 0xFA, 0xD1, 0x34]);
481    }
482}