Skip to main content

neco_secp/
keys.rs

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
11// -----------------------------------------------------------------------
12// 型エイリアス
13// -----------------------------------------------------------------------
14
15type FpField = Fp<Secp256k1Field>;
16type Scalar = Fp<Secp256k1Order>;
17
18// -----------------------------------------------------------------------
19// secp256k1 曲線定数
20// a = 0, b = 7, G = (Gx, Gy)
21// -----------------------------------------------------------------------
22
23fn 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// -----------------------------------------------------------------------
55// 点の表現
56// -----------------------------------------------------------------------
57
58#[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
92// -----------------------------------------------------------------------
93// secp256k1 群演算
94// -----------------------------------------------------------------------
95
96/// 射影点の2倍算。secp256k1 は a=0 なので M = 3*X²。
97fn 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    // a=0: M = 3*X²
107    let x2 = FpField::sqr(x);
108    let two_x2 = FpField::add(x2, x2);
109    let m = FpField::add(two_x2, x2);
110
111    // S = 4*X*Y²
112    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    // X' = M² - 2*S
118    let m2 = FpField::sqr(m);
119    let two_s = FpField::add(s, s);
120    let x_new = FpField::sub(m2, two_s);
121
122    // Y' = M*(S - X') - 8*Y⁴
123    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    // Z' = 2*Y*Z
134    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
145/// 射影点の加算 (Jacobian)
146fn 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
214/// Montgomery ladder: constant-time scalar multiplication
215fn 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
231/// y² = x³ + 7
232fn 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
239// -----------------------------------------------------------------------
240// SEC1 エンコード/デコード
241// -----------------------------------------------------------------------
242
243fn 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    // y² = x³ + 7
275    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
299// -----------------------------------------------------------------------
300// ECDSA 署名/検証
301// -----------------------------------------------------------------------
302
303fn 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
430// -----------------------------------------------------------------------
431// BIP340 Schnorr
432// -----------------------------------------------------------------------
433
434fn 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
443/// x 座標から偶数 y の点を復元 (BIP340 lift_x)
444fn 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    // BIP340: 偶数 y を選択
462    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    // BIP340 recommends fresh randomness for fault attack resistance
473    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    // BIP340: y が奇数なら d = n - d0
490    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    // t XOR d
501    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    // nonce = tagged_hash("BIP0340/nonce", rand || px || digest)
508    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    // k0 mod n
516    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    // e = tagged_hash("BIP0340/challenge", rx || px || digest) mod n
540    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    // sig = (rx, k + e*d mod n)
554    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    // e = tagged_hash("BIP0340/challenge", rx || px || digest) mod n
582    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    // R = s*G - e*P
598    let sg = scalar_mul(s, g);
599    // -e*P = (n-e)*P
600    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    // R.y must be even
610    if r_affine.y.to_u256().l0 & 1 == 1 {
611        return false;
612    }
613
614    // R.x must equal rx
615    let rx = U256::from_be_bytes(rx_bytes);
616    r_affine.x.to_u256() == rx
617}
618
619// -----------------------------------------------------------------------
620// ECDH
621// -----------------------------------------------------------------------
622
623#[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
635// -----------------------------------------------------------------------
636// 秘密鍵バリデーション
637// -----------------------------------------------------------------------
638
639fn 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// -----------------------------------------------------------------------
648// 公開 API
649// -----------------------------------------------------------------------
650
651#[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    /// aux_rand をゼロ固定にした決定的 BIP-340 Schnorr 署名。
720    ///
721    /// 同じ秘密鍵・同じ digest に対して常に同じ署名バイト列を返す。
722    /// BIP-340 はフォールトアタック耐性のためフレッシュな aux_rand を推奨しており、
723    /// 本 API はテスト互換や外部ツールとの fixture 照合用途に限って使用すること。
724    /// 通常の署名用途では [`sign_schnorr_prehash`] を使うこと。
725    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}