nx_ed25519_point_test.nx source
↩ module page · 110 lines · 4148 B
1// nx_ed25519_point_test.nx -- KAT for Edwards-curve point decompression.
2//
3// Verifies:
4// A. Decompress RFC 8032 §7.1 TEST 1 pubkey -> resulting GeP3
5// satisfies the twisted Edwards equation (-X^2 + Y^2 == 1 + d*X^2*Y^2)
6// B. Decompress the basepoint (RFC 8032 §6 / RFC 7748 base point
7// encoded as Ed25519 pubkey) -> on curve
8// C. Sign-bit flip: decompress P, then decompress P with high
9// bit of byte 31 flipped -> X coordinates have opposite parity
10// (one is the negative of the other)
11// D. Y coordinate round-trips through fe_to_bytes
12//
13// expect_exit: 0
14// license_tier: ORIGINAL
15
16import "nx_syscalls.nx"
17import "nx_x25519.nx"
18import "nx_ed25519_field.nx"
19import "nx_ed25519_point.nx"
20
21func main() -> i64 {
22 // ---- Test A: RFC 8032 §7.1 TEST 1 pubkey ----
23 // d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a
24 let pk1: *u8 = sys_mmap(64)
25 pk1[0]=0xd7; pk1[1]=0x5a; pk1[2]=0x98; pk1[3]=0x01
26 pk1[4]=0x82; pk1[5]=0xb1; pk1[6]=0x0a; pk1[7]=0xb7
27 pk1[8]=0xd5; pk1[9]=0x4b; pk1[10]=0xfe; pk1[11]=0xd3
28 pk1[12]=0xc9; pk1[13]=0x64; pk1[14]=0x07; pk1[15]=0x3a
29 pk1[16]=0x0e; pk1[17]=0xe1; pk1[18]=0x72; pk1[19]=0xf3
30 pk1[20]=0xda; pk1[21]=0xa6; pk1[22]=0x23; pk1[23]=0x25
31 pk1[24]=0xaf; pk1[25]=0x02; pk1[26]=0x1a; pk1[27]=0x68
32 pk1[28]=0xf7; pk1[29]=0x07; pk1[30]=0x51; pk1[31]=0x1a
33
34 let p1: *GeP3 = ge_p3_alloc()
35 let v1: i64 = ge_p3_decompress(p1, pk1)
36 if v1 != NX_GE_VERDICT_OK { return 1 }
37 if ge_p3_on_curve(p1) != 1 { return 2 }
38
39 // ---- Test B: Ed25519 basepoint compressed encoding ----
40 // Basepoint Y coordinate per RFC 8032 §5.1: y = 4/5 mod p
41 // Encoded LE: 5866666666666666666666666666666666666666666666666666666666666666
42 // (Y bytes are all 0x66 with byte 0 = 0x58; sign bit of x = 0)
43 let bp: *u8 = sys_mmap(64)
44 bp[0]=0x58
45 var bi: i64 = 1
46 while bi < 32 {
47 bp[bi] = 0x66
48 bi = bi + 1
49 }
50 let p2: *GeP3 = ge_p3_alloc()
51 let v2: i64 = ge_p3_decompress(p2, bp)
52 if v2 != NX_GE_VERDICT_OK { return 10 }
53 if ge_p3_on_curve(p2) != 1 { return 11 }
54
55 // ---- Test C: sign-bit flip produces a different X ----
56 let pk1_flipped: *u8 = sys_mmap(64)
57 var fi: i64 = 0
58 while fi < 32 {
59 pk1_flipped[fi] = pk1[fi]
60 fi = fi + 1
61 }
62 pk1_flipped[31] = pk1_flipped[31] ^ 0x80 // flip sign-of-x bit
63 let p1_alt: *GeP3 = ge_p3_alloc()
64 let v3: i64 = ge_p3_decompress(p1_alt, pk1_flipped)
65 if v3 != NX_GE_VERDICT_OK { return 20 }
66 if ge_p3_on_curve(p1_alt) != 1 { return 21 }
67
68 // Y MUST be the same (only the sign bit of x was flipped)
69 if fe_canonical_equal(p1.Y, p1_alt.Y) != 1 { return 22 }
70
71 // X MUST differ (one is negative of the other)
72 if fe_canonical_equal(p1.X, p1_alt.X) == 1 { return 23 }
73
74 // Specifically: p1.X + p1_alt.X should equal 0 mod p (sums to zero)
75 let x_sum: *i64 = fe_alloc()
76 fe_add(x_sum, p1.X, p1_alt.X)
77 let zero: *i64 = fe_alloc()
78 fe_zero(zero)
79 if fe_canonical_equal(x_sum, zero) != 1 { return 24 }
80
81 // ---- Test D: Y round-trips through canonical encoding ----
82 // The Y stored in p1 should encode back to the same bytes
83 // (modulo the sign bit, which is X's not Y's).
84 let y_re: *u8 = sys_mmap(32)
85 fe_to_bytes(y_re, p1.Y)
86 var di: i64 = 0
87 while di < 31 {
88 if (y_re[di] & 0xff) != (pk1[di] & 0xff) { return 40 + di }
89 di = di + 1
90 }
91 // Byte 31: pubkey has sign bit; our Y encoding doesn't. Mask high bit.
92 if (y_re[31] & 0x7f) != (pk1[31] & 0x7f) { return 80 }
93
94 // ---- Test E: T == X * Y (extended-coord consistency) ----
95 let xy: *i64 = fe_alloc()
96 fe_mul(xy, p1.X, p1.Y)
97 if fe_canonical_equal(p1.T, xy) != 1 { return 90 }
98
99 // ---- Test F: Z == 1 ----
100 let one: *i64 = fe_alloc()
101 fe_one(one)
102 if fe_canonical_equal(p1.Z, one) != 1 { return 91 }
103
104 // ---- Test G: verdict gate ----
105 if nx_ge_verdict_is_valid(NX_GE_VERDICT_OK) != 1 { return 100 }
106 if nx_ge_verdict_is_valid(NX_GE_VERDICT_N) != 0 { return 101 }
107 if nx_ge_verdict_is_valid(0 - 1) != 0 { return 102 }
108
109 return 0
110}