nx_p256_field_test.nx source
↩ module page · 179 lines · 5928 B
1// nx_p256_field_test.nx -- KAT for P-256 prime field add/sub/neg.
2//
3// Verifies modular arithmetic correctness using:
4// - 0 + 0, 1 + 1, p-1 + 1 (boundary wrap)
5// - p-1 + p-1 = p-2 (general wrap)
6// - p-1 - p-1 = 0
7// - 0 - 1 = p-1 (underflow + add-p)
8// - -0 = 0
9// - -1 = p-1
10// - -(p-1) = 1
11// - -(-a) = a (involutive)
12// - a + (-a) = 0 (definition)
13// - load_p produces the correct byte sequence
14//
15// expect_exit: 0
16// license_tier: ORIGINAL
17
18import "nx_syscalls.nx"
19import "nx_u256.nx"
20import "nx_p256_field.nx"
21
22func main() -> i64 {
23 let a: *i64 = u256_alloc()
24 let b: *i64 = u256_alloc()
25 let r: *i64 = u256_alloc()
26 let tmp: *i64 = u256_alloc()
27 let p: *i64 = u256_alloc()
28 p256_field_load_p(p)
29
30 // ---- Test A: load_p byte-exact ----
31 // BE bytes: FF FF FF FF 00 00 00 01 00 00 00 00 00 00 00 00
32 // 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF
33 let p_bytes: *u8 = sys_mmap(32)
34 u256_store_be(p_bytes, p)
35 if (p_bytes[0] & 0xff) != 0xFF { return 1 }
36 if (p_bytes[1] & 0xff) != 0xFF { return 2 }
37 if (p_bytes[2] & 0xff) != 0xFF { return 3 }
38 if (p_bytes[3] & 0xff) != 0xFF { return 4 }
39 if (p_bytes[4] & 0xff) != 0x00 { return 5 }
40 if (p_bytes[5] & 0xff) != 0x00 { return 6 }
41 if (p_bytes[6] & 0xff) != 0x00 { return 7 }
42 if (p_bytes[7] & 0xff) != 0x01 { return 8 }
43 if (p_bytes[15] & 0xff) != 0x00 { return 9 }
44 if (p_bytes[16] & 0xff) != 0x00 { return 10 }
45 if (p_bytes[20] & 0xff) != 0xFF { return 11 }
46 if (p_bytes[31] & 0xff) != 0xFF { return 12 }
47
48 // ---- Test B: 0 + 0 = 0 ----
49 p256_field_zero(a)
50 p256_field_zero(b)
51 p256_field_add(r, a, b)
52 if u256_is_zero(r) != 1 { return 20 }
53
54 // ---- Test C: 1 + 1 = 2 ----
55 p256_field_one(a)
56 p256_field_one(b)
57 p256_field_add(r, a, b)
58 if r[0] != 2 { return 21 }
59 var i: i64 = 1
60 while i < 8 {
61 if r[i] != 0 { return 22 }
62 i = i + 1
63 }
64
65 // ---- Test D: (p-1) + 1 = 0 (boundary wrap) ----
66 u256_copy(a, p)
67 u256_zero(b); b[0] = 1
68 p256_field_sub(a, a, b) // a = p - 1
69 // Verify a < p:
70 if u256_cmp(a, p) != (0 - 1) { return 30 }
71 p256_field_add(r, a, b) // r = (p-1) + 1 = p mod p = 0
72 if u256_is_zero(r) != 1 { return 31 }
73
74 // ---- Test E: (p-1) + (p-1) = p - 2 ----
75 // (compute via 2*(p-1) = 2p - 2 = -2 mod p = p - 2)
76 u256_copy(a, p)
77 u256_zero(b); b[0] = 1
78 p256_field_sub(a, a, b) // a = p - 1
79 p256_field_add(r, a, a) // r = 2*(p-1) mod p
80
81 let p_minus_2: *i64 = u256_alloc()
82 u256_copy(p_minus_2, p)
83 u256_zero(b); b[0] = 2
84 p256_field_sub(p_minus_2, p_minus_2, b)
85 if p256_field_eq(r, p_minus_2) != 1 { return 40 }
86
87 // ---- Test F: 0 - 1 = p - 1 (underflow + add-p) ----
88 p256_field_zero(a)
89 p256_field_one(b)
90 p256_field_sub(r, a, b)
91 let p_minus_1: *i64 = u256_alloc()
92 u256_copy(p_minus_1, p)
93 p256_field_one(tmp)
94 p256_field_sub(p_minus_1, p_minus_1, tmp)
95 if p256_field_eq(r, p_minus_1) != 1 { return 50 }
96
97 // ---- Test G: (p-1) - (p-1) = 0 ----
98 u256_copy(a, p_minus_1)
99 u256_copy(b, p_minus_1)
100 p256_field_sub(r, a, b)
101 if u256_is_zero(r) != 1 { return 60 }
102
103 // ---- Test H: -0 = 0 ----
104 p256_field_zero(a)
105 p256_field_neg(r, a)
106 if u256_is_zero(r) != 1 { return 70 }
107
108 // ---- Test I: -1 = p-1 ----
109 p256_field_one(a)
110 p256_field_neg(r, a)
111 if p256_field_eq(r, p_minus_1) != 1 { return 80 }
112
113 // ---- Test J: -(p-1) = 1 ----
114 u256_copy(a, p_minus_1)
115 p256_field_neg(r, a)
116 p256_field_one(tmp)
117 if p256_field_eq(r, tmp) != 1 { return 90 }
118
119 // ---- Test K: -(-a) = a (involutive) ----
120 u256_zero(a)
121 a[0] = 0x12345678; a[1] = 0xABCDEF01; a[3] = 0xCAFEBABE
122 // Ensure canonical (a < p): since a's top limb is 0 << p's top limb 0xFFFFFFFF, it's fine.
123 p256_field_neg(tmp, a)
124 p256_field_neg(r, tmp)
125 if p256_field_eq(r, a) != 1 { return 100 }
126
127 // ---- Test L: a + (-a) = 0 ----
128 u256_zero(a)
129 a[0] = 0x99887766; a[2] = 0x11223344; a[7] = 0x12345678
130 // Verify a < p
131 if u256_cmp(a, p) != (0 - 1) { return 110 }
132 p256_field_neg(tmp, a)
133 p256_field_add(r, a, tmp)
134 if u256_is_zero(r) != 1 { return 111 }
135
136 // ---- Test M: (p-1) + 2 = 1 (cross-boundary wrap +1) ----
137 u256_copy(a, p_minus_1)
138 u256_zero(b); b[0] = 2
139 p256_field_add(r, a, b)
140 p256_field_one(tmp)
141 if p256_field_eq(r, tmp) != 1 { return 120 }
142
143 // ---- Test N: a + b = b + a (commutative) ----
144 u256_zero(a)
145 a[0] = 0x55555555; a[3] = 0xAAAAAAAA; a[6] = 0x33333333
146 u256_zero(b)
147 b[1] = 0x12345678; b[4] = 0xDEADBEEF; b[7] = 0x77777777
148 // Ensure canonical
149 if u256_cmp(a, p) != (0 - 1) { return 130 }
150 if u256_cmp(b, p) != (0 - 1) { return 131 }
151 p256_field_add(r, a, b)
152 p256_field_add(tmp, b, a)
153 if p256_field_eq(r, tmp) != 1 { return 132 }
154
155 // ---- Test O: a + 0 = a, a - 0 = a, -0 == 0 again ----
156 u256_zero(a)
157 a[0] = 0xDEADBEEF; a[4] = 0xCAFEBABE
158 p256_field_zero(b)
159 p256_field_add(r, a, b)
160 if p256_field_eq(r, a) != 1 { return 140 }
161 p256_field_sub(r, a, b)
162 if p256_field_eq(r, a) != 1 { return 141 }
163
164 // ---- Test P: result always canonical (< p) ----
165 // Test a known case: (p-1) + (p-1) = p - 2 < p
166 u256_copy(a, p_minus_1)
167 p256_field_add(r, a, a)
168 if u256_cmp(r, p) != (0 - 1) { return 150 }
169
170 // ---- Test Q: verdict gate ----
171 if nx_p256_field_verdict_is_valid(NX_P256_FIELD_OK) != 1 { return 160 }
172 if nx_p256_field_verdict_is_valid(NX_P256_FIELD_BAD) != 1 { return 161 }
173 if nx_p256_field_verdict_is_valid(NX_P256_FIELD_VERDICT_N) != 0 { return 162 }
174 if nx_p256_field_verdict_is_valid(0) != 0 { return 163 }
175 if nx_p256_field_verdict_is_valid(0 - 1) != 0 { return 164 }
176 if nx_p256_field_verdict_is_valid(999) != 0 { return 165 }
177
178 return 0
179}