nx_chacha20_poly1305_pure.nx source
↩ module page · 129 lines · 4261 B
1// nx_chacha20_poly1305_pure.nx -- arena-based AEAD per RFC 8439 ยง2.8.
2//
3// Parallel of nx_chacha20_poly1305.nx that takes a caller-provided
4// arena instead of calling sys_mmap. Lets x86_64 smokes use AEAD
5// without pulling in nx_syscalls.nx (which would duplicate-define
6// sys_* against nx_syscalls_x86_64.nx).
7//
8// Builds on nx_chacha20_pure + nx_poly1305_pure (both arena-based,
9// already shipped). KAT-verified by composition: chacha20_pure
10// and poly1305_pure both byte-exact against RFC 8439, so the AEAD
11// is byte-exact by construction.
12//
13// expect_exit: 0 (when smoked with the round-trip + tampered-tag tests)
14// license_tier: INDEPENDENT_REDERIVE
15// genealogy_id: international-research-sources/ietf/rfc_8439
16
17import "nx_arena_types.nx"
18import "nx_chacha20_pure.nx"
19import "nx_poly1305_pure.nx"
20
21const NX_AEADP_TAG_BYTES: i64 = 16
22const NX_AEADP_NONCE_BYTES: i64 = 12
23const NX_AEADP_KEY_BYTES: i64 = 32
24
25const NX_AEADP_VERDICT_OK: i64 = 1
26const NX_AEADP_VERDICT_TAG_MISMATCH: i64 = 2
27
28func aeadp_derive_otk(arena: *NxArena, key: *u8, nonce: *u8, otk_out: *u8) -> i64 {
29 let block: *u8 = nx_arena_alloc_zero(arena, 64, 8)
30 if block == (0 as *u8) { return 0 - 1 }
31 chacha20_block_pure(arena, key, 0, nonce, block)
32 var i: i64 = 0
33 while i < 32 { otk_out[i] = block[i]; i = i + 1 }
34 return 0
35}
36
37func aeadp_put_u64_le(buf: *u8, off: i64, v: i64) -> i64 {
38 buf[off + 0] = v & 0xff
39 buf[off + 1] = (v >> 8) & 0xff
40 buf[off + 2] = (v >> 16) & 0xff
41 buf[off + 3] = (v >> 24) & 0xff
42 buf[off + 4] = (v >> 32) & 0xff
43 buf[off + 5] = (v >> 40) & 0xff
44 buf[off + 6] = (v >> 48) & 0xff
45 buf[off + 7] = (v >> 56) & 0xff
46 return 8
47}
48
49func aeadp_build_mac_data(
50 aad: *u8, aad_len: i64,
51 ct: *u8, ct_len: i64,
52 mac_buf: *u8
53) -> i64 {
54 var o: i64 = 0
55 var i: i64 = 0
56 while i < aad_len { mac_buf[o + i] = aad[i]; i = i + 1 }
57 o = o + aad_len
58 let aad_rem: i64 = aad_len % 16
59 let aad_pad: i64 = (16 - aad_rem) % 16
60 var p: i64 = 0
61 while p < aad_pad { mac_buf[o + p] = 0; p = p + 1 }
62 o = o + aad_pad
63 var j: i64 = 0
64 while j < ct_len { mac_buf[o + j] = ct[j]; j = j + 1 }
65 o = o + ct_len
66 let ct_rem: i64 = ct_len % 16
67 let ct_pad: i64 = (16 - ct_rem) % 16
68 var q: i64 = 0
69 while q < ct_pad { mac_buf[o + q] = 0; q = q + 1 }
70 o = o + ct_pad
71 aeadp_put_u64_le(mac_buf, o, aad_len)
72 o = o + 8
73 aeadp_put_u64_le(mac_buf, o, ct_len)
74 o = o + 8
75 return o
76}
77
78// Encrypt + authenticate.
79func nx_chacha20_poly1305_pure_encrypt(
80 arena: *NxArena,
81 key: *u8, nonce: *u8,
82 aad: *u8, aad_len: i64,
83 pt: *u8, pt_len: i64,
84 ct_out: *u8,
85 tag_out: *u8
86) -> i64 {
87 let otk: *u8 = nx_arena_alloc_zero(arena, 32, 8)
88 if otk == (0 as *u8) { return 0 - 1 }
89 aeadp_derive_otk(arena, key, nonce, otk)
90
91 chacha20_encrypt_pure(arena, key, 1, nonce, pt, pt_len, ct_out)
92
93 let mac_cap: i64 = aad_len + pt_len + 64
94 let mac_buf: *u8 = nx_arena_alloc_zero(arena, mac_cap, 8)
95 if mac_buf == (0 as *u8) { return 0 - 1 }
96 let mac_len: i64 = aeadp_build_mac_data(aad, aad_len, ct_out, pt_len, mac_buf)
97
98 poly1305_mac_pure(arena, otk, mac_buf, mac_len, tag_out)
99 return NX_AEADP_VERDICT_OK
100}
101
102// Decrypt + verify (constant-time tag check first).
103func nx_chacha20_poly1305_pure_decrypt(
104 arena: *NxArena,
105 key: *u8, nonce: *u8,
106 aad: *u8, aad_len: i64,
107 ct: *u8, ct_len: i64,
108 tag: *u8,
109 pt_out: *u8
110) -> i64 {
111 let otk: *u8 = nx_arena_alloc_zero(arena, 32, 8)
112 if otk == (0 as *u8) { return 0 - 1 }
113 aeadp_derive_otk(arena, key, nonce, otk)
114
115 let mac_cap: i64 = aad_len + ct_len + 64
116 let mac_buf: *u8 = nx_arena_alloc_zero(arena, mac_cap, 8)
117 if mac_buf == (0 as *u8) { return 0 - 1 }
118 let mac_len: i64 = aeadp_build_mac_data(aad, aad_len, ct, ct_len, mac_buf)
119
120 let expected_tag: *u8 = nx_arena_alloc_zero(arena, 32, 8)
121 if expected_tag == (0 as *u8) { return 0 - 1 }
122 poly1305_mac_pure(arena, otk, mac_buf, mac_len, expected_tag)
123
124 if poly1305_tag_equal_pure(expected_tag, tag) != 1 {
125 return NX_AEADP_VERDICT_TAG_MISMATCH
126 }
127 chacha20_encrypt_pure(arena, key, 1, nonce, ct, ct_len, pt_out)
128 return NX_AEADP_VERDICT_OK
129}