nx_rsa_pkcs1_v1_5_sha512.nx source
↩ module page · 133 lines · 5178 B
1// nx_rsa_pkcs1_v1_5_sha512.nx -- RSA-PKCS#1 v1.5 signature verify with
2// SHA-512, for RSA-2048 AND RSA-4096 keys.
3//
4// Parallel to nx_rsa_pkcs1_v1_5_sha256{,_4096} and nx_rsa_pkcs1_v1_5_sha384;
5// authored 2026-07-25 because publicreporting.cftc.gov signs its TLS-1.2
6// ServerKeyExchange with sha512WithRSAEncryption (signature_algorithms
7// hash=6 sig=1) and our TLS-1.2 client REFUSED it by name -- the last thing
8// blocking CFTC Commitments-of-Traders positioning data.
9//
10// Differs from the SHA-384 variant ONLY in: hash = SHA-512 (64 bytes), the
11// DigestInfo DER prefix, and PS length. RSA mod-exp is REUSED verbatim -- no
12// new bignum or padding logic is introduced here.
13//
14// RFC 8017 8.2.2: s -> m = s^e mod n; EM = 00 01 PS(FF..) 00 T H,
15// T = SHA-512 DigestInfo prefix (19 bytes), RFC 8017 A.2.4:
16// 30 51 30 0d 06 09 60 86 48 01 65 03 04 02 03 05 00 04 40
17// (vs SHA-384's ... 04 02 02 05 00 04 30 -- the OID's final byte is 03
18// not 02, and the digest OCTET STRING length is 0x40=64 not 0x30=48)
19// H = SHA-512(message) (64 bytes).
20// PS_LEN = 256-3-19-64 = 170 (RSA-2048) / 512-3-19-64 = 426 (RSA-4096).
21//
22// license_tier: INDEPENDENT_REDERIVE
23import "nx_syscalls.nx"
24import "nx_sha512.nx"
25import "nx_u2048.nx"
26import "nx_rsa2048_mod_exp.nx"
27import "nx_rsa2048_mont.nx"
28import "nx_u4096.nx"
29import "nx_rsa4096_mod_exp.nx"
30
31const NX_RSA_PKCS1_V15_512_OK: i64 = 1
32const NX_RSA_PKCS1_V15_512_S_OUT_OF_RANGE: i64 = 2
33const NX_RSA_PKCS1_V15_512_BAD_PAD: i64 = 3
34const NX_RSA_PKCS1_V15_512_HASH_MISMATCH: i64 = 4
35const NX_RSA_PKCS1_V15_512_VERDICT_N: i64 = 5
36
37const NX_RSA512_DI_LEN: i64 = 19
38const NX_RSA512_HASH_LEN: i64 = 64
39const NX_RSA512_PS_2048: i64 = 170
40const NX_RSA512_PS_4096: i64 = 426
41
42func nx_rsa_pkcs1_v15_512_verdict_is_valid(v: i64) -> i64 {
43 if v < NX_RSA_PKCS1_V15_512_OK { return 0 }
44 if v >= NX_RSA_PKCS1_V15_512_VERDICT_N { return 0 }
45 return 1
46}
47
48// SHA-512 DigestInfo DER prefix, byte i (RFC 8017 A.2.4).
49func rsa_pkcs1_sha512_di_byte(i: i64) -> i64 {
50 if i == 0 { return 0x30 }
51 if i == 1 { return 0x51 }
52 if i == 2 { return 0x30 }
53 if i == 3 { return 0x0d }
54 if i == 4 { return 0x06 }
55 if i == 5 { return 0x09 }
56 if i == 6 { return 0x60 }
57 if i == 7 { return 0x86 }
58 if i == 8 { return 0x48 }
59 if i == 9 { return 0x01 }
60 if i == 10 { return 0x65 }
61 if i == 11 { return 0x03 }
62 if i == 12 { return 0x04 }
63 if i == 13 { return 0x02 }
64 if i == 14 { return 0x03 }
65 if i == 15 { return 0x05 }
66 if i == 16 { return 0x00 }
67 if i == 17 { return 0x04 }
68 if i == 18 { return 0x40 }
69 return 0
70}
71
72// Shared EM structural check given a recovered EM, its total byte length,
73// the precomputed SHA-512 of the message, and the PS length.
74func _rsa512_check_em(em: *u8, em_len: i64, h: *u8, ps_len: i64) -> i64 {
75 if (em[0] & 0xff) != 0x00 { return NX_RSA_PKCS1_V15_512_BAD_PAD }
76 if (em[1] & 0xff) != 0x01 { return NX_RSA_PKCS1_V15_512_BAD_PAD }
77 var i: i64 = 0
78 while i < ps_len {
79 if (em[2 + i] & 0xff) != 0xff { return NX_RSA_PKCS1_V15_512_BAD_PAD }
80 i = i + 1
81 }
82 if (em[2 + ps_len] & 0xff) != 0x00 { return NX_RSA_PKCS1_V15_512_BAD_PAD }
83 let di_off: i64 = 2 + ps_len + 1
84 i = 0
85 while i < NX_RSA512_DI_LEN {
86 let expected: i64 = rsa_pkcs1_sha512_di_byte(i)
87 if (em[di_off + i] & 0xff) != (expected & 0xff) { return NX_RSA_PKCS1_V15_512_BAD_PAD }
88 i = i + 1
89 }
90 let h_off: i64 = di_off + NX_RSA512_DI_LEN
91 i = 0
92 while i < NX_RSA512_HASH_LEN {
93 if (em[h_off + i] & 0xff) != (h[i] & 0xff) { return NX_RSA_PKCS1_V15_512_HASH_MISMATCH }
94 i = i + 1
95 }
96 return NX_RSA_PKCS1_V15_512_OK
97}
98
99// RSA-2048: sig_bytes is 256 bytes BE, n_2048 is a 64-limb LE U2048.
100func rsa_pkcs1_v1_5_sha512_verify(msg: *u8, msg_len: i64,
101 sig_bytes: *u8,
102 n_2048: *i64, e_i64: i64) -> i64 {
103 let s_int: *i64 = u2048_alloc()
104 u2048_load_be(s_int, sig_bytes)
105 let m_int: *i64 = u2048_alloc()
106 let rc: i64 = rsa2048_mod_exp_mont(m_int, s_int, e_i64, n_2048)
107 if rc != NX_RSA2048_MONT_OK { return NX_RSA_PKCS1_V15_512_S_OUT_OF_RANGE }
108 let em: *u8 = sys_mmap(256)
109 u2048_store_be(em, m_int)
110 let h: *u8 = sys_mmap(64)
111 sha512_digest(msg, msg_len, h)
112 return _rsa512_check_em(em, 256, h, NX_RSA512_PS_2048)
113}
114
115// RSA-4096: sig_bytes is 512 bytes BE, n_4096 is a 128-limb LE U4096.
116func rsa_pkcs1_v1_5_sha512_4096_verify(msg: *u8, msg_len: i64,
117 sig_bytes: *u8,
118 n_4096: *i64, e_i64: i64) -> i64 {
119 let s_int: *i64 = u4096_alloc()
120 u4096_load_be(s_int, sig_bytes)
121 let m_int: *i64 = u4096_alloc()
122 let rc: i64 = rsa4096_mod_exp(m_int, s_int, e_i64, n_4096)
123 if rc != NX_RSA4096_MOD_EXP_OK { return NX_RSA_PKCS1_V15_512_S_OUT_OF_RANGE }
124 let em: *u8 = sys_mmap(512)
125 u4096_store_be(em, m_int)
126 let h: *u8 = sys_mmap(64)
127 sha512_digest(msg, msg_len, h)
128 return _rsa512_check_em(em, 512, h, NX_RSA512_PS_4096)
129}
130
131func main() -> i64 {
132 return 0
133}