waves_rust/model/account/
private_key.rs

1use crate::constants::SIGNATURE_LENGTH;
2use crate::error::{Error, Result};
3use crate::model::account::PublicKey;
4use crate::model::ByteString;
5use crate::util::{Base58, Crypto};
6use curve25519_dalek::montgomery::MontgomeryPoint;
7use ed25519_dalek::{PublicKey as EdPublicKey, Signature, Verifier};
8
9#[derive(Clone, Eq, PartialEq, Hash)]
10pub struct PrivateKey {
11    bytes: [u8; 32],
12    public_key: PublicKey,
13}
14
15impl std::str::FromStr for PrivateKey {
16    type Err = Error;
17
18    fn from_str(base58string: &str) -> Result<PrivateKey> {
19        let bytes = Base58::decode(base58string)?;
20        let bytes_array: [u8; 32] = bytes
21            .try_into()
22            .map_err(Error::VectorToArrayConversionError)?;
23        PrivateKey::from_bytes(bytes_array)
24    }
25}
26
27impl PrivateKey {
28    pub fn from_seed(seed_phrase: &str, nonce: u8) -> Result<Self> {
29        let hash_seed = Crypto::get_account_seed(seed_phrase.as_bytes(), nonce)?;
30        let private_key = Crypto::get_private_key(&hash_seed)?;
31        let public_key = PublicKey::from_bytes(&Crypto::get_public_key(&private_key))?;
32        Ok(Self {
33            bytes: private_key,
34            public_key,
35        })
36    }
37
38    pub fn from_bytes(bytes: [u8; 32]) -> Result<Self> {
39        let public_key = PublicKey::from_bytes(&Crypto::get_public_key(&bytes))?;
40        Ok(Self { bytes, public_key })
41    }
42
43    pub fn encoded(&self) -> String {
44        Base58::encode(&self.bytes.to_vec(), false)
45    }
46
47    pub fn bytes(&self) -> [u8; 32] {
48        self.bytes
49    }
50
51    pub fn public_key(&self) -> PublicKey {
52        self.public_key.clone()
53    }
54
55    pub fn sign(&self, message: &[u8]) -> Result<Vec<u8>> {
56        Ok(Crypto::sign(&self.bytes, message))
57    }
58
59    pub fn is_signature_valid(&self, message: &[u8], signature: &[u8]) -> Result<bool> {
60        let sig_arr = <[u8; SIGNATURE_LENGTH]>::try_from(signature.to_owned()).map_err(|_| {
61            Error::InvalidBytesLength {
62                expected_len: SIGNATURE_LENGTH,
63                actual_len: signature.len(),
64            }
65        })?;
66        let sign = sig_arr[63] & 0x80;
67        let mut sig = [0u8; SIGNATURE_LENGTH];
68        sig.copy_from_slice(signature);
69        sig[63] &= 0x7f;
70
71        let public_key_bytes = self.public_key.bytes();
72        let mut ed_public_key =
73            MontgomeryPoint(<[u8; 32]>::try_from(public_key_bytes.clone()).map_err(|_| {
74                Error::InvalidBytesLength {
75                    expected_len: 32,
76                    actual_len: public_key_bytes.len(),
77                }
78            })?)
79            .to_edwards(sign)
80            .ok_or(Error::MontgomeryPointConversionError)?
81            .compress()
82            .to_bytes();
83        ed_public_key[31] &= 0x7F;
84        ed_public_key[31] |= sign;
85
86        Ok(EdPublicKey::from_bytes(&ed_public_key)?
87            .verify(message, &Signature::from_bytes(&sig)?)
88            .is_ok())
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use std::str::FromStr;
95
96    use crate::error::Error::InvalidBytesLength;
97    use crate::error::Result;
98    use crate::model::account::PrivateKey;
99    use crate::model::ByteString;
100
101    #[test]
102    fn test_private_key_from_seed() -> Result<()> {
103        let seed_phrase = "blame vacant regret company chase trip grant funny brisk innocent";
104
105        let expected_private_key_with_nonce_0 = "3j2aMHzh9azPphzuW7aF3cmUefGEQC9dcWYXYCyoPcJg";
106        let expected_private_key_with_nonce_128 = "HCK7dUsScMH9mTCoyaV7bVhkTxwsyCHdbMBfb9TpVhPd";
107        let expected_private_key_with_nonce_255 = "5Kdsn9jH3ifWSrZ19NYqnaCN9GmaPmNpZYnuSAEE4Yga";
108
109        let expected_public_key_from_nonce_0 = "8cj6YzvQPhSHGvnjupNTW8zrADTT8CMAAd2xTuej84gB";
110        let expected_public_key_from_nonce_128 = "DTvCW1nzFr7mHrHkGf1apstRfwPp4yYL19YvjjLEAPBh";
111        let expected_public_key_from_nonce_255 = "esjbpqVWSg8iCaPYQA3SoxZo3oUkdRJSi9tKLoqKQoC";
112
113        assert_eq!(
114            PrivateKey::from_seed(seed_phrase, 0)?.encoded(),
115            expected_private_key_with_nonce_0
116        );
117        assert_eq!(
118            PrivateKey::from_seed(seed_phrase, 128)?.encoded(),
119            expected_private_key_with_nonce_128
120        );
121        assert_eq!(
122            PrivateKey::from_seed(seed_phrase, 255)?.encoded(),
123            expected_private_key_with_nonce_255
124        );
125
126        assert_eq!(
127            PrivateKey::from_seed(seed_phrase, 0)?
128                .public_key()
129                .encoded(),
130            expected_public_key_from_nonce_0
131        );
132        assert_eq!(
133            PrivateKey::from_seed(seed_phrase, 128)?
134                .public_key()
135                .encoded(),
136            expected_public_key_from_nonce_128
137        );
138        assert_eq!(
139            PrivateKey::from_seed(seed_phrase, 255)?
140                .public_key()
141                .encoded(),
142            expected_public_key_from_nonce_255
143        );
144        Ok(())
145    }
146
147    #[test]
148    fn test_invalid_signature_size() -> Result<()> {
149        let private_key = PrivateKey::from_seed("a", 0)?;
150        let result = private_key.is_signature_valid(&[], &[0_u8; 32]);
151        match result {
152            Ok(_) => panic!("expected error"),
153            Err(err) => match err {
154                InvalidBytesLength { .. } => Ok(()),
155                _ => panic!("expected error"),
156            },
157        }
158    }
159
160    #[test]
161    fn test_private_key_from_bytes() -> Result<()> {
162        let private_key = PrivateKey::from_bytes([0; 32])?;
163        assert_eq!(private_key.bytes(), [0_u8; 32]);
164        Ok(())
165    }
166
167    #[test]
168    fn test_private_key_std_from_str() -> Result<()> {
169        let seed_phrase = "blame vacant regret company chase trip grant funny brisk innocent";
170        let private_key_from_seed = PrivateKey::from_seed(seed_phrase, 0)?;
171        let private_key_from_str = PrivateKey::from_str(&private_key_from_seed.encoded())?;
172        assert_eq!(private_key_from_seed.bytes(), private_key_from_str.bytes());
173        Ok(())
174    }
175}