bsv_primitives/ec/
private_key.rs1use k256::ecdsa::SigningKey;
7use k256::elliptic_curve::sec1::ToEncodedPoint;
8use k256::elliptic_curve::ScalarPrimitive;
9use k256::{Scalar, Secp256k1};
10use rand::rngs::OsRng;
11
12use crate::ec::public_key::PublicKey;
13use crate::ec::signature::Signature;
14use crate::hash::{sha256_hmac, sha256d};
15use crate::PrimitivesError;
16
17#[derive(Clone, Debug)]
22pub struct PrivateKey {
23 inner: SigningKey,
25}
26
27const PRIVATE_KEY_BYTES_LEN: usize = 32;
29
30const MAINNET_PREFIX: u8 = 0x80;
32
33const COMPRESS_MAGIC: u8 = 0x01;
35
36impl PrivateKey {
37 pub fn new() -> Self {
42 let signing_key = SigningKey::random(&mut OsRng);
43 PrivateKey { inner: signing_key }
44 }
45
46 pub fn from_bytes(bytes: &[u8]) -> Result<Self, PrimitivesError> {
55 if bytes.len() != PRIVATE_KEY_BYTES_LEN {
56 return Err(PrimitivesError::InvalidPrivateKey(format!(
57 "expected {} bytes, got {}",
58 PRIVATE_KEY_BYTES_LEN,
59 bytes.len()
60 )));
61 }
62 let signing_key = SigningKey::from_bytes(bytes.into())?;
63 Ok(PrivateKey { inner: signing_key })
64 }
65
66 pub fn from_hex(hex_str: &str) -> Result<Self, PrimitivesError> {
74 if hex_str.is_empty() {
75 return Err(PrimitivesError::InvalidPrivateKey(
76 "private key hex is empty".to_string(),
77 ));
78 }
79 let bytes = hex::decode(hex_str)?;
80 Self::from_bytes(&bytes)
81 }
82
83 pub fn from_wif(wif: &str) -> Result<Self, PrimitivesError> {
94 let decoded = bs58::decode(wif)
95 .into_vec()
96 .map_err(|e| PrimitivesError::InvalidWif(e.to_string()))?;
97 let decoded_len = decoded.len();
98
99 let is_compressed = match decoded_len {
103 38 => {
104 if decoded[33] != COMPRESS_MAGIC {
105 return Err(PrimitivesError::InvalidWif(
106 "malformed private key: invalid compression flag".to_string(),
107 ));
108 }
109 true
110 }
111 37 => false,
112 _ => {
113 return Err(PrimitivesError::InvalidWif(format!(
114 "malformed private key: invalid length {}",
115 decoded_len
116 )));
117 }
118 };
119
120 let payload_end = if is_compressed {
122 1 + PRIVATE_KEY_BYTES_LEN + 1
123 } else {
124 1 + PRIVATE_KEY_BYTES_LEN
125 };
126 let checksum = sha256d(&decoded[..payload_end]);
127 if checksum[..4] != decoded[decoded_len - 4..] {
128 return Err(PrimitivesError::ChecksumMismatch);
129 }
130
131 let key_bytes = &decoded[1..1 + PRIVATE_KEY_BYTES_LEN];
132 Self::from_bytes(key_bytes)
133 }
134
135 pub fn to_wif(&self) -> String {
142 self.to_wif_prefix(MAINNET_PREFIX)
143 }
144
145 pub fn to_wif_prefix(&self, prefix: u8) -> String {
155 let key_bytes = self.to_bytes();
157 let mut payload = Vec::with_capacity(1 + PRIVATE_KEY_BYTES_LEN + 1 + 4);
158 payload.push(prefix);
159 payload.extend_from_slice(&key_bytes);
160 payload.push(COMPRESS_MAGIC); let checksum = sha256d(&payload);
163 payload.extend_from_slice(&checksum[..4]);
164
165 bs58::encode(payload).into_string()
166 }
167
168 pub fn to_bytes(&self) -> [u8; 32] {
173 let mut out = [0u8; 32];
174 out.copy_from_slice(&self.inner.to_bytes());
175 out
176 }
177
178 pub fn to_hex(&self) -> String {
183 hex::encode(self.to_bytes())
184 }
185
186 pub fn pub_key(&self) -> PublicKey {
191 let verifying_key = self.inner.verifying_key();
192 PublicKey::from_k256_verifying_key(verifying_key)
193 }
194
195 pub fn sign(&self, hash: &[u8]) -> Result<Signature, PrimitivesError> {
206 Signature::sign(hash, self)
207 }
208
209 pub fn derive_shared_secret(&self, pub_key: &PublicKey) -> Result<PublicKey, PrimitivesError> {
221 let their_point = pub_key.to_projective_point()?;
222 let scalar = self.to_scalar();
223 let shared_point = their_point * scalar;
224
225 let affine = shared_point.to_affine();
226 let encoded = affine.to_encoded_point(true);
227 PublicKey::from_bytes(encoded.as_bytes())
228 }
229
230 pub fn derive_child(
244 &self,
245 pub_key: &PublicKey,
246 invoice_number: &str,
247 ) -> Result<PrivateKey, PrimitivesError> {
248 let shared_secret = self.derive_shared_secret(pub_key)?;
249 let shared_compressed = shared_secret.to_compressed();
250
251 let hmac_result = sha256_hmac(&shared_compressed, invoice_number.as_bytes());
254
255 let current_scalar = self.to_scalar();
257 let hmac_scalar = scalar_from_bytes(&hmac_result)?;
258 let new_scalar = current_scalar + hmac_scalar;
259
260 let scalar_primitive: ScalarPrimitive<Secp256k1> = new_scalar.into();
262 let bytes = scalar_primitive.to_bytes();
263 PrivateKey::from_bytes(&bytes)
264 }
265
266 pub(crate) fn signing_key(&self) -> &SigningKey {
271 &self.inner
272 }
273
274 pub(crate) fn to_scalar(&self) -> Scalar {
279 *self.inner.as_nonzero_scalar().as_ref()
280 }
281}
282
283impl Default for PrivateKey {
284 fn default() -> Self {
285 Self::new()
286 }
287}
288
289impl PartialEq for PrivateKey {
295 fn eq(&self, other: &Self) -> bool {
296 self.to_bytes() == other.to_bytes()
297 }
298}
299
300impl Eq for PrivateKey {}
301
302fn scalar_from_bytes(bytes: &[u8; 32]) -> Result<Scalar, PrimitivesError> {
310 use k256::elliptic_curve::ops::Reduce;
311 let uint = k256::U256::from_be_slice(bytes);
312 Ok(<Scalar as Reduce<k256::U256>>::reduce(uint))
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
321 fn test_priv_keys() {
322 let key_bytes: [u8; 32] = [
323 0xea, 0xf0, 0x2c, 0xa3, 0x48, 0xc5, 0x24, 0xe6, 0x39, 0x26, 0x55, 0xba, 0x4d, 0x29,
324 0x60, 0x3c, 0xd1, 0xa7, 0x34, 0x7d, 0x9d, 0x65, 0xcf, 0xe9, 0x3c, 0xe1, 0xeb, 0xff,
325 0xdc, 0xa2, 0x26, 0x94,
326 ];
327
328 let priv_key = PrivateKey::from_bytes(&key_bytes).unwrap();
329 let pub_key = priv_key.pub_key();
330
331 let uncompressed = pub_key.to_uncompressed();
333 let _parsed = PublicKey::from_bytes(&uncompressed).unwrap();
334
335 let hash: [u8; 10] = [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9];
337 let sig = priv_key.sign(&hash).unwrap();
338 assert!(pub_key.verify(&hash, &sig));
339
340 let serialized = priv_key.to_bytes();
342 assert_eq!(serialized, key_bytes);
343 }
344
345 #[test]
347 fn test_private_key_serialization_and_deserialization() {
348 let pk = PrivateKey::new();
349
350 let serialized = pk.to_bytes();
352 let deserialized = PrivateKey::from_bytes(&serialized).unwrap();
353 assert_eq!(pk, deserialized);
354
355 let hex_str = pk.to_hex();
357 let deserialized = PrivateKey::from_hex(&hex_str).unwrap();
358 assert_eq!(pk, deserialized);
359
360 let wif = pk.to_wif();
362 let deserialized = PrivateKey::from_wif(&wif).unwrap();
363 assert_eq!(pk, deserialized);
364 }
365
366 #[test]
368 fn test_private_key_from_invalid_hex() {
369 assert!(PrivateKey::from_hex("").is_err());
370
371 let wif = "L4o1GXuUSHauk19f9Cfpm1qfSXZuGLBUAC2VZM6vdmfMxRxAYkWq";
373 assert!(PrivateKey::from_hex(wif).is_err());
374 }
375
376 #[test]
378 fn test_private_key_from_invalid_wif() {
379 assert!(
381 PrivateKey::from_wif("L401GXuUSHauk19f9Cfpm1qfSXZuGLBUAC2VZM6vdmfMxRxAYkWq").is_err()
382 );
383 assert!(
385 PrivateKey::from_wif("L4o1GXuUSHauk19f9Cfpm1qfSXZuGLBUAC2VZM6vdmfMxRxAYkW").is_err()
386 );
387 assert!(PrivateKey::from_wif(
389 "L4o1GXuUSHauk19f9Cfpm1qfSXZuGLBUAC2VZM6vdmfMxRxAYkWqL4o1GXuUSHauk19f9Cfpm1qfSXZuGLBUAC2VZM6vdmfMxRxAYkWq"
390 ).is_err());
391 }
392
393 #[test]
395 fn test_brc42_private_vectors() {
396 let vectors_json = include_str!("testdata/BRC42.private.vectors.json");
397 let vectors: Vec<serde_json::Value> = serde_json::from_str(vectors_json).unwrap();
398
399 for (i, v) in vectors.iter().enumerate() {
400 let sender_pub_hex = v["senderPublicKey"].as_str().unwrap();
401 let recipient_priv_hex = v["recipientPrivateKey"].as_str().unwrap();
402 let invoice_number = v["invoiceNumber"].as_str().unwrap();
403 let expected_priv_hex = v["privateKey"].as_str().unwrap();
404
405 let public_key = PublicKey::from_hex(sender_pub_hex)
406 .unwrap_or_else(|e| panic!("vector #{}: parse pub key: {}", i + 1, e));
407 let private_key = PrivateKey::from_hex(recipient_priv_hex)
408 .unwrap_or_else(|e| panic!("vector #{}: parse priv key: {}", i + 1, e));
409
410 let derived = private_key
411 .derive_child(&public_key, invoice_number)
412 .unwrap_or_else(|e| panic!("vector #{}: derive child: {}", i + 1, e));
413
414 let derived_hex = derived.to_hex();
415 assert_eq!(
416 derived_hex,
417 expected_priv_hex,
418 "BRC42 private vector #{}: derived key mismatch",
419 i + 1
420 );
421 }
422 }
423}