neo3/neo_crypto/key_pair.rs
1//! # KeyPair
2//!
3//! `KeyPair` is a module that provides an implementation for Elliptic Curve Key Pairs using the `p256` crate.
4//!
5//! This structure can be used to manage and manipulate EC key pairs,
6//! including generating new pairs, importing them from raw bytes,
7//! and converting them to various formats.
8
9use rand::rngs::OsRng;
10
11use crate::{
12 builder::VerificationScript,
13 crypto::{
14 private_key_from_wif, wif_from_private_key, CryptoError, PublicKeyExtension,
15 Secp256r1PrivateKey, Secp256r1PublicKey,
16 },
17 neo_types::{ScriptHash, ScriptHashExtension},
18};
19
20/// Represents an Elliptic Curve Key Pair containing both a private and a public key.
21#[derive(Debug, Clone)]
22pub struct KeyPair {
23 /// The private key component of the key pair.
24 pub private_key: Secp256r1PrivateKey,
25
26 /// The public key component of the key pair.
27 pub public_key: Secp256r1PublicKey,
28}
29
30impl KeyPair {
31 /// Creates a new `KeyPair` instance given a private key and its corresponding public key.
32 ///
33 /// # Arguments
34 ///
35 /// * `private_key` - A `Secp256r1PrivateKey` representing the private key.
36 /// * `public_key` - A `Secp256r1PublicKey` representing the public key.
37 pub fn new(private_key: Secp256r1PrivateKey, public_key: Secp256r1PublicKey) -> Self {
38 Self { private_key, public_key }
39 }
40
41 pub fn private_key(&self) -> Secp256r1PrivateKey {
42 self.private_key.clone()
43 }
44
45 pub fn public_key(&self) -> Secp256r1PublicKey {
46 self.public_key.clone()
47 }
48
49 /// Derives a new `KeyPair` instance from just a private key.
50 /// The public key is derived from the given private key.
51 ///
52 /// # Arguments
53 ///
54 /// * `private_key` - A `Secp256r1PrivateKey` representing the private key.
55 pub fn from_secret_key(private_key: &Secp256r1PrivateKey) -> Self {
56 let public_key = private_key.clone().to_public_key();
57 Self::new(private_key.clone(), public_key)
58 }
59
60 /// Returns the 32-byte representation of the private key.
61 pub fn private_key_bytes(&self) -> [u8; 32] {
62 self.private_key.to_raw_bytes()
63 }
64
65 /// Returns the 64-byte uncompressed representation of the public key.
66 pub fn public_key_bytes(&self) -> [u8; 64] {
67 let mut buf = [0u8; 64];
68 // Convert the Secp256r1PublicKey to its byte representation
69 let vec_bytes: Vec<u8> = self.public_key.to_vec(); // uncompressed form
70 buf.copy_from_slice(&vec_bytes[0..64]);
71
72 buf
73 }
74}
75
76impl KeyPair {
77 /// Generates a new random `KeyPair`.
78 pub fn new_random() -> Self {
79 let mut rng = OsRng; // A cryptographically secure random number generator
80 let secret_key = Secp256r1PrivateKey::random(&mut rng);
81 Self::from_secret_key(&secret_key)
82 }
83
84 /// Creates an `KeyPair` from a given 32-byte private key.
85 ///
86 /// # Arguments
87 ///
88 /// * `private_key` - A 32-byte slice representing the private key.
89 pub fn from_private_key(private_key: &[u8; 32]) -> Result<Self, CryptoError> {
90 let secret_key = Secp256r1PrivateKey::from_bytes(private_key)?;
91 Ok(Self::from_secret_key(&secret_key))
92 }
93
94 /// Creates an `KeyPair` from a given Wallet Import Format (WIF) string.
95 /// This will use the private key encoded in the WIF to generate the key pair.
96 ///
97 /// # Arguments
98 ///
99 /// * `wif` - A Wallet Import Format (WIF) string.
100 ///
101 /// The WIF string should be in the format `Kx...` or `Lx...`.
102 /// The key pair will be generated from the private key encoded in the WIF.
103 /// The public key will be derived from the private key.
104 pub fn from_wif(wif: &str) -> Result<Self, CryptoError> {
105 let private_key = private_key_from_wif(wif)?;
106 Ok(Self::from_secret_key(&private_key))
107 }
108
109 /// Creates an `KeyPair` from a given 65-byte public key.
110 /// This will use a dummy private key internally.
111 ///
112 /// # Arguments
113 ///
114 /// * `public_key` - A 65-byte slice representing the uncompressed public key.
115 pub fn from_public_key(public_key: &[u8; 64]) -> Result<Self, CryptoError> {
116 let public_key = Secp256r1PublicKey::from_slice(public_key)?;
117 let secret_key = Secp256r1PrivateKey::from_bytes(&[0u8; 32]).unwrap(); // dummy private key
118 Ok(Self::new(secret_key, public_key))
119 }
120
121 /// Exports the key pair as a Wallet Import Format (WIF) string
122 ///
123 /// Returns: The WIF encoding of this key pair
124 pub fn export_as_wif(&self) -> String {
125 wif_from_private_key(&self.private_key())
126 }
127
128 pub fn get_script_hash(&self) -> ScriptHash {
129 let vs = VerificationScript::from_public_key(&self.public_key());
130 vs.hash()
131 }
132
133 pub fn get_address(&self) -> String {
134 self.get_script_hash().to_address()
135 }
136}
137
138impl PartialEq for KeyPair {
139 fn eq(&self, other: &Self) -> bool {
140 self.private_key == other.private_key && self.public_key == other.public_key
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use crate::{config::TestConstants, crypto::KeyPair, ScriptHash, ScriptHashExtension};
147 use hex;
148
149 #[test]
150 fn test_public_key_wif() {
151 let private_key =
152 hex::decode("c7134d6fd8e73d819e82755c64c93788d8db0961929e025a53363c4cc02a6962")
153 .unwrap();
154 let private_key_arr: &[u8; 32] = private_key.as_slice().try_into().unwrap();
155 let key_pair = KeyPair::from_private_key(private_key_arr).unwrap();
156 assert_eq!(
157 key_pair.export_as_wif(),
158 "L3tgppXLgdaeqSGSFw1Go3skBiy8vQAM7YMXvTHsKQtE16PBncSU"
159 );
160 }
161
162 #[test]
163 fn test_address() {
164 let private_key = hex::decode(TestConstants::DEFAULT_ACCOUNT_PRIVATE_KEY).unwrap();
165 let private_key_arr: &[u8; 32] = private_key.as_slice().try_into().unwrap();
166 let key_pair = KeyPair::from_private_key(private_key_arr).unwrap();
167 assert_eq!(key_pair.get_address(), TestConstants::DEFAULT_ACCOUNT_ADDRESS);
168 }
169
170 #[test]
171 fn test_script_hash() {
172 let private_key = hex::decode(TestConstants::DEFAULT_ACCOUNT_PRIVATE_KEY).unwrap();
173 let private_key_arr: &[u8; 32] = private_key.as_slice().try_into().unwrap();
174 let key_pair = KeyPair::from_private_key(private_key_arr).unwrap();
175 assert_eq!(
176 key_pair.get_script_hash(),
177 ScriptHash::from_hex(TestConstants::DEFAULT_ACCOUNT_SCRIPT_HASH).unwrap()
178 );
179 }
180
181 // #[test]
182 // pub fn setup_new_ec_public_key_and_get_encoded_and_get_ec_point() {
183 // let expected_x = hex!("b4af8d061b6b320cce6c63bc4ec7894dce107bfc5f5ef5c68a93b4ad1e136816");
184 // let expected_y = hex!("5f4f7fb1c5862465543c06dd5a2aa414f6583f92a5cc3e1d4259df79bf6839c9");
185
186 // let expected_ec_point = EncodedPoint::from_affine_coordinates(
187 // &expected_x.into(),
188 // &expected_y.into(),
189 // false
190 // );
191
192 // let enc_ec_point = "03b4af8d061b6b320cce6c63bc4ec7894dce107bfc5f5ef5c68a93b4ad1e136816";
193 // let enc_ec_point_bytes = hex::decode(enc_ec_point).unwrap();
194
195 // let pub_key = Secp256r1PublicKey::from_encoded(&enc_ec_point).unwrap();
196
197 // assert_eq!(pub_key.get_encoded_point(false), expected_ec_point);
198 // assert_eq!(pub_key.get_encoded(true), enc_ec_point_bytes);
199 // assert_eq!(pub_key.get_encoded_compressed_hex(), enc_ec_point);
200 // }
201
202 // #[test]
203 // pub fn create_ec_public_key_from_uncompressed_ec_point() {
204 // let ec_point = "04b4af8d061b6b320cce6c63bc4ec7894dce107bfc5f5ef5c68a93b4ad1e1368165f4f7fb1c5862465543c06dd5a2aa414f6583f92a5cc3e1d4259df79bf6839c9";
205
206 // let pub_key = Secp256r1PublicKey::from_encoded(&ec_point).unwrap();
207
208 // assert_eq!(
209 // pub_key.get_encoded_compressed_hex(),
210 // "03b4af8d061b6b320cce6c63bc4ec7894dce107bfc5f5ef5c68a93b4ad1e136816"
211 // );
212 // }
213
214 // #[test]
215 // pub fn invalid_size() {
216 // ///
217 // /// Need futher adjustments to deal with specifc error messages in PublicKey
218 // ///
219 // let pub_key_hex = "03b4af8d061b6b320cce6c63bc4ec7894dce107bfc5f5ef5c68a93b4ad1e1368"; //only 32 bits
220
221 // let pub_key = Secp256r1PublicKey::from_encoded(&pub_key_hex);
222
223 // assert_eq!(
224 // pub_key,
225 // None
226 // );
227 // }
228
229 // #[test]
230 // pub fn clean_hex_prefix() {
231 // ///
232 // /// Need futher adjustments to deal with specifc error messages in PublicKey
233 // ///
234 // let pub_key_hex = "0x03b4af8d061b6b320cce6c63bc4ec7894dce107bfc5f5ef5c68a93b4ad1e136816";
235 // let expected = "03b4af8d061b6b320cce6c63bc4ec7894dce107bfc5f5ef5c68a93b4ad1e136816";
236
237 // let pub_key = Secp256r1PublicKey::from_encoded(&pub_key_hex).unwrap();
238
239 // assert_eq!(
240 // pub_key.get_encoded_compressed_hex(),
241 // expected
242 // );
243 // }
244
245 // #[test]
246 // pub fn serialize_public_key() {
247 // ///
248 // /// Need futher adjustments to deal with specifc error messages in PublicKey
249 // ///
250 // let enc_point = "03b4af8d061b6b320cce6c63bc4ec7894dce107bfc5f5ef5c68a93b4ad1e136816";
251 // let pub_key = Secp256r1PublicKey::from_encoded(&enc_point).unwrap();
252
253 // assert_eq!(
254 // pub_key.to_array(),
255 // enc_point.from_hex().unwrap()
256 // );
257 // }
258}