1use crate::hex::{hex_decode, hex_encode};
2use crate::{EcdsaSignature, SchnorrSignature, SecpError};
3
4use neco_galois::generate_k;
5use neco_galois::{Fp, PrimeField, Secp256k1Field, Secp256k1Order, SQRT_EXP_SECP256K1, U256};
6use neco_sha2::Sha256;
7
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10
11type FpField = Fp<Secp256k1Field>;
16type Scalar = Fp<Secp256k1Order>;
17
18fn curve_b() -> FpField {
24 FpField::from_u256(U256::from_u64(7))
25}
26
27fn generator_x() -> FpField {
28 FpField::from_u256(U256::from_be_bytes([
29 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B,
30 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8,
31 0x17, 0x98,
32 ]))
33}
34
35fn generator_y() -> FpField {
36 FpField::from_u256(U256::from_be_bytes([
37 0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65, 0x5D, 0xA4, 0xFB, 0xFC, 0x0E, 0x11, 0x08,
38 0xA8, 0xFD, 0x17, 0xB4, 0x48, 0xA6, 0x85, 0x54, 0x19, 0x9C, 0x47, 0xD0, 0x8F, 0xFB, 0x10,
39 0xD4, 0xB8,
40 ]))
41}
42
43fn generator() -> AffinePoint {
44 AffinePoint {
45 x: generator_x(),
46 y: generator_y(),
47 }
48}
49
50fn order_u256() -> U256 {
51 Secp256k1Order::MODULUS
52}
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
59pub(crate) struct AffinePoint {
60 pub(crate) x: FpField,
61 pub(crate) y: FpField,
62}
63
64#[derive(Clone, Copy, Debug)]
65struct ProjectivePoint {
66 x: FpField,
67 y: FpField,
68 z: FpField,
69 is_infinity: bool,
70}
71
72impl ProjectivePoint {
73 fn infinity() -> Self {
74 ProjectivePoint {
75 x: FpField::ZERO,
76 y: FpField::ZERO,
77 z: FpField::ZERO,
78 is_infinity: true,
79 }
80 }
81
82 fn from_affine(p: AffinePoint) -> Self {
83 ProjectivePoint {
84 x: p.x,
85 y: p.y,
86 z: FpField::one(),
87 is_infinity: false,
88 }
89 }
90}
91
92fn point_double(p: ProjectivePoint) -> ProjectivePoint {
98 if p.is_infinity {
99 return ProjectivePoint::infinity();
100 }
101
102 let x = p.x;
103 let y = p.y;
104 let z = p.z;
105
106 let x2 = FpField::sqr(x);
108 let two_x2 = FpField::add(x2, x2);
109 let m = FpField::add(two_x2, x2);
110
111 let y2 = FpField::sqr(y);
113 let xy = FpField::mul(x, y2);
114 let two_xy = FpField::add(xy, xy);
115 let s = FpField::add(two_xy, two_xy);
116
117 let m2 = FpField::sqr(m);
119 let two_s = FpField::add(s, s);
120 let x_new = FpField::sub(m2, two_s);
121
122 let s_minus_x = FpField::sub(s, x_new);
124 let my = FpField::mul(m, s_minus_x);
125 let y4 = FpField::sqr(y2);
126 let eight_y4 = {
127 let two = FpField::add(y4, y4);
128 let four = FpField::add(two, two);
129 FpField::add(four, four)
130 };
131 let y_new = FpField::sub(my, eight_y4);
132
133 let yz = FpField::mul(y, z);
135 let z_new = FpField::add(yz, yz);
136
137 ProjectivePoint {
138 x: x_new,
139 y: y_new,
140 z: z_new,
141 is_infinity: false,
142 }
143}
144
145fn point_add(p: ProjectivePoint, q: ProjectivePoint) -> ProjectivePoint {
147 if p.is_infinity {
148 return q;
149 }
150 if q.is_infinity {
151 return p;
152 }
153
154 let x1 = p.x;
155 let y1 = p.y;
156 let z1 = p.z;
157 let x2 = q.x;
158 let y2 = q.y;
159 let z2 = q.z;
160
161 let z1_sq = FpField::sqr(z1);
162 let z2_sq = FpField::sqr(z2);
163 let u1 = FpField::mul(x1, z2_sq);
164 let u2 = FpField::mul(x2, z1_sq);
165
166 let s1 = FpField::mul(y1, FpField::mul(z2, z2_sq));
167 let s2 = FpField::mul(y2, FpField::mul(z1, z1_sq));
168
169 let h = FpField::sub(u2, u1);
170 let r = FpField::sub(s2, s1);
171
172 if FpField::is_zero(h) {
173 if FpField::is_zero(r) {
174 return point_double(p);
175 } else {
176 return ProjectivePoint::infinity();
177 }
178 }
179
180 let h2 = FpField::sqr(h);
181 let h3 = FpField::mul(h, h2);
182 let u1h2 = FpField::mul(u1, h2);
183
184 let r2 = FpField::sqr(r);
185 let two_u1h2 = FpField::add(u1h2, u1h2);
186 let x3 = FpField::sub(FpField::sub(r2, h3), two_u1h2);
187
188 let u1h2_minus_x3 = FpField::sub(u1h2, x3);
189 let s1h3 = FpField::mul(s1, h3);
190 let y3 = FpField::sub(FpField::mul(r, u1h2_minus_x3), s1h3);
191
192 let z3 = FpField::mul(h, FpField::mul(z1, z2));
193
194 ProjectivePoint {
195 x: x3,
196 y: y3,
197 z: z3,
198 is_infinity: false,
199 }
200}
201
202fn to_affine(p: ProjectivePoint) -> Option<AffinePoint> {
203 if p.is_infinity {
204 return None;
205 }
206 let z_inv = FpField::inv(p.z);
207 let z_inv2 = FpField::sqr(z_inv);
208 let z_inv3 = FpField::mul(z_inv, z_inv2);
209 let x = FpField::mul(p.x, z_inv2);
210 let y = FpField::mul(p.y, z_inv3);
211 Some(AffinePoint { x, y })
212}
213
214fn scalar_mul(k: U256, p: AffinePoint) -> ProjectivePoint {
216 let mut r0 = ProjectivePoint::infinity();
217 let mut r1 = ProjectivePoint::from_affine(p);
218
219 for i in (0..256u32).rev() {
220 if U256::bit(k, i) {
221 r0 = point_add(r0, r1);
222 r1 = point_double(r1);
223 } else {
224 r1 = point_add(r0, r1);
225 r0 = point_double(r0);
226 }
227 }
228 r0
229}
230
231fn is_on_curve(p: AffinePoint) -> bool {
233 let y2 = FpField::sqr(p.y);
234 let x3 = FpField::mul(FpField::sqr(p.x), p.x);
235 let rhs = FpField::add(x3, curve_b());
236 FpField::eq(y2, rhs)
237}
238
239fn encode_sec1_compressed(p: AffinePoint) -> [u8; 33] {
244 let x_bytes = p.x.to_u256().to_be_bytes();
245 let y_val = p.y.to_u256();
246 let prefix = if y_val.l0 & 1 == 1 { 0x03u8 } else { 0x02u8 };
247 let mut out = [0u8; 33];
248 out[0] = prefix;
249 out[1..].copy_from_slice(&x_bytes);
250 out
251}
252
253fn decode_sec1_compressed(bytes: &[u8]) -> Option<AffinePoint> {
254 if bytes.len() != 33 {
255 return None;
256 }
257 let prefix = bytes[0];
258 if prefix != 0x02 && prefix != 0x03 {
259 return None;
260 }
261 let x_bytes: [u8; 32] = bytes[1..].try_into().ok()?;
262 let x_u256 = U256::from_be_bytes(x_bytes);
263
264 if !matches!(
265 U256::cmp(x_u256, Secp256k1Field::MODULUS),
266 core::cmp::Ordering::Less
267 ) {
268 return None;
269 }
270
271 let x = FpField::from_u256(x_u256);
272 let b = curve_b();
273
274 let x3 = FpField::mul(FpField::sqr(x), x);
276 let rhs = FpField::add(x3, b);
277
278 let y = FpField::sqrt(rhs, SQRT_EXP_SECP256K1)?;
279 let y_u256 = y.to_u256();
280
281 let y_odd = y_u256.l0 & 1 == 1;
282 let want_odd = prefix == 0x03;
283
284 let y_final = if y_odd == want_odd {
285 y
286 } else {
287 FpField::neg(y)
288 };
289
290 let point = AffinePoint { x, y: y_final };
291
292 if !is_on_curve(point) {
293 return None;
294 }
295
296 Some(point)
297}
298
299fn normalize_s(s: U256, n: U256) -> U256 {
304 let half_n = U256::shr1(n);
305 if let core::cmp::Ordering::Greater = U256::cmp(s, half_n) {
306 let (ns, _) = U256::sub(n, s);
307 ns
308 } else {
309 s
310 }
311}
312
313fn ecdsa_sign(secret_bytes: &[u8; 32], digest: &[u8; 32]) -> Option<[u8; 64]> {
314 let n = order_u256();
315 let g = generator();
316
317 let d = U256::from_be_bytes(*secret_bytes);
318
319 let k = generate_k(secret_bytes, digest, &n);
320
321 let r_proj = scalar_mul(k, g);
322 let r_affine = to_affine(r_proj)?;
323
324 let rx = r_affine.x.to_u256();
325 let r = if let core::cmp::Ordering::Less = U256::cmp(rx, n) {
326 rx
327 } else {
328 let (v, _) = U256::sub(rx, n);
329 v
330 };
331 if U256::is_zero(r) {
332 return None;
333 }
334
335 let e_raw = U256::from_be_bytes(*digest);
336 let e = if let core::cmp::Ordering::Less = U256::cmp(e_raw, n) {
337 e_raw
338 } else {
339 let (v, _) = U256::sub(e_raw, n);
340 v
341 };
342
343 let k_scalar = Scalar::from_u256(k);
344 let k_inv = Scalar::inv(k_scalar);
345 let r_scalar = Scalar::from_u256(r);
346 let d_scalar = Scalar::from_u256(d);
347 let e_scalar = Scalar::from_u256(e);
348
349 let rd = Scalar::mul(r_scalar, d_scalar);
350 let e_plus_rd = Scalar::add(e_scalar, rd);
351 let s_scalar = Scalar::mul(k_inv, e_plus_rd);
352 let s = s_scalar.to_u256();
353
354 if U256::is_zero(s) {
355 return None;
356 }
357
358 let s = normalize_s(s, n);
359
360 let mut out = [0u8; 64];
361 out[..32].copy_from_slice(&r.to_be_bytes());
362 out[32..].copy_from_slice(&s.to_be_bytes());
363 Some(out)
364}
365
366fn ecdsa_verify(pubkey_sec1: &[u8; 33], digest: &[u8; 32], sig_bytes: &[u8; 64]) -> bool {
367 let n = order_u256();
368 let g = generator();
369
370 let r = U256::from_be_bytes(sig_bytes[..32].try_into().unwrap());
371 let s = U256::from_be_bytes(sig_bytes[32..].try_into().unwrap());
372
373 if U256::is_zero(r) || U256::is_zero(s) {
374 return false;
375 }
376 if !matches!(U256::cmp(r, n), core::cmp::Ordering::Less) {
377 return false;
378 }
379 if !matches!(U256::cmp(s, n), core::cmp::Ordering::Less) {
380 return false;
381 }
382
383 let half_n = U256::shr1(n);
384 if let core::cmp::Ordering::Greater = U256::cmp(s, half_n) {
385 return false;
386 }
387
388 let pubkey = match decode_sec1_compressed(pubkey_sec1) {
389 Some(p) => p,
390 None => return false,
391 };
392
393 let e_raw = U256::from_be_bytes(*digest);
394 let e = if let core::cmp::Ordering::Less = U256::cmp(e_raw, n) {
395 e_raw
396 } else {
397 let (v, _) = U256::sub(e_raw, n);
398 v
399 };
400
401 let s_scalar = Scalar::from_u256(s);
402 let w_scalar = Scalar::inv(s_scalar);
403
404 let e_scalar = Scalar::from_u256(e);
405 let r_scalar = Scalar::from_u256(r);
406
407 let u1 = Scalar::mul(e_scalar, w_scalar).to_u256();
408 let u2 = Scalar::mul(r_scalar, w_scalar).to_u256();
409
410 let u1g = scalar_mul(u1, g);
411 let u2q = scalar_mul(u2, pubkey);
412 let r_point = point_add(u1g, u2q);
413
414 let r_affine = match to_affine(r_point) {
415 Some(p) => p,
416 None => return false,
417 };
418
419 let r_x = r_affine.x.to_u256();
420 let r_x_mod_n = if let core::cmp::Ordering::Less = U256::cmp(r_x, n) {
421 r_x
422 } else {
423 let (v, _) = U256::sub(r_x, n);
424 v
425 };
426
427 U256::cmp(r_x_mod_n, r) == core::cmp::Ordering::Equal
428}
429
430fn tagged_hash(tag: &[u8], data: &[u8]) -> [u8; 32] {
435 let tag_hash = Sha256::digest(tag);
436 let mut h = Sha256::new();
437 h.update(&tag_hash);
438 h.update(&tag_hash);
439 h.update(data);
440 h.finalize()
441}
442
443fn lift_x(x_bytes: &[u8; 32]) -> Option<AffinePoint> {
445 let x_u256 = U256::from_be_bytes(*x_bytes);
446 if !matches!(
447 U256::cmp(x_u256, Secp256k1Field::MODULUS),
448 core::cmp::Ordering::Less
449 ) {
450 return None;
451 }
452
453 let x = FpField::from_u256(x_u256);
454 let b = curve_b();
455 let x3 = FpField::mul(FpField::sqr(x), x);
456 let rhs = FpField::add(x3, b);
457
458 let y = FpField::sqrt(rhs, SQRT_EXP_SECP256K1)?;
459 let y_u256 = y.to_u256();
460
461 let y_final = if y_u256.l0 & 1 == 1 {
463 FpField::neg(y)
464 } else {
465 y
466 };
467
468 Some(AffinePoint { x, y: y_final })
469}
470
471fn schnorr_sign(secret: &[u8; 32], digest: &[u8; 32]) -> Option<[u8; 64]> {
472 let mut aux = [0u8; 32];
474 let _ = getrandom::getrandom(&mut aux);
475 schnorr_sign_with_aux(secret, digest, &aux)
476}
477
478fn schnorr_sign_with_aux(secret: &[u8; 32], digest: &[u8; 32], aux: &[u8; 32]) -> Option<[u8; 64]> {
479 let n = order_u256();
480 let d0 = U256::from_be_bytes(*secret);
481 if U256::is_zero(d0) || !matches!(U256::cmp(d0, n), core::cmp::Ordering::Less) {
482 return None;
483 }
484
485 let g = generator();
486 let p_proj = scalar_mul(d0, g);
487 let p_affine = to_affine(p_proj)?;
488
489 let py = p_affine.y.to_u256();
491 let d = if py.l0 & 1 == 1 {
492 let (neg, _) = U256::sub(n, d0);
493 neg
494 } else {
495 d0
496 };
497 let px_bytes = p_affine.x.to_u256().to_be_bytes();
498
499 let t = tagged_hash(b"BIP0340/aux", aux);
500 let d_bytes = d.to_be_bytes();
502 let mut rand = [0u8; 32];
503 for i in 0..32 {
504 rand[i] = d_bytes[i] ^ t[i];
505 }
506
507 let mut nonce_input = [0u8; 96];
509 nonce_input[..32].copy_from_slice(&rand);
510 nonce_input[32..64].copy_from_slice(&px_bytes);
511 nonce_input[64..].copy_from_slice(digest);
512 let nonce_hash = tagged_hash(b"BIP0340/nonce", &nonce_input);
513
514 let k0 = U256::from_be_bytes(nonce_hash);
515 let k0 = if let core::cmp::Ordering::Less = U256::cmp(k0, n) {
517 k0
518 } else {
519 let (v, _) = U256::sub(k0, n);
520 v
521 };
522 if U256::is_zero(k0) {
523 return None;
524 }
525
526 let r_proj = scalar_mul(k0, g);
527 let r_affine = to_affine(r_proj)?;
528
529 let ry = r_affine.y.to_u256();
530 let k = if ry.l0 & 1 == 1 {
531 let (neg, _) = U256::sub(n, k0);
532 neg
533 } else {
534 k0
535 };
536
537 let rx_bytes = r_affine.x.to_u256().to_be_bytes();
538
539 let mut challenge_input = [0u8; 96];
541 challenge_input[..32].copy_from_slice(&rx_bytes);
542 challenge_input[32..64].copy_from_slice(&px_bytes);
543 challenge_input[64..].copy_from_slice(digest);
544 let e_hash = tagged_hash(b"BIP0340/challenge", &challenge_input);
545 let e_raw = U256::from_be_bytes(e_hash);
546 let e = if let core::cmp::Ordering::Less = U256::cmp(e_raw, n) {
547 e_raw
548 } else {
549 let (v, _) = U256::sub(e_raw, n);
550 v
551 };
552
553 let e_scalar = Scalar::from_u256(e);
555 let d_scalar = Scalar::from_u256(d);
556 let k_scalar = Scalar::from_u256(k);
557 let s = Scalar::add(k_scalar, Scalar::mul(e_scalar, d_scalar)).to_u256();
558
559 let mut sig = [0u8; 64];
560 sig[..32].copy_from_slice(&rx_bytes);
561 sig[32..].copy_from_slice(&s.to_be_bytes());
562 Some(sig)
563}
564
565fn schnorr_verify(pubkey_x: &[u8; 32], digest: &[u8; 32], sig: &[u8; 64]) -> bool {
566 let n = order_u256();
567
568 let rx_bytes: [u8; 32] = sig[..32].try_into().unwrap();
569 let s_bytes: [u8; 32] = sig[32..].try_into().unwrap();
570
571 let s = U256::from_be_bytes(s_bytes);
572 if !matches!(U256::cmp(s, n), core::cmp::Ordering::Less) {
573 return false;
574 }
575
576 let p = match lift_x(pubkey_x) {
577 Some(p) => p,
578 None => return false,
579 };
580
581 let mut challenge_input = [0u8; 96];
583 challenge_input[..32].copy_from_slice(&rx_bytes);
584 challenge_input[32..64].copy_from_slice(pubkey_x);
585 challenge_input[64..].copy_from_slice(digest);
586 let e_hash = tagged_hash(b"BIP0340/challenge", &challenge_input);
587 let e_raw = U256::from_be_bytes(e_hash);
588 let e = if let core::cmp::Ordering::Less = U256::cmp(e_raw, n) {
589 e_raw
590 } else {
591 let (v, _) = U256::sub(e_raw, n);
592 v
593 };
594
595 let g = generator();
596
597 let sg = scalar_mul(s, g);
599 let (neg_e, _) = U256::sub(n, e);
601 let neg_ep = scalar_mul(neg_e, p);
602 let r_proj = point_add(sg, neg_ep);
603
604 let r_affine = match to_affine(r_proj) {
605 Some(p) => p,
606 None => return false,
607 };
608
609 if r_affine.y.to_u256().l0 & 1 == 1 {
611 return false;
612 }
613
614 let rx = U256::from_be_bytes(rx_bytes);
616 r_affine.x.to_u256() == rx
617}
618
619#[cfg(any(feature = "nip04", feature = "nip44"))]
624pub(crate) fn ecdh_raw(secret: &[u8; 32], pubkey: AffinePoint) -> Option<[u8; 32]> {
625 let d = U256::from_be_bytes(*secret);
626 let n = order_u256();
627 if U256::is_zero(d) || !matches!(U256::cmp(d, n), core::cmp::Ordering::Less) {
628 return None;
629 }
630 let q = scalar_mul(d, pubkey);
631 let q_affine = to_affine(q)?;
632 Some(q_affine.x.to_u256().to_be_bytes())
633}
634
635fn validate_secret_key(bytes: &[u8; 32]) -> bool {
640 let d = U256::from_be_bytes(*bytes);
641 if U256::is_zero(d) {
642 return false;
643 }
644 matches!(U256::cmp(d, order_u256()), core::cmp::Ordering::Less)
645}
646
647#[derive(Debug, Clone, Copy, PartialEq, Eq)]
652pub struct SecretKey {
653 pub(crate) bytes: [u8; 32],
654}
655
656impl SecretKey {
657 pub fn generate() -> Result<Self, SecpError> {
658 let n = order_u256();
659 loop {
660 let mut buf = [0u8; 32];
661 getrandom::getrandom(&mut buf).map_err(|_| SecpError::InvalidSecretKey)?;
662 let k = U256::from_be_bytes(buf);
663 if !U256::is_zero(k) {
664 if let core::cmp::Ordering::Less = U256::cmp(k, n) {
665 return Ok(Self { bytes: buf });
666 }
667 }
668 }
669 }
670
671 pub fn from_hex(hex: &str) -> Result<Self, SecpError> {
672 let bytes = hex_decode(hex)?;
673 if bytes.len() != 32 {
674 return Err(SecpError::InvalidHex("expected 64 hex characters"));
675 }
676 let mut arr = [0u8; 32];
677 arr.copy_from_slice(&bytes);
678 Self::from_bytes(arr)
679 }
680
681 pub fn from_bytes(bytes: [u8; 32]) -> Result<Self, SecpError> {
682 if !validate_secret_key(&bytes) {
683 return Err(SecpError::InvalidSecretKey);
684 }
685 Ok(Self { bytes })
686 }
687
688 pub fn to_hex(&self) -> String {
689 hex_encode(&self.bytes)
690 }
691
692 pub fn to_bytes(&self) -> [u8; 32] {
693 self.bytes
694 }
695
696 pub fn public_key(&self) -> Result<PublicKey, SecpError> {
697 let d = U256::from_be_bytes(self.bytes);
698 let g = generator();
699 let q_proj = scalar_mul(d, g);
700 let q = to_affine(q_proj).ok_or(SecpError::InvalidSecretKey)?;
701 let sec1_bytes = encode_sec1_compressed(q);
702 Ok(PublicKey { sec1_bytes })
703 }
704
705 pub fn xonly_public_key(&self) -> Result<XOnlyPublicKey, SecpError> {
706 let d = U256::from_be_bytes(self.bytes);
707 let g = generator();
708 let q_proj = scalar_mul(d, g);
709 let q = to_affine(q_proj).ok_or(SecpError::InvalidSecretKey)?;
710 let x_bytes = q.x.to_u256().to_be_bytes();
711 Ok(XOnlyPublicKey { bytes: x_bytes })
712 }
713
714 pub fn sign_schnorr_prehash(&self, digest32: [u8; 32]) -> Result<SchnorrSignature, SecpError> {
715 let sig_bytes = schnorr_sign(&self.bytes, &digest32).ok_or(SecpError::InvalidSignature)?;
716 Ok(SchnorrSignature { bytes: sig_bytes })
717 }
718
719 pub fn sign_schnorr_prehash_deterministic(
726 &self,
727 digest32: [u8; 32],
728 ) -> Result<SchnorrSignature, SecpError> {
729 let zero_aux = [0u8; 32];
730 let sig_bytes = schnorr_sign_with_aux(&self.bytes, &digest32, &zero_aux)
731 .ok_or(SecpError::InvalidSignature)?;
732 Ok(SchnorrSignature { bytes: sig_bytes })
733 }
734
735 pub fn sign_ecdsa_prehash(&self, digest32: [u8; 32]) -> Result<EcdsaSignature, SecpError> {
736 let sig_bytes = ecdsa_sign(&self.bytes, &digest32).ok_or(SecpError::InvalidSignature)?;
737 Ok(EcdsaSignature { bytes: sig_bytes })
738 }
739}
740
741#[derive(Debug, Clone, Copy, PartialEq, Eq)]
742pub struct PublicKey {
743 sec1_bytes: [u8; 33],
744}
745
746impl PublicKey {
747 pub fn from_hex(hex: &str) -> Result<Self, SecpError> {
748 let bytes = hex_decode(hex)?;
749 if bytes.len() != 33 {
750 return Err(SecpError::InvalidHex("expected 66 hex characters"));
751 }
752 Self::from_sec1_bytes(&bytes)
753 }
754
755 pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self, SecpError> {
756 if bytes.len() != 33 {
757 return Err(SecpError::InvalidPublicKey);
758 }
759 let point = decode_sec1_compressed(bytes).ok_or(SecpError::InvalidPublicKey)?;
760 let sec1_bytes = encode_sec1_compressed(point);
761 Ok(Self { sec1_bytes })
762 }
763
764 pub fn to_hex(&self) -> String {
765 hex_encode(&self.sec1_bytes)
766 }
767
768 pub fn to_sec1_bytes(&self) -> [u8; 33] {
769 self.sec1_bytes
770 }
771
772 pub fn verify_ecdsa_prehash(
773 &self,
774 digest32: [u8; 32],
775 sig: &EcdsaSignature,
776 ) -> Result<(), SecpError> {
777 if ecdsa_verify(&self.sec1_bytes, &digest32, &sig.bytes) {
778 Ok(())
779 } else {
780 Err(SecpError::InvalidSignature)
781 }
782 }
783}
784
785#[derive(Debug, Clone, Copy, PartialEq, Eq)]
786pub struct XOnlyPublicKey {
787 pub(crate) bytes: [u8; 32],
788}
789
790impl XOnlyPublicKey {
791 pub fn from_hex(hex: &str) -> Result<Self, SecpError> {
792 let bytes = hex_decode(hex)?;
793 if bytes.len() != 32 {
794 return Err(SecpError::InvalidHex("expected 64 hex characters"));
795 }
796 let mut arr = [0u8; 32];
797 arr.copy_from_slice(&bytes);
798 Self::from_bytes(arr)
799 }
800
801 pub fn from_bytes(bytes: [u8; 32]) -> Result<Self, SecpError> {
802 let _ = lift_x(&bytes).ok_or(SecpError::InvalidPublicKey)?;
803 Ok(Self { bytes })
804 }
805
806 pub fn to_hex(&self) -> String {
807 hex_encode(&self.bytes)
808 }
809
810 pub fn to_bytes(&self) -> [u8; 32] {
811 self.bytes
812 }
813
814 pub fn verify_schnorr_prehash(
815 &self,
816 digest32: [u8; 32],
817 sig: &SchnorrSignature,
818 ) -> Result<(), SecpError> {
819 if schnorr_verify(&self.bytes, &digest32, &sig.bytes) {
820 Ok(())
821 } else {
822 Err(SecpError::InvalidSignature)
823 }
824 }
825}
826
827#[cfg(feature = "serde")]
828impl Serialize for XOnlyPublicKey {
829 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
830 where
831 S: Serializer,
832 {
833 serializer.serialize_str(&self.to_hex())
834 }
835}
836
837#[cfg(feature = "serde")]
838impl<'de> Deserialize<'de> for XOnlyPublicKey {
839 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
840 where
841 D: Deserializer<'de>,
842 {
843 let hex = String::deserialize(deserializer)?;
844 Self::from_hex(&hex).map_err(serde::de::Error::custom)
845 }
846}
847
848#[cfg(any(feature = "nip04", feature = "nip44"))]
849pub(crate) fn decode_xonly_pubkey(pubkey: &XOnlyPublicKey) -> Result<AffinePoint, SecpError> {
850 lift_x(&pubkey.bytes).ok_or(SecpError::InvalidPublicKey)
851}