waves_rust/model/account/
private_key.rs1use 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}