Skip to main content

neco_p256/
lib.rs

1//! Minimal P-256 ECDSA signing core.
2//!
3//! p256 crate への依存を排除し、neco-galois ベースの自前実装に置換。
4
5use core::fmt;
6
7use neco_galois::generate_k;
8use neco_galois::{Fp, P256Field, P256Order, PrimeField, SQRT_EXP_P256, U256};
9
10// -----------------------------------------------------------------------
11// 型エイリアス
12// -----------------------------------------------------------------------
13
14type FpField = Fp<P256Field>;
15type Scalar = Fp<P256Order>;
16
17// -----------------------------------------------------------------------
18// P-256 曲線定数
19// a = -3 (mod p)
20// b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b
21// G = (Gx, Gy)
22// -----------------------------------------------------------------------
23
24/// P-256 の係数 a = p - 3 (= -3 mod p)
25fn curve_a() -> FpField {
26    let (a_val, _) = U256::sub(P256Field::MODULUS, U256::from_u64(3));
27    FpField::from_u256(a_val)
28}
29
30/// P-256 の係数 b
31fn curve_b() -> FpField {
32    FpField::from_u256(U256::from_be_bytes([
33        0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7, 0xb3, 0xeb, 0xbd, 0x55, 0x76, 0x98, 0x86,
34        0xbc, 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53, 0xb0, 0xf6, 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2,
35        0x60, 0x4b,
36    ]))
37}
38
39/// P-256 生成元 G の x 座標
40fn generator_x() -> FpField {
41    FpField::from_u256(U256::from_be_bytes([
42        0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47, 0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4, 0x40,
43        0xf2, 0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, 0x33, 0xa0, 0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98,
44        0xc2, 0x96,
45    ]))
46}
47
48/// P-256 生成元 G の y 座標
49fn generator_y() -> FpField {
50    FpField::from_u256(U256::from_be_bytes([
51        0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b, 0x8e, 0xe7, 0xeb, 0x4a, 0x7c, 0x0f, 0x9e,
52        0x16, 0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce, 0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf,
53        0x51, 0xf5,
54    ]))
55}
56
57/// P-256 の群位数 n
58fn order_u256() -> U256 {
59    P256Order::MODULUS
60}
61
62// -----------------------------------------------------------------------
63// 点の表現
64// -----------------------------------------------------------------------
65
66/// アフィン座標点
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68struct AffinePoint {
69    x: FpField,
70    y: FpField,
71}
72
73/// Jacobian 射影座標点 (X:Y:Z)、アフィン座標では (X/Z², Y/Z³)
74#[derive(Clone, Copy, Debug)]
75struct ProjectivePoint {
76    x: FpField,
77    y: FpField,
78    z: FpField,
79    is_infinity: bool,
80}
81
82impl ProjectivePoint {
83    fn infinity() -> Self {
84        ProjectivePoint {
85            x: FpField::ZERO,
86            y: FpField::ZERO,
87            z: FpField::ZERO,
88            is_infinity: true,
89        }
90    }
91
92    fn from_affine(p: AffinePoint) -> Self {
93        ProjectivePoint {
94            x: p.x,
95            y: p.y,
96            z: FpField::one(),
97            is_infinity: false,
98        }
99    }
100}
101
102// -----------------------------------------------------------------------
103// P-256 群演算
104// -----------------------------------------------------------------------
105
106/// 射影点の2倍算。P-256 の a=-3 最適化を使用。
107/// M = 3*(X - Z²)*(X + Z²) (Hankerson et al. Algorithm 3.21)
108fn point_double(p: ProjectivePoint) -> ProjectivePoint {
109    if p.is_infinity {
110        return ProjectivePoint::infinity();
111    }
112
113    let x = p.x;
114    let y = p.y;
115    let z = p.z;
116
117    // a=-3 最適化: M = 3*(X - Z²)*(X + Z²)
118    let z2 = FpField::sqr(z);
119    let xmz2 = FpField::sub(x, z2);
120    let xpz2 = FpField::add(x, z2);
121    let m_part = FpField::mul(xmz2, xpz2);
122    let two_m = FpField::add(m_part, m_part);
123    let m = FpField::add(two_m, m_part); // M = 3*(X-Z²)*(X+Z²)
124
125    // S = 4*X*Y²
126    let y2 = FpField::sqr(y);
127    let xy = FpField::mul(x, y2);
128    let two_xy = FpField::add(xy, xy);
129    let s = FpField::add(two_xy, two_xy);
130
131    // X' = M² - 2*S
132    let m2 = FpField::sqr(m);
133    let two_s = FpField::add(s, s);
134    let x_new = FpField::sub(m2, two_s);
135
136    // Y' = M*(S - X') - 8*Y⁴
137    let s_minus_x = FpField::sub(s, x_new);
138    let my = FpField::mul(m, s_minus_x);
139    let y4 = FpField::sqr(y2);
140    let eight_y4 = {
141        let two = FpField::add(y4, y4);
142        let four = FpField::add(two, two);
143        FpField::add(four, four)
144    };
145    let y_new = FpField::sub(my, eight_y4);
146
147    // Z' = 2*Y*Z
148    let yz = FpField::mul(y, z);
149    let z_new = FpField::add(yz, yz);
150
151    ProjectivePoint {
152        x: x_new,
153        y: y_new,
154        z: z_new,
155        is_infinity: false,
156    }
157}
158
159/// 射影点の加算 (Jacobian)
160fn point_add(p: ProjectivePoint, q: ProjectivePoint) -> ProjectivePoint {
161    if p.is_infinity {
162        return q;
163    }
164    if q.is_infinity {
165        return p;
166    }
167
168    let x1 = p.x;
169    let y1 = p.y;
170    let z1 = p.z;
171    let x2 = q.x;
172    let y2 = q.y;
173    let z2 = q.z;
174
175    // U1 = X1*Z2², U2 = X2*Z1²
176    let z1_sq = FpField::sqr(z1);
177    let z2_sq = FpField::sqr(z2);
178    let u1 = FpField::mul(x1, z2_sq);
179    let u2 = FpField::mul(x2, z1_sq);
180
181    // S1 = Y1*Z2³, S2 = Y2*Z1³
182    let s1 = FpField::mul(y1, FpField::mul(z2, z2_sq));
183    let s2 = FpField::mul(y2, FpField::mul(z1, z1_sq));
184
185    let h = FpField::sub(u2, u1);
186    let r = FpField::sub(s2, s1);
187
188    if FpField::is_zero(h) {
189        if FpField::is_zero(r) {
190            return point_double(p);
191        } else {
192            return ProjectivePoint::infinity();
193        }
194    }
195
196    let h2 = FpField::sqr(h);
197    let h3 = FpField::mul(h, h2);
198    let u1h2 = FpField::mul(u1, h2);
199
200    // X' = R² - H³ - 2*U1*H²
201    let r2 = FpField::sqr(r);
202    let two_u1h2 = FpField::add(u1h2, u1h2);
203    let x3 = FpField::sub(FpField::sub(r2, h3), two_u1h2);
204
205    // Y' = R*(U1*H² - X') - S1*H³
206    let u1h2_minus_x3 = FpField::sub(u1h2, x3);
207    let s1h3 = FpField::mul(s1, h3);
208    let y3 = FpField::sub(FpField::mul(r, u1h2_minus_x3), s1h3);
209
210    // Z' = H*Z1*Z2
211    let z3 = FpField::mul(h, FpField::mul(z1, z2));
212
213    ProjectivePoint {
214        x: x3,
215        y: y3,
216        z: z3,
217        is_infinity: false,
218    }
219}
220
221/// 射影点をアフィン点に変換 (Z^-1 で正規化)
222fn to_affine(p: ProjectivePoint) -> Option<AffinePoint> {
223    if p.is_infinity {
224        return None;
225    }
226    let z_inv = FpField::inv(p.z);
227    let z_inv2 = FpField::sqr(z_inv);
228    let z_inv3 = FpField::mul(z_inv, z_inv2);
229    let x = FpField::mul(p.x, z_inv2);
230    let y = FpField::mul(p.y, z_inv3);
231    Some(AffinePoint { x, y })
232}
233
234/// Montgomery ladder: constant-time scalar multiplication
235fn scalar_mul(k: U256, p: AffinePoint) -> ProjectivePoint {
236    let mut r0 = ProjectivePoint::infinity();
237    let mut r1 = ProjectivePoint::from_affine(p);
238
239    for i in (0..256u32).rev() {
240        if U256::bit(k, i) {
241            r0 = point_add(r0, r1);
242            r1 = point_double(r1);
243        } else {
244            r1 = point_add(r0, r1);
245            r0 = point_double(r0);
246        }
247    }
248    r0
249}
250
251/// 点が曲線上にあるか検証: y² = x³ + ax + b (mod p)
252fn is_on_curve(p: AffinePoint) -> bool {
253    let x = p.x;
254    let y = p.y;
255    let a = curve_a();
256    let b = curve_b();
257
258    let y2 = FpField::sqr(y);
259    let x3 = FpField::mul(FpField::sqr(x), x);
260    let ax = FpField::mul(a, x);
261    let rhs = FpField::add(FpField::add(x3, ax), b);
262
263    FpField::eq(y2, rhs)
264}
265
266// -----------------------------------------------------------------------
267// SEC1 エンコード/デコード
268// -----------------------------------------------------------------------
269
270/// アフィン点を SEC1 圧縮形式 (33バイト) にエンコード
271fn encode_sec1_compressed(p: AffinePoint) -> [u8; 33] {
272    let x_bytes = p.x.to_u256().to_be_bytes();
273    let y_val = p.y.to_u256();
274    let prefix = if y_val.l0 & 1 == 1 { 0x03u8 } else { 0x02u8 };
275    let mut out = [0u8; 33];
276    out[0] = prefix;
277    out[1..].copy_from_slice(&x_bytes);
278    out
279}
280
281/// SEC1 圧縮バイト列から AffinePoint に復元
282fn decode_sec1_compressed(bytes: &[u8]) -> Option<AffinePoint> {
283    if bytes.len() != 33 {
284        return None;
285    }
286    let prefix = bytes[0];
287    if prefix != 0x02 && prefix != 0x03 {
288        return None;
289    }
290    let x_bytes: [u8; 32] = bytes[1..].try_into().ok()?;
291    let x_u256 = U256::from_be_bytes(x_bytes);
292
293    // x < p の範囲チェック
294    if let core::cmp::Ordering::Less = U256::cmp(x_u256, P256Field::MODULUS) {
295        // ok
296    } else {
297        return None;
298    }
299
300    let x = FpField::from_u256(x_u256);
301    let a = curve_a();
302    let b = curve_b();
303
304    // y² = x³ + ax + b
305    let x3 = FpField::mul(FpField::sqr(x), x);
306    let ax = FpField::mul(a, x);
307    let rhs = FpField::add(FpField::add(x3, ax), b);
308
309    let y = FpField::sqrt(rhs, SQRT_EXP_P256)?;
310    let y_u256 = y.to_u256();
311
312    // prefix に合わせて y の奇偶を選択
313    let y_odd = y_u256.l0 & 1 == 1;
314    let want_odd = prefix == 0x03;
315
316    let y_final = if y_odd == want_odd {
317        y
318    } else {
319        FpField::neg(y)
320    };
321
322    let point = AffinePoint { x, y: y_final };
323
324    if !is_on_curve(point) {
325        return None;
326    }
327
328    Some(point)
329}
330
331// -----------------------------------------------------------------------
332// ECDSA 署名/検証
333// -----------------------------------------------------------------------
334
335/// s > n/2 なら n - s を返す (low-s 正規化)
336fn normalize_s(s: U256, n: U256) -> U256 {
337    let half_n = U256::shr1(n);
338    if let core::cmp::Ordering::Greater = U256::cmp(s, half_n) {
339        let (ns, _) = U256::sub(n, s);
340        ns
341    } else {
342        s
343    }
344}
345
346/// RFC 6979 + ECDSA 署名 (prehash)
347fn ecdsa_sign(secret_bytes: &[u8; 32], digest: &[u8; 32]) -> Option<[u8; 64]> {
348    let n = order_u256();
349    let g = AffinePoint {
350        x: generator_x(),
351        y: generator_y(),
352    };
353
354    let d = U256::from_be_bytes(*secret_bytes);
355
356    // RFC 6979 で決定論的 k を生成
357    let k = generate_k(secret_bytes, digest, &n);
358
359    // R = k * G
360    let r_proj = scalar_mul(k, g);
361    let r_affine = to_affine(r_proj)?;
362
363    // r = x_R mod n
364    let rx = r_affine.x.to_u256();
365    let r = if let core::cmp::Ordering::Less = U256::cmp(rx, n) {
366        rx
367    } else {
368        let (v, _) = U256::sub(rx, n);
369        v
370    };
371    if U256::is_zero(r) {
372        return None;
373    }
374
375    // e = digest as integer (mod n)
376    let e_raw = U256::from_be_bytes(*digest);
377    let e = if let core::cmp::Ordering::Less = U256::cmp(e_raw, n) {
378        e_raw
379    } else {
380        let (v, _) = U256::sub(e_raw, n);
381        v
382    };
383
384    // s = k^-1 * (e + r*d) mod n
385    let k_scalar = Scalar::from_u256(k);
386    let k_inv = Scalar::inv(k_scalar);
387    let r_scalar = Scalar::from_u256(r);
388    let d_scalar = Scalar::from_u256(d);
389    let e_scalar = Scalar::from_u256(e);
390
391    let rd = Scalar::mul(r_scalar, d_scalar);
392    let e_plus_rd = Scalar::add(e_scalar, rd);
393    let s_scalar = Scalar::mul(k_inv, e_plus_rd);
394    let s = s_scalar.to_u256();
395
396    if U256::is_zero(s) {
397        return None;
398    }
399
400    // low-s 正規化
401    let s = normalize_s(s, n);
402
403    let mut out = [0u8; 64];
404    out[..32].copy_from_slice(&r.to_be_bytes());
405    out[32..].copy_from_slice(&s.to_be_bytes());
406    Some(out)
407}
408
409/// ECDSA 検証 (prehash)
410fn ecdsa_verify(pubkey_sec1: &[u8; 33], digest: &[u8; 32], sig_bytes: &[u8; 64]) -> bool {
411    let n = order_u256();
412    let g = AffinePoint {
413        x: generator_x(),
414        y: generator_y(),
415    };
416
417    let r = U256::from_be_bytes(sig_bytes[..32].try_into().unwrap());
418    let s = U256::from_be_bytes(sig_bytes[32..].try_into().unwrap());
419
420    // r, s は [1, n-1] の範囲
421    if U256::is_zero(r) || U256::is_zero(s) {
422        return false;
423    }
424    if !matches!(U256::cmp(r, n), core::cmp::Ordering::Less) {
425        return false;
426    }
427    if !matches!(U256::cmp(s, n), core::cmp::Ordering::Less) {
428        return false;
429    }
430
431    // high-s 拒否
432    let half_n = U256::shr1(n);
433    if let core::cmp::Ordering::Greater = U256::cmp(s, half_n) {
434        return false;
435    }
436
437    // 公開鍵
438    let pubkey = match decode_sec1_compressed(pubkey_sec1) {
439        Some(p) => p,
440        None => return false,
441    };
442
443    // e = digest mod n
444    let e_raw = U256::from_be_bytes(*digest);
445    let e = if let core::cmp::Ordering::Less = U256::cmp(e_raw, n) {
446        e_raw
447    } else {
448        let (v, _) = U256::sub(e_raw, n);
449        v
450    };
451
452    // w = s^-1 mod n
453    let s_scalar = Scalar::from_u256(s);
454    let w_scalar = Scalar::inv(s_scalar);
455
456    // u1 = e*w mod n, u2 = r*w mod n
457    let e_scalar = Scalar::from_u256(e);
458    let r_scalar = Scalar::from_u256(r);
459
460    let u1 = Scalar::mul(e_scalar, w_scalar).to_u256();
461    let u2 = Scalar::mul(r_scalar, w_scalar).to_u256();
462
463    // R = u1*G + u2*Q
464    let u1g = scalar_mul(u1, g);
465    let u2q = scalar_mul(u2, pubkey);
466    let r_point = point_add(u1g, u2q);
467
468    let r_affine = match to_affine(r_point) {
469        Some(p) => p,
470        None => return false,
471    };
472
473    // r_x mod n == r
474    let r_x = r_affine.x.to_u256();
475    let r_x_mod_n = if let core::cmp::Ordering::Less = U256::cmp(r_x, n) {
476        r_x
477    } else {
478        let (v, _) = U256::sub(r_x, n);
479        v
480    };
481
482    U256::cmp(r_x_mod_n, r) == core::cmp::Ordering::Equal
483}
484
485// -----------------------------------------------------------------------
486// エラー型
487// -----------------------------------------------------------------------
488
489#[derive(Debug, Clone, PartialEq, Eq)]
490pub enum P256Error {
491    InvalidSecretKey,
492    InvalidPublicKey,
493    InvalidSignature,
494    InvalidHex(&'static str),
495}
496
497impl fmt::Display for P256Error {
498    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
499        match self {
500            Self::InvalidSecretKey => f.write_str("invalid secret key"),
501            Self::InvalidPublicKey => f.write_str("invalid public key"),
502            Self::InvalidSignature => f.write_str("invalid signature"),
503            Self::InvalidHex(message) => f.write_str(message),
504        }
505    }
506}
507
508impl std::error::Error for P256Error {}
509
510// -----------------------------------------------------------------------
511// Hex ユーティリティ
512// -----------------------------------------------------------------------
513
514fn hex_encode(bytes: &[u8]) -> String {
515    const HEX: &[u8; 16] = b"0123456789abcdef";
516    let mut s = String::with_capacity(bytes.len() * 2);
517    for &b in bytes {
518        s.push(HEX[(b >> 4) as usize] as char);
519        s.push(HEX[(b & 0x0f) as usize] as char);
520    }
521    s
522}
523
524fn hex_decode(hex: &str) -> Result<Vec<u8>, P256Error> {
525    let hex = hex.as_bytes();
526    if hex.len() % 2 != 0 {
527        return Err(P256Error::InvalidHex("odd length"));
528    }
529
530    let mut out = Vec::with_capacity(hex.len() / 2);
531    for chunk in hex.chunks(2) {
532        let high = hex_nibble(chunk[0])?;
533        let low = hex_nibble(chunk[1])?;
534        out.push((high << 4) | low);
535    }
536    Ok(out)
537}
538
539fn hex_nibble(b: u8) -> Result<u8, P256Error> {
540    match b {
541        b'0'..=b'9' => Ok(b - b'0'),
542        b'a'..=b'f' => Ok(b - b'a' + 10),
543        b'A'..=b'F' => Ok(b - b'A' + 10),
544        _ => Err(P256Error::InvalidHex("invalid character")),
545    }
546}
547
548// -----------------------------------------------------------------------
549// 秘密鍵バリデーション: 1 ≤ d < n
550// -----------------------------------------------------------------------
551
552fn validate_secret_key(bytes: &[u8; 32]) -> bool {
553    let d = U256::from_be_bytes(*bytes);
554    if U256::is_zero(d) {
555        return false;
556    }
557    matches!(U256::cmp(d, order_u256()), core::cmp::Ordering::Less)
558}
559
560// -----------------------------------------------------------------------
561// 公開 API
562// -----------------------------------------------------------------------
563
564#[derive(Debug, Clone, Copy, PartialEq, Eq)]
565pub struct SecretKey {
566    bytes: [u8; 32],
567}
568
569impl SecretKey {
570    pub fn generate() -> Result<Self, P256Error> {
571        let n = order_u256();
572        loop {
573            let mut buf = [0u8; 32];
574            getrandom::getrandom(&mut buf).map_err(|_| P256Error::InvalidSecretKey)?;
575            let k = U256::from_be_bytes(buf);
576            if !U256::is_zero(k) {
577                if let core::cmp::Ordering::Less = U256::cmp(k, n) {
578                    return Ok(Self { bytes: buf });
579                }
580            }
581        }
582    }
583
584    pub fn from_hex(hex: &str) -> Result<Self, P256Error> {
585        let bytes = hex_decode(hex)?;
586        if bytes.len() != 32 {
587            return Err(P256Error::InvalidHex("expected 64 hex characters"));
588        }
589
590        let mut arr = [0u8; 32];
591        arr.copy_from_slice(&bytes);
592        Self::from_bytes(arr)
593    }
594
595    pub fn from_bytes(bytes: [u8; 32]) -> Result<Self, P256Error> {
596        if !validate_secret_key(&bytes) {
597            return Err(P256Error::InvalidSecretKey);
598        }
599        Ok(Self { bytes })
600    }
601
602    pub fn to_hex(&self) -> String {
603        hex_encode(&self.bytes)
604    }
605
606    pub fn to_bytes(&self) -> [u8; 32] {
607        self.bytes
608    }
609
610    pub fn public_key(&self) -> Result<PublicKey, P256Error> {
611        let d = U256::from_be_bytes(self.bytes);
612        let g = AffinePoint {
613            x: generator_x(),
614            y: generator_y(),
615        };
616        let q_proj = scalar_mul(d, g);
617        let q = to_affine(q_proj).ok_or(P256Error::InvalidSecretKey)?;
618        let sec1_bytes = encode_sec1_compressed(q);
619        Ok(PublicKey { sec1_bytes })
620    }
621
622    pub fn sign_ecdsa_prehash(&self, digest32: [u8; 32]) -> Result<EcdsaSignature, P256Error> {
623        let sig_bytes = ecdsa_sign(&self.bytes, &digest32).ok_or(P256Error::InvalidSignature)?;
624        Ok(EcdsaSignature { bytes: sig_bytes })
625    }
626}
627
628#[derive(Debug, Clone, Copy, PartialEq, Eq)]
629pub struct PublicKey {
630    sec1_bytes: [u8; 33],
631}
632
633impl PublicKey {
634    pub fn from_hex(hex: &str) -> Result<Self, P256Error> {
635        let bytes = hex_decode(hex)?;
636        if bytes.len() != 33 {
637            return Err(P256Error::InvalidHex("expected 66 hex characters"));
638        }
639        Self::from_sec1_bytes(&bytes)
640    }
641
642    pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self, P256Error> {
643        if bytes.len() != 33 {
644            return Err(P256Error::InvalidPublicKey);
645        }
646        let point = decode_sec1_compressed(bytes).ok_or(P256Error::InvalidPublicKey)?;
647        let sec1_bytes = encode_sec1_compressed(point);
648        Ok(Self { sec1_bytes })
649    }
650
651    pub fn to_hex(&self) -> String {
652        hex_encode(&self.sec1_bytes)
653    }
654
655    pub fn to_sec1_bytes(&self) -> [u8; 33] {
656        self.sec1_bytes
657    }
658
659    pub fn verify_ecdsa_prehash(
660        &self,
661        digest32: [u8; 32],
662        sig: &EcdsaSignature,
663    ) -> Result<(), P256Error> {
664        if ecdsa_verify(&self.sec1_bytes, &digest32, &sig.bytes) {
665            Ok(())
666        } else {
667            Err(P256Error::InvalidSignature)
668        }
669    }
670}
671
672#[derive(Debug, Clone, Copy, PartialEq, Eq)]
673pub struct EcdsaSignature {
674    bytes: [u8; 64],
675}
676
677impl EcdsaSignature {
678    pub fn from_hex(hex: &str) -> Result<Self, P256Error> {
679        let bytes = hex_decode(hex)?;
680        if bytes.len() != 64 {
681            return Err(P256Error::InvalidHex("expected 128 hex characters"));
682        }
683
684        let mut arr = [0u8; 64];
685        arr.copy_from_slice(&bytes);
686        Ok(Self::from_bytes(arr))
687    }
688
689    pub fn from_bytes(bytes: [u8; 64]) -> Self {
690        Self { bytes }
691    }
692
693    pub fn to_hex(&self) -> String {
694        hex_encode(&self.bytes)
695    }
696
697    pub fn to_bytes(&self) -> [u8; 64] {
698        self.bytes
699    }
700}
701
702// -----------------------------------------------------------------------
703// ユニットテスト
704// -----------------------------------------------------------------------
705
706#[cfg(test)]
707mod tests {
708    use super::*;
709
710    fn hex_to_bytes32(s: &str) -> [u8; 32] {
711        let mut out = [0u8; 32];
712        for i in 0..32 {
713            out[i] = u8::from_str_radix(&s[i * 2..i * 2 + 2], 16).unwrap();
714        }
715        out
716    }
717
718    #[test]
719    fn generator_on_curve() {
720        let g = AffinePoint {
721            x: generator_x(),
722            y: generator_y(),
723        };
724        assert!(is_on_curve(g), "generator G must be on P-256 curve");
725    }
726
727    #[test]
728    fn secret_key_validation() {
729        assert!(SecretKey::from_bytes([0u8; 32]).is_err());
730        let n_bytes = order_u256().to_be_bytes();
731        assert!(SecretKey::from_bytes(n_bytes).is_err());
732        let (n_minus_1, _) = U256::sub(order_u256(), U256::ONE);
733        assert!(SecretKey::from_bytes(n_minus_1.to_be_bytes()).is_ok());
734    }
735
736    #[test]
737    fn public_key_derivation_k1() {
738        // k=1 → Q = G の x 座標が一致する
739        let mut sk_bytes = [0u8; 32];
740        sk_bytes[31] = 1;
741        let sk = SecretKey::from_bytes(sk_bytes).unwrap();
742        let pk = sk.public_key().unwrap();
743        let sec1 = pk.to_sec1_bytes();
744        let gx = generator_x().to_u256().to_be_bytes();
745        assert_eq!(&sec1[1..], &gx, "1*G should have generator x");
746    }
747
748    #[test]
749    fn sign_verify_roundtrip() {
750        let sk = SecretKey::generate().unwrap();
751        let pk = sk.public_key().unwrap();
752        let digest = [0x42u8; 32];
753        let sig = sk.sign_ecdsa_prehash(digest).unwrap();
754        pk.verify_ecdsa_prehash(digest, &sig).unwrap();
755    }
756
757    #[test]
758    fn verify_wrong_message_fails() {
759        let sk = SecretKey::generate().unwrap();
760        let pk = sk.public_key().unwrap();
761        let sig = sk.sign_ecdsa_prehash([0x11; 32]).unwrap();
762        assert!(pk.verify_ecdsa_prehash([0x22; 32], &sig).is_err());
763    }
764
765    #[test]
766    fn low_s_normalization() {
767        let sk = SecretKey::generate().unwrap();
768        let digest = [0x77u8; 32];
769        let sig = sk.sign_ecdsa_prehash(digest).unwrap();
770        let s = U256::from_be_bytes(sig.to_bytes()[32..].try_into().unwrap());
771        let n = order_u256();
772        let half_n = U256::shr1(n);
773        assert!(
774            matches!(
775                U256::cmp(s, half_n),
776                core::cmp::Ordering::Less | core::cmp::Ordering::Equal
777            ),
778            "s must be <= n/2"
779        );
780    }
781
782    #[test]
783    fn high_s_rejected_in_verify() {
784        let sk = SecretKey::generate().unwrap();
785        let pk = sk.public_key().unwrap();
786        let digest = [0x77u8; 32];
787        let sig = sk.sign_ecdsa_prehash(digest).unwrap();
788
789        // s の high-s 相当 = n - s を作成
790        let mut bytes = sig.to_bytes();
791        let s = U256::from_be_bytes(bytes[32..].try_into().unwrap());
792        let n = order_u256();
793        let (high_s, _) = U256::sub(n, s);
794        bytes[32..].copy_from_slice(&high_s.to_be_bytes());
795
796        let high_s_sig = EcdsaSignature::from_bytes(bytes);
797        let half_n = U256::shr1(n);
798        // high_s > half_n の場合のみ拒否
799        if let core::cmp::Ordering::Greater = U256::cmp(high_s, half_n) {
800            assert!(
801                pk.verify_ecdsa_prehash(digest, &high_s_sig).is_err(),
802                "high-S should be rejected"
803            );
804        }
805    }
806
807    #[test]
808    fn hex_roundtrip() {
809        let sk = SecretKey::generate().unwrap();
810        let pk = sk.public_key().unwrap();
811        let sig = sk.sign_ecdsa_prehash([0x55; 32]).unwrap();
812
813        assert_eq!(SecretKey::from_hex(&sk.to_hex()).unwrap(), sk);
814        assert_eq!(PublicKey::from_hex(&pk.to_hex()).unwrap(), pk);
815        assert_eq!(EcdsaSignature::from_hex(&sig.to_hex()).unwrap(), sig);
816    }
817
818    #[test]
819    fn deterministic_signatures() {
820        // 同じ秘密鍵 + ダイジェストで同じ署名 (RFC 6979)
821        let sk_bytes =
822            hex_to_bytes32("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
823        let sk = SecretKey::from_bytes(sk_bytes).unwrap();
824        let digest = [0x42u8; 32];
825        let sig1 = sk.sign_ecdsa_prehash(digest).unwrap();
826        let sig2 = sk.sign_ecdsa_prehash(digest).unwrap();
827        assert_eq!(sig1.to_bytes(), sig2.to_bytes());
828    }
829
830    #[test]
831    fn point_double_equals_point_add_self() {
832        // 2*G = G + G
833        let g = AffinePoint {
834            x: generator_x(),
835            y: generator_y(),
836        };
837        let g_proj = ProjectivePoint::from_affine(g);
838        let two_g_double = to_affine(point_double(g_proj)).unwrap();
839        let two_g_add = to_affine(point_add(g_proj, g_proj)).unwrap();
840        assert_eq!(two_g_double.x.to_u256(), two_g_add.x.to_u256());
841        assert_eq!(two_g_double.y.to_u256(), two_g_add.y.to_u256());
842    }
843
844    #[test]
845    fn sec1_roundtrip() {
846        let sk = SecretKey::generate().unwrap();
847        let pk = sk.public_key().unwrap();
848        let sec1 = pk.to_sec1_bytes();
849        let pk2 = PublicKey::from_sec1_bytes(&sec1).unwrap();
850        assert_eq!(pk, pk2);
851    }
852}