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}