nx_rsa_pkcs1_v1_5_sha256_4096.nx source
↩ module page · 123 lines · 5093 B
1// nx_rsa_pkcs1_v1_5_sha256_4096.nx -- RSA-PKCS#1 v1.5 signature verify
2// with SHA-256 hash, for RSA-4096 keys.
3//
4// Parallel to nx_rsa_pkcs1_v1_5_sha256 (the RSA-2048 variant);
5// kept as a separate module rather than parameterizing the existing
6// module so the existing 2048 callers continue to compile unchanged.
7//
8// RFC 8017 ยง8.2.2 RSASSA-PKCS1-v1_5-VERIFY (k=512 bytes for RSA-4096):
9// 1. s -> m = s^e mod n (4096-bit RSA modular exponentiation)
10// 2. Encode message into EM_test:
11// EM_test = 0x00 || 0x01 || PS (FF*458) || 0x00 || T || H
12// where T = SHA-256 DigestInfo DER prefix (19 bytes):
13// 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20
14// and H = SHA-256(message) (32 bytes).
15// PS_LEN = 512 - 3 - 19 - 32 = 458 bytes of 0xFF.
16// 3. Convert m to 512-byte big-endian EM.
17// 4. Compare EM == EM_test byte-by-byte.
18//
19// API:
20// rsa_pkcs1_v1_5_sha256_4096_verify(msg, msg_len, sig_bytes, n_4096, e_i64) -> verdict
21//
22// Sealed verdict (parallel to 2048-variant; same shape so cert-chain
23// verifier can map verdicts uniformly):
24// NX_RSA_PKCS1_V15_4096_OK valid signature
25// NX_RSA_PKCS1_V15_4096_S_OUT_OF_RANGE s_int >= n
26// NX_RSA_PKCS1_V15_4096_BAD_PAD padding mismatch
27// NX_RSA_PKCS1_V15_4096_HASH_MISMATCH trailing 32 bytes != SHA-256(msg)
28//
29// Composes:
30// - sha256_digest (already shipped)
31// - u4096_load_be / u4096_store_be (stone 1)
32// - rsa4096_mod_exp (stone 3)
33// - rsa_pkcs1_sha256_di_byte (shared with 2048 variant)
34//
35// Unblocks: L11 step 2d -> 2e (LE E7 -> ISRG Root X1 chain validates;
36// nx_acme daemon can drive state machine end-to-end against LE).
37//
38// license_tier: INDEPENDENT_REDERIVE
39// genealogy_id: international-research-sources/ietf/rfc_8017 + nist/fips_180_4
40// lineage_id: nishi_rsa_pkcs1_v1_5_sha256_4096_q1
41
42import "nx_syscalls.nx"
43import "nx_sha256.nx"
44import "nx_u4096.nx"
45import "nx_rsa4096_mod_exp.nx"
46import "nx_rsa4096_mont.nx" // leak-free Montgomery-4096 (KAT-gated byte-identical, scratch freed)
47import "nx_rsa_pkcs1_v1_5_sha256.nx" // for rsa_pkcs1_sha256_di_byte
48const NX_MAGIC_4096: i64 = 4096
49
50const NX_RSA_PKCS1_V15_4096_OK: i64 = 1
51const NX_RSA_PKCS1_V15_4096_S_OUT_OF_RANGE: i64 = 2
52const NX_RSA_PKCS1_V15_4096_BAD_PAD: i64 = 3
53const NX_RSA_PKCS1_V15_4096_HASH_MISMATCH: i64 = 4
54const NX_RSA_PKCS1_V15_4096_VERDICT_N: i64 = 5
55
56func rsa_pkcs1_v1_5_4096_verdict_is_valid(v: i64) -> i64 {
57 if v < NX_RSA_PKCS1_V15_4096_OK { return 0 }
58 if v >= NX_RSA_PKCS1_V15_4096_VERDICT_N { return 0 }
59 return 1
60}
61
62// Verify RSA-PKCS#1 v1.5 signature with SHA-256 over a 4096-bit RSA key.
63//
64// msg: message bytes (TBS for X.509 outer-sig verify)
65// msg_len: msg byte length
66// sig_bytes: 512-byte big-endian signature
67// n_4096: 128-limb LE U4096 modulus
68// e_i64: public exponent (typically 65537)
69//
70// Returns NX_RSA_PKCS1_V15_4096_OK on valid, non-OK verdict otherwise.
71func rsa_pkcs1_v1_5_sha256_4096_verify(msg: *u8, msg_len: i64,
72 sig_bytes: *u8,
73 n_4096: *i64, e_i64: i64) -> i64 {
74 // Step 1: load sig as u4096, compute m = s^e mod n.
75 let s_int: *i64 = u4096_alloc()
76 u4096_load_be(s_int, sig_bytes)
77 let m_int: *i64 = u4096_alloc()
78 let rc: i64 = rsa4096_mod_exp_mont(m_int, s_int, e_i64, n_4096) // leak-free Montgomery; KAT-proven == slow path
79 if rc != NX_RSA4096_MONT_OK { return NX_RSA_PKCS1_V15_4096_S_OUT_OF_RANGE }
80
81 // Step 2: convert m to 512-byte BE EM.
82 let em: *u8 = sys_mmap(512)
83 u4096_store_be(em, m_int)
84
85 // Step 3: compute SHA-256(msg).
86 let h: *u8 = sys_mmap(32)
87 sha256_digest(msg, msg_len, h)
88
89 // Step 4: byte-by-byte structural check of EM.
90 // EM[0] == 0x00
91 // EM[1] == 0x01
92 // EM[2..2+PS_LEN) == 0xFF (PS_LEN = 512 - 3 - 19 - 32 = 458)
93 // EM[2+PS_LEN] == 0x00
94 // EM[2+PS_LEN+1 .. 2+PS_LEN+1+19) == DigestInfo prefix (19 bytes)
95 // EM[2+PS_LEN+1+19 .. 512) == SHA-256(msg) (32 bytes)
96 if (em[0] & 0xff) != 0x00 { return NX_RSA_PKCS1_V15_4096_BAD_PAD }
97 if (em[1] & 0xff) != 0x01 { return NX_RSA_PKCS1_V15_4096_BAD_PAD }
98 let ps_len: i64 = 458
99 var i: i64 = 0
100 while i < ps_len {
101 if (em[2 + i] & 0xff) != 0xff { return NX_RSA_PKCS1_V15_4096_BAD_PAD }
102 i = i + 1
103 }
104 if (em[2 + ps_len] & 0xff) != 0x00 { return NX_RSA_PKCS1_V15_4096_BAD_PAD }
105 let di_off: i64 = 2 + ps_len + 1
106 i = 0
107 while i < 19 {
108 let expected: i64 = rsa_pkcs1_sha256_di_byte(i)
109 if (em[di_off + i] & 0xff) != (expected & 0xff) { return NX_RSA_PKCS1_V15_4096_BAD_PAD }
110 i = i + 1
111 }
112 let h_off: i64 = di_off + 19
113 i = 0
114 while i < 32 {
115 if (em[h_off + i] & 0xff) != (h[i] & 0xff) { return NX_RSA_PKCS1_V15_4096_HASH_MISMATCH }
116 i = i + 1
117 }
118 return NX_RSA_PKCS1_V15_4096_OK
119}
120
121func main() -> i64 {
122 return 0
123}