waves_rust/util/
crypto.rs

1use crate::constants::{ADDRESS_LENGTH, ADDRESS_VERSION, SIGNATURE_LENGTH};
2use crate::error::Result;
3use crate::util::{Bytes, Hash, WORDS};
4use curve25519_dalek::constants;
5use curve25519_dalek::scalar::Scalar;
6use rand::Rng;
7use sha2::digest::Update;
8use sha2::Sha512;
9
10pub struct Crypto;
11
12impl Crypto {
13    pub fn get_random_seed_phrase(words_count: u8) -> String {
14        let mut rng = rand::thread_rng();
15        let results = rand::seq::index::sample(&mut rng, 2048, words_count as usize).into_vec();
16        let mut seed_phrase_array: Vec<&str> = vec![];
17        for result in results {
18            seed_phrase_array.push(WORDS[result])
19        }
20        seed_phrase_array.join(" ")
21    }
22
23    pub fn get_account_seed(seed_phrase: &[u8], nonce: u8) -> Result<Vec<u8>> {
24        Hash::secure_hash(&Bytes::concat(vec![
25            Bytes::from_nonce(nonce),
26            seed_phrase.to_vec(),
27        ]))
28    }
29
30    pub fn get_private_key(account_seed: &Vec<u8>) -> Result<[u8; 32]> {
31        let mut private_key = [0u8; 32];
32        let hashed_account_seed = Hash::sha256(account_seed);
33        private_key.copy_from_slice(&hashed_account_seed);
34        private_key[0] &= 248;
35        private_key[31] &= 127;
36        private_key[31] |= 64;
37
38        Ok(private_key)
39    }
40
41    pub fn get_public_key(private_key: &[u8; 32]) -> Vec<u8> {
42        let mut pk = [0u8; 32];
43        pk.copy_from_slice(private_key);
44        let ed_pk = &Scalar::from_bits(pk) * &constants::ED25519_BASEPOINT_TABLE;
45        ed_pk.to_montgomery().to_bytes().to_vec()
46    }
47
48    pub fn get_public_key_hash(public_key: &[u8]) -> Result<Vec<u8>> {
49        let hash = Hash::secure_hash(public_key)?;
50        Ok(hash[0..20].to_vec())
51    }
52
53    pub fn get_address(chain_id: &u8, public_key_hash: &[u8]) -> Result<Vec<u8>> {
54        let mut buf = [0u8; ADDRESS_LENGTH];
55        buf[0] = ADDRESS_VERSION;
56        buf[1] = *chain_id;
57        buf[2..22].copy_from_slice(public_key_hash);
58        let checksum = &Hash::secure_hash(&buf[..22])?[..4];
59        buf[22..].copy_from_slice(checksum);
60        Ok(buf.to_vec())
61    }
62
63    pub fn sign(private_key: &[u8; 32], message: &[u8]) -> Vec<u8> {
64        let mut hash = Sha512::default();
65
66        hash.update(&INITBUF);
67
68        hash.update(private_key);
69        hash.update(message);
70
71        let mut rand = rand::thread_rng();
72        let mut rndbuf: Vec<u8> = vec![0; 64];
73        (0..63).for_each(|i| rndbuf[i] = rand.gen::<u8>());
74
75        hash.update(&rndbuf);
76
77        let rsc = Scalar::from_hash(hash);
78        let r = (&rsc * &constants::ED25519_BASEPOINT_TABLE)
79            .compress()
80            .to_bytes();
81
82        let ed_public_key = constants::ED25519_BASEPOINT_POINT * Scalar::from_bits(*private_key);
83        let public_key = ed_public_key.compress().to_bytes();
84
85        hash = Sha512::default();
86        hash.update(&r);
87        hash.update(&public_key);
88        hash.update(message);
89        let s = (Scalar::from_hash(hash) * Scalar::from_bits(*private_key)) + rsc;
90
91        let sign = public_key[31] & 0x80;
92        let mut result = [0; SIGNATURE_LENGTH];
93        result[..32].copy_from_slice(&r);
94        result[32..].copy_from_slice(&s.to_bytes());
95        result[63] &= 0x7F;
96        result[63] |= sign;
97        result.to_vec()
98    }
99}
100
101#[cfg(test)]
102mod tests {
103
104    use crate::error::Result;
105    use crate::model::ChainId;
106    use crate::util::{Base58, Crypto};
107
108    #[test]
109    fn test_get_private_key() {
110        let seed_phrase = "blame vacant regret company chase trip grant funny brisk innocent"
111            .as_bytes()
112            .to_vec();
113        let expected_private_key = "3j2aMHzh9azPphzuW7aF3cmUefGEQC9dcWYXYCyoPcJg";
114        let account_seed =
115            Crypto::get_account_seed(&seed_phrase, 0).expect("failed to get account seed");
116        let private_key =
117            Crypto::get_private_key(&account_seed).expect("failed to get private key");
118        let encoded_private_key = Base58::encode(&private_key.to_vec(), false);
119        assert_eq!(encoded_private_key, expected_private_key)
120    }
121
122    #[test]
123    fn test_get_public_key() {
124        let seed_phrase = "blame vacant regret company chase trip grant funny brisk innocent";
125
126        let expected_public_key_from_nonce_0 = "8cj6YzvQPhSHGvnjupNTW8zrADTT8CMAAd2xTuej84gB";
127        let expected_public_key_from_nonce_128 = "DTvCW1nzFr7mHrHkGf1apstRfwPp4yYL19YvjjLEAPBh";
128        let expected_public_key_from_nonce_255 = "esjbpqVWSg8iCaPYQA3SoxZo3oUkdRJSi9tKLoqKQoC";
129        assert_eq!(
130            Crypto::get_public_key(
131                &private_key(seed_phrase, 0).expect("failed to get private key")
132            ),
133            Base58::decode(expected_public_key_from_nonce_0).expect("Failed to decode str")
134        );
135        assert_eq!(
136            Crypto::get_public_key(
137                &private_key(seed_phrase, 128).expect("failed to get private key")
138            ),
139            Base58::decode(expected_public_key_from_nonce_128).expect("Failed to decode str")
140        );
141        assert_eq!(
142            Crypto::get_public_key(
143                &private_key(seed_phrase, 255).expect("failed to get private key")
144            ),
145            Base58::decode(expected_public_key_from_nonce_255).expect("Failed to decode str")
146        );
147    }
148
149    #[test]
150    fn test_get_address() {
151        let seed_phrase = "blame vacant regret company chase trip grant funny brisk innocent";
152
153        let expected_address = "3Ms87NGAAaPWZux233TB9A3TXps4LDkyJWN";
154
155        let public_key = Crypto::get_public_key(
156            &private_key(seed_phrase, 0).expect("failed to get private key"),
157        );
158        let public_key_hash =
159            Crypto::get_public_key_hash(&public_key).expect("failed to get public key hash");
160
161        let address = Crypto::get_address(&ChainId::TESTNET.byte(), &public_key_hash)
162            .expect("failed to get address");
163        let encoded_address = Base58::encode(&address, false);
164
165        assert_eq!(encoded_address, expected_address)
166    }
167
168    #[test]
169    fn test_get_random_seed_phrase() {
170        let rng_seed_phrase = Crypto::get_random_seed_phrase(12);
171        assert_eq!(12, rng_seed_phrase.split(' ').into_iter().count())
172    }
173
174    fn private_key(seed_phrase: &str, nonce: u8) -> Result<[u8; 32]> {
175        let account_seed = Crypto::get_account_seed(seed_phrase.as_bytes(), nonce)
176            .expect("failed to get account seed");
177        Crypto::get_private_key(&account_seed)
178    }
179}
180
181static INITBUF: [u8; 32] = [
182    0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
183    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
184];