bsv/primitives/
public_key.rs1use crate::primitives::base_point::BasePoint;
8use crate::primitives::big_number::{BigNumber, Endian};
9use crate::primitives::ecdsa::ecdsa_verify;
10use crate::primitives::error::PrimitivesError;
11use crate::primitives::hash::{hash160, sha256, sha256_hmac};
12use crate::primitives::point::Point;
13use crate::primitives::private_key::PrivateKey;
14use crate::primitives::signature::Signature;
15use crate::primitives::utils::{base58_check_encode, from_hex, to_hex};
16
17#[derive(Clone, Debug)]
23pub struct PublicKey {
24 point: Point,
25}
26
27impl PublicKey {
28 pub fn from_point(point: Point) -> Self {
30 PublicKey { point }
31 }
32
33 pub fn from_private_key(key: &crate::primitives::private_key::PrivateKey) -> Self {
35 key.to_public_key()
36 }
37
38 pub fn from_string(s: &str) -> Result<Self, PrimitivesError> {
43 let bytes = from_hex(s)?;
44 Self::from_der_bytes(&bytes)
45 }
46
47 pub fn from_der_bytes(bytes: &[u8]) -> Result<Self, PrimitivesError> {
49 if bytes.is_empty() {
50 return Err(PrimitivesError::InvalidPublicKey(
51 "empty public key data".to_string(),
52 ));
53 }
54
55 match bytes[0] {
56 0x02 | 0x03 => {
57 if bytes.len() != 33 {
59 return Err(PrimitivesError::InvalidPublicKey(format!(
60 "compressed key should be 33 bytes, got {}",
61 bytes.len()
62 )));
63 }
64 let odd = bytes[0] == 0x03;
65 let x = BigNumber::from_bytes(&bytes[1..], Endian::Big);
66 let point = Point::from_x(&x, odd)?;
67 Ok(PublicKey { point })
68 }
69 0x04 => {
70 if bytes.len() != 65 {
72 return Err(PrimitivesError::InvalidPublicKey(format!(
73 "uncompressed key should be 65 bytes, got {}",
74 bytes.len()
75 )));
76 }
77 let x = BigNumber::from_bytes(&bytes[1..33], Endian::Big);
78 let y = BigNumber::from_bytes(&bytes[33..], Endian::Big);
79 let point = Point::new(x, y);
80
81 if !point.validate() {
82 return Err(PrimitivesError::InvalidPublicKey(
83 "point not on curve".to_string(),
84 ));
85 }
86
87 Ok(PublicKey { point })
88 }
89 prefix => Err(PrimitivesError::InvalidPublicKey(format!(
90 "unknown prefix byte: 0x{:02x}",
91 prefix
92 ))),
93 }
94 }
95
96 pub fn to_der(&self) -> Vec<u8> {
101 self.point.to_der(true)
102 }
103
104 pub fn to_der_hex(&self) -> String {
106 to_hex(&self.to_der())
107 }
108
109 pub fn to_der_uncompressed(&self) -> Vec<u8> {
113 self.point.to_der(false)
114 }
115
116 pub fn to_hash(&self) -> Vec<u8> {
120 let der = self.to_der();
121 hash160(&der).to_vec()
122 }
123
124 pub fn to_address(&self, prefix: &[u8]) -> String {
129 let pkh = self.to_hash();
130 base58_check_encode(&pkh, prefix)
131 }
132
133 pub fn verify(&self, message: &[u8], signature: &Signature) -> bool {
137 let msg_hash = sha256(message);
138 ecdsa_verify(&msg_hash, signature, &self.point)
139 }
140
141 pub fn derive_shared_secret(&self, private_key: &PrivateKey) -> Result<Point, PrimitivesError> {
145 private_key.derive_shared_secret(self)
146 }
147
148 pub fn derive_child(
153 &self,
154 private_key: &PrivateKey,
155 invoice_number: &str,
156 ) -> Result<PublicKey, PrimitivesError> {
157 let shared_secret = private_key.derive_shared_secret(self)?;
158 self.derive_child_with_secret(&shared_secret, invoice_number)
159 }
160
161 pub fn derive_child_with_secret(
171 &self,
172 shared_secret: &Point,
173 invoice_number: &str,
174 ) -> Result<PublicKey, PrimitivesError> {
175 let shared_secret_bytes = shared_secret.to_der(true); let hmac_result = sha256_hmac(&shared_secret_bytes, invoice_number.as_bytes());
177 let hmac_bn = BigNumber::from_bytes(&hmac_result, Endian::Big);
178 let base_point = BasePoint::instance();
179 let offset_point = base_point.mul(&hmac_bn);
180 let child_point = self.point.add(&offset_point);
181
182 Ok(PublicKey::from_point(child_point))
183 }
184
185 pub fn point(&self) -> &Point {
187 &self.point
188 }
189}
190
191impl PartialEq for PublicKey {
192 fn eq(&self, other: &Self) -> bool {
193 self.point.eq(&other.point)
194 }
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200 use crate::primitives::private_key::PrivateKey;
201
202 #[test]
207 fn test_public_key_from_private_key() {
208 let priv_key = PrivateKey::from_hex("1").unwrap();
209 let pub_key = PublicKey::from_private_key(&priv_key);
210
211 assert_eq!(
213 pub_key.to_der_hex(),
214 "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
215 );
216 }
217
218 #[test]
223 fn test_public_key_from_string_compressed() {
224 let hex = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798";
225 let pub_key = PublicKey::from_string(hex).unwrap();
226 assert_eq!(pub_key.to_der_hex(), hex);
227 }
228
229 #[test]
230 fn test_public_key_from_string_uncompressed() {
231 let hex = "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8";
232 let pub_key = PublicKey::from_string(hex).unwrap();
233 assert_eq!(
235 pub_key.to_der_hex(),
236 "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
237 );
238 }
239
240 #[test]
245 fn test_public_key_der_roundtrip() {
246 let priv_key = PrivateKey::from_hex("ff").unwrap();
247 let pub_key = PublicKey::from_private_key(&priv_key);
248
249 let der_hex = pub_key.to_der_hex();
250 let recovered = PublicKey::from_string(&der_hex).unwrap();
251 assert_eq!(pub_key, recovered, "DER compression roundtrip should work");
252 }
253
254 #[test]
259 fn test_public_key_to_hash() {
260 let priv_key = PrivateKey::from_hex("1").unwrap();
261 let pub_key = PublicKey::from_private_key(&priv_key);
262 let hash = pub_key.to_hash();
263 assert_eq!(hash.len(), 20, "hash160 should be 20 bytes");
264 }
265
266 #[test]
271 fn test_public_key_to_address_mainnet() {
272 let priv_key = PrivateKey::from_hex("1").unwrap();
274 let pub_key = PublicKey::from_private_key(&priv_key);
275 let address = pub_key.to_address(&[0x00]);
276 assert_eq!(address, "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH");
277 }
278
279 #[test]
284 fn test_public_key_verify() {
285 let priv_key = PrivateKey::from_hex("1").unwrap();
286 let pub_key = PublicKey::from_private_key(&priv_key);
287 let sig = priv_key.sign(b"test verify", true).unwrap();
288
289 assert!(
290 pub_key.verify(b"test verify", &sig),
291 "Should verify valid signature"
292 );
293 assert!(
294 !pub_key.verify(b"wrong message", &sig),
295 "Should reject wrong message"
296 );
297 }
298
299 #[test]
304 fn test_public_key_uncompressed() {
305 let priv_key = PrivateKey::from_hex("1").unwrap();
306 let pub_key = PublicKey::from_private_key(&priv_key);
307 let uncompressed = pub_key.to_der_uncompressed();
308 assert_eq!(uncompressed.len(), 65);
309 assert_eq!(uncompressed[0], 0x04);
310 }
311
312 #[test]
317 fn test_public_key_der_vectors() {
318 use serde::Deserialize;
319
320 #[derive(Deserialize)]
321 struct DerVector {
322 private_key_hex: String,
323 public_key_compressed: String,
324 public_key_uncompressed: String,
325 address_mainnet: String,
326 #[allow(dead_code)]
327 address_prefix: String,
328 #[allow(dead_code)]
329 description: String,
330 }
331
332 let data = include_str!("../../test-vectors/public_key_der.json");
333 let vectors: Vec<DerVector> = serde_json::from_str(data).unwrap();
334
335 for (i, v) in vectors.iter().enumerate() {
336 let priv_key = PrivateKey::from_hex(&v.private_key_hex).unwrap();
337 let pub_key = PublicKey::from_private_key(&priv_key);
338
339 assert_eq!(
341 pub_key.to_der_hex(),
342 v.public_key_compressed,
343 "Vector {}: compressed mismatch",
344 i
345 );
346
347 let uncompressed_hex = to_hex(&pub_key.to_der_uncompressed());
349 assert_eq!(
350 uncompressed_hex, v.public_key_uncompressed,
351 "Vector {}: uncompressed mismatch",
352 i
353 );
354
355 let address = pub_key.to_address(&[0x00]);
357 assert_eq!(address, v.address_mainnet, "Vector {}: address mismatch", i);
358 }
359 }
360
361 #[test]
366 fn test_sign_verify_roundtrip_multiple_keys() {
367 for i in 1..=5 {
368 let priv_key = PrivateKey::from_hex(&format!("{:064x}", i * 1000)).unwrap();
369 let pub_key = PublicKey::from_private_key(&priv_key);
370 let msg = format!("Message number {}", i);
371
372 let sig = priv_key.sign(msg.as_bytes(), true).unwrap();
373 assert!(
374 pub_key.verify(msg.as_bytes(), &sig),
375 "Key {} should verify",
376 i
377 );
378 }
379 }
380}