nx_rsa_pkcs1_v1_5_sha256.nx source
↩ module page · 142 lines · 5052 B
1// nx_rsa_pkcs1_v1_5_sha256.nx -- RSA-PKCS#1 v1.5 signature verify
2// with SHA-256 hash, for RSA-2048 keys.
3//
4// RFC 8017 ยง8.2.2 RSASSA-PKCS1-v1_5-VERIFY (k=256 bytes for RSA-2048):
5// 1. s -> m = s^e mod n (RSA modular exponentiation)
6// 2. Encode message into EM_test:
7// EM_test = 0x00 || 0x01 || PS (FF*202) || 0x00 || T || H
8// where T = SHA-256 DigestInfo DER prefix (19 bytes):
9// 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20
10// and H = SHA-256(message) (32 bytes).
11// 3. Convert m to 256-byte big-endian EM.
12// 4. Compare EM == EM_test byte-by-byte.
13//
14// API:
15// rsa_pkcs1_v1_5_sha256_verify(msg, msg_len, sig_bytes, n_2048, e_i64) -> verdict
16//
17// Sealed verdict:
18// NX_RSA_PKCS1_V15_OK valid signature
19// NX_RSA_PKCS1_V15_S_OUT_OF_RANGE s_int >= n
20// NX_RSA_PKCS1_V15_BAD_PAD padding (PS / leading bytes / T) mismatch
21// NX_RSA_PKCS1_V15_HASH_MISMATCH trailing 32 bytes != SHA-256(msg)
22//
23// Composes:
24// - sha256_digest
25// - u2048_load_be / u2048_store_be
26// - rsa2048_mod_exp
27//
28// license_tier: INDEPENDENT_REDERIVE
29// genealogy_id: international-research-sources/ietf/rfc_8017 + nist/fips_180_4
30// lineage_id: nishi_rsa_pkcs1_v1_5_sha256_q10
31
32// nx_safety_envelope:
33// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
34// sil_target: SIL2
35// evidence: [bulk_applied_2026-05-20, rsa2048-pkcs1v15-sha256-verify]
36// verdict: NOT_YET_EVALUATED
37
38import "nx_syscalls.nx"
39import "nx_sha256.nx"
40import "nx_u2048.nx"
41import "nx_rsa2048_mod_exp.nx"
42import "nx_rsa2048_mont.nx"
43
44const NX_RSA_PKCS1_V15_OK: i64 = 1
45const NX_RSA_PKCS1_V15_S_OUT_OF_RANGE: i64 = 2
46const NX_RSA_PKCS1_V15_BAD_PAD: i64 = 3
47const NX_RSA_PKCS1_V15_HASH_MISMATCH: i64 = 4
48const NX_RSA_PKCS1_V15_VERDICT_N: i64 = 5
49
50func rsa_pkcs1_v1_5_verdict_is_valid(v: i64) -> i64 {
51 if v < NX_RSA_PKCS1_V15_OK { return 0 }
52 if v >= NX_RSA_PKCS1_V15_VERDICT_N { return 0 }
53 return 1
54}
55
56// Returns 1 if byte b matches expected SHA-256 DigestInfo prefix byte
57// at position i (0..18), else 0.
58func rsa_pkcs1_sha256_di_byte(i: i64) -> i64 {
59 if i == 0 { return 0x30 }
60 if i == 1 { return 0x31 }
61 if i == 2 { return 0x30 }
62 if i == 3 { return 0x0d }
63 if i == 4 { return 0x06 }
64 if i == 5 { return 0x09 }
65 if i == 6 { return 0x60 }
66 if i == 7 { return 0x86 }
67 if i == 8 { return 0x48 }
68 if i == 9 { return 0x01 }
69 if i == 10 { return 0x65 }
70 if i == 11 { return 0x03 }
71 if i == 12 { return 0x04 }
72 if i == 13 { return 0x02 }
73 if i == 14 { return 0x01 }
74 if i == 15 { return 0x05 }
75 if i == 16 { return 0x00 }
76 if i == 17 { return 0x04 }
77 if i == 18 { return 0x20 }
78 return 0
79}
80
81// Verify RSA-PKCS#1 v1.5 signature with SHA-256.
82//
83// msg: message bytes (TBS for X.509 outer-sig verify)
84// msg_len: msg byte length
85// sig_bytes: 256-byte big-endian signature
86// n_2048: 64-limb LE U2048 modulus
87// e_i64: public exponent (typically 65537)
88//
89// Returns NX_RSA_PKCS1_V15_OK on valid, non-OK verdict otherwise.
90func rsa_pkcs1_v1_5_sha256_verify(msg: *u8, msg_len: i64,
91 sig_bytes: *u8,
92 n_2048: *i64, e_i64: i64) -> i64 {
93 // Step 1: load sig as u2048, compute m = s^e mod n.
94 let s_int: *i64 = u2048_alloc()
95 u2048_load_be(s_int, sig_bytes)
96 let m_int: *i64 = u2048_alloc()
97 let rc: i64 = rsa2048_mod_exp_mont(m_int, s_int, e_i64, n_2048)
98 if rc != NX_RSA2048_MONT_OK { return NX_RSA_PKCS1_V15_S_OUT_OF_RANGE }
99
100 // Step 2: convert m to 256-byte BE EM.
101 let em: *u8 = sys_mmap(256)
102 u2048_store_be(em, m_int)
103
104 // Step 3: compute SHA-256(msg).
105 let h: *u8 = sys_mmap(32)
106 sha256_digest(msg, msg_len, h)
107
108 // Step 4: byte-by-byte structural check of EM.
109 // EM[0] == 0x00
110 // EM[1] == 0x01
111 // EM[2..2+PS_LEN) == 0xFF (PS_LEN = 256 - 3 - 19 - 32 = 202)
112 // EM[2+PS_LEN] == 0x00
113 // EM[2+PS_LEN+1 .. 2+PS_LEN+1+19) == DigestInfo prefix (19 bytes)
114 // EM[2+PS_LEN+1+19 .. 256) == SHA-256(msg) (32 bytes)
115 if (em[0] & 0xff) != 0x00 { return NX_RSA_PKCS1_V15_BAD_PAD }
116 if (em[1] & 0xff) != 0x01 { return NX_RSA_PKCS1_V15_BAD_PAD }
117 let ps_len: i64 = 202
118 var i: i64 = 0
119 while i < ps_len {
120 if (em[2 + i] & 0xff) != 0xff { return NX_RSA_PKCS1_V15_BAD_PAD }
121 i = i + 1
122 }
123 if (em[2 + ps_len] & 0xff) != 0x00 { return NX_RSA_PKCS1_V15_BAD_PAD }
124 let di_off: i64 = 2 + ps_len + 1
125 i = 0
126 while i < 19 {
127 let expected: i64 = rsa_pkcs1_sha256_di_byte(i)
128 if (em[di_off + i] & 0xff) != (expected & 0xff) { return NX_RSA_PKCS1_V15_BAD_PAD }
129 i = i + 1
130 }
131 let h_off: i64 = di_off + 19
132 i = 0
133 while i < 32 {
134 if (em[h_off + i] & 0xff) != (h[i] & 0xff) { return NX_RSA_PKCS1_V15_HASH_MISMATCH }
135 i = i + 1
136 }
137 return NX_RSA_PKCS1_V15_OK
138}
139
140func main() -> i64 {
141 return 0
142}