waves_rust/util/
crypto.rs1use 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];