nx_ecdsa_sig_der_384.nx source
↩ module page · 117 lines · 3999 B
1// nx_ecdsa_sig_der_384.nx -- parse DER-encoded ECDSA-P384 signatures
2// into (r, s) U384 scalars.
3//
4// Parallel to nx_ecdsa_sig_der.nx (which targets U256 / P-256). The
5// DER shape is identical -- SEQUENCE { INTEGER r, INTEGER s } -- but
6// the magnitude width changes from 32 to 48 bytes:
7// - 48 bytes when leading bit is 0
8// - 49 bytes when leading bit is 1 (DER 0x00 sign prefix)
9// - fewer than 48 bytes when high bytes happen to be zero
10//
11// Public API:
12// nx_ecdsa_sig_parse_der_384(buf, len, out_r, out_s) -> verdict
13//
14// Sealed verdict enum mirrors nx_ecdsa_sig_der's; reuses the same
15// constants (NX_ECDSA_DER_*).
16//
17// Per Cardinals 9 (single-responsibility -- parse, not verify),
18// 12 (defensive at boundaries -- bounded reads), 23 (preamble
19// explains why 49-byte INTEGER is normal for P-384).
20//
21// license_tier: INDEPENDENT_REDERIVE
22// genealogy_id: international-research-sources/ietf/rfc_3279 + rfc_5480 + itu_t/x_690
23// lineage_id: nishi_ecdsa_sig_der_384_q10
24
25// nx_safety_envelope:
26// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
27// sil_target: SIL1
28// evidence: [bulk_applied_2026-05-20, ecdsa-sig-der-384-parse]
29// verdict: NOT_YET_EVALUATED
30
31import "nx_syscalls.nx"
32import "nx_u384.nx"
33import "nx_asn1.nx"
34import "nx_ecdsa_sig_der.nx"
35
36// Helper: load a 1-49 byte big-endian unsigned integer into a
37// 12-limb LE U384 buffer, zero-padding the high limbs as needed.
38// Strips a single leading 0x00 byte (the DER positive-prefix) when
39// len == 49.
40//
41// Returns 0 on success, NX_ECDSA_DER_LEN_TOO_BIG if len > 49,
42// or NX_ECDSA_DER_BAD_R if len <= 0.
43func ecdsa_int_load_384(out: *i64, bytes: *u8, off: i64, len: i64) -> i64 {
44 if len <= 0 { return NX_ECDSA_DER_BAD_R }
45 if len > 49 { return NX_ECDSA_DER_LEN_TOO_BIG }
46
47 var skip: i64 = 0
48 var elen: i64 = len
49 if len == 49 {
50 if (bytes[off] & 0xff) != 0 { return NX_ECDSA_DER_LEN_TOO_BIG }
51 skip = 1
52 elen = 48
53 }
54
55 u384_zero(out)
56 let padded: *u8 = sys_mmap(48)
57 var k: i64 = 0
58 while k < 48 { padded[k] = 0; k = k + 1 }
59 let pad_off: i64 = 48 - elen
60 k = 0
61 while k < elen {
62 padded[pad_off + k] = bytes[off + skip + k]
63 k = k + 1
64 }
65 u384_load_be(out, padded)
66 return 0
67}
68
69// Parse a DER-encoded ECDSA signature (SEQUENCE { INTEGER r,
70// INTEGER s }) into two 12-limb LE U384 buffers.
71//
72// Returns NX_ECDSA_DER_OK on success, or a non-OK verdict.
73func nx_ecdsa_sig_parse_der_384(buf: *u8, len: i64,
74 out_r: *i64, out_s: *i64) -> i64 {
75 let c_raw: *u8 = sys_mmap(32)
76 let c: *Asn1Cursor = c_raw as *Asn1Cursor
77 asn1_cursor_init(c, len)
78
79 let outer_len_p: *i64 = sys_mmap(16) as *i64
80 let rc1: i64 = asn1_expect_tag(buf, c, ASN1_SEQUENCE, outer_len_p)
81 if rc1 < 0 { return NX_ECDSA_DER_BAD_OUTER }
82 let outer_end: i64 = c.pos + *outer_len_p
83 if outer_end > len { return NX_ECDSA_DER_BAD_OUTER }
84
85 let r_len_p: *i64 = sys_mmap(16) as *i64
86 let rc2: i64 = asn1_expect_tag(buf, c, ASN1_INTEGER, r_len_p)
87 if rc2 < 0 { return NX_ECDSA_DER_BAD_R }
88 let r_off: i64 = c.pos
89 let r_len: i64 = *r_len_p
90 c.pos = c.pos + r_len
91 if c.pos > outer_end { return NX_ECDSA_DER_BAD_R }
92
93 let load_r: i64 = ecdsa_int_load_384(out_r, buf, r_off, r_len)
94 if load_r != 0 { return load_r }
95
96 let s_len_p: *i64 = sys_mmap(16) as *i64
97 let rc3: i64 = asn1_expect_tag(buf, c, ASN1_INTEGER, s_len_p)
98 if rc3 < 0 { return NX_ECDSA_DER_BAD_S }
99 let s_off: i64 = c.pos
100 let s_len: i64 = *s_len_p
101 c.pos = c.pos + s_len
102 if c.pos > outer_end { return NX_ECDSA_DER_BAD_S }
103
104 let load_s: i64 = ecdsa_int_load_384(out_s, buf, s_off, s_len)
105 if load_s != 0 {
106 if load_s == NX_ECDSA_DER_BAD_R { return NX_ECDSA_DER_BAD_S }
107 return load_s
108 }
109
110 if c.pos != outer_end { return NX_ECDSA_DER_TRAILING }
111
112 return NX_ECDSA_DER_OK
113}
114
115func main() -> i64 {
116 return 0
117}