code wiki / (root) / nx_p256_field_test.nx

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}