Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
aes128.test.cpp
Go to the documentation of this file.
1#include "aes128.hpp"
7
8#include <gtest/gtest.h>
9
10using namespace bb;
11
12// Helper function to create field element as either constant or witness
23
24// Helper function to convert byte array to uint256_t
26{
27 uint256_t converted(0);
28 for (uint64_t i = 0; i < 16; ++i) {
29 uint256_t to_add = uint256_t((uint64_t)(data[i])) << uint256_t((15 - i) * 8);
30 converted += to_add;
31 }
32 return converted;
33}
34
35// Test function for a specific combination of witness/constant inputs
36void test_aes128_combination(bool key_as_witness, bool iv_as_witness, bool input_as_witness)
37{
39
40 uint8_t key[16]{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
41 uint8_t out[64]{ 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d,
42 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2,
43 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16,
44 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 };
45 uint8_t iv[16]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
46 uint8_t in[64]{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
47 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
48 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
49 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 };
50
52
53 // Create input blocks with specified witness/constant configuration
54 std::vector<field_pt> in_field;
55 for (size_t i = 0; i < 4; ++i) {
56 in_field.push_back(create_field_element(builder, convert_bytes_to_uint256(in + i * 16), input_as_witness));
57 }
58
59 // Create key and IV with specified witness/constant configuration
61 field_pt iv_field = create_field_element(builder, convert_bytes_to_uint256(iv), iv_as_witness);
62
63 // Expected results
64 std::vector<fr> expected{ convert_bytes_to_uint256(out),
67 convert_bytes_to_uint256(out + 48) };
68
69 // Run AES encryption
70 const auto result = stdlib::aes128::encrypt_buffer_cbc(in_field, iv_field, key_field);
71
72 // Verify results
73 for (size_t i = 0; i < 4; ++i) {
74 EXPECT_EQ(result[i].get_value(), expected[i])
75 << "Combination: key=" << (key_as_witness ? "witness" : "constant")
76 << ", iv=" << (iv_as_witness ? "witness" : "constant")
77 << ", input=" << (input_as_witness ? "witness" : "constant");
78 }
79
80 // Verify circuit is valid (only if there are witnesses)
81 if (key_as_witness || iv_as_witness || input_as_witness) {
82 bool proof_result = CircuitChecker::check(builder);
83 EXPECT_EQ(proof_result, true) << "Circuit check failed for combination: key="
84 << (key_as_witness ? "witness" : "constant")
85 << ", iv=" << (iv_as_witness ? "witness" : "constant")
86 << ", input=" << (input_as_witness ? "witness" : "constant");
87 }
88}
89
90// Test function for mixed input blocks (some constant, some witness)
91void test_aes128_mixed_input(bool key_as_witness, bool iv_as_witness, const std::vector<bool>& input_block_config)
92{
94
95 uint8_t key[16]{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
96 uint8_t out[64]{ 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d,
97 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2,
98 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16,
99 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 };
100 uint8_t iv[16]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
101 uint8_t in[64]{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
102 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
103 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
104 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 };
105
107
108 // Create input blocks with mixed witness/constant configuration
109 std::vector<field_pt> in_field;
110 for (size_t i = 0; i < input_block_config.size(); ++i) {
111 in_field.push_back(create_field_element(builder, convert_bytes_to_uint256(in + i * 16), input_block_config[i]));
112 }
113
114 // Create key and IV with specified witness/constant configuration
116 field_pt iv_field = create_field_element(builder, convert_bytes_to_uint256(iv), iv_as_witness);
117
118 // Expected results
119 std::vector<fr> expected;
120 for (size_t i = 0; i < input_block_config.size(); ++i) {
121 expected.push_back(convert_bytes_to_uint256(out + i * 16));
122 }
123
124 // Run AES encryption
125 const auto result = stdlib::aes128::encrypt_buffer_cbc(in_field, iv_field, key_field);
126
127 // Verify results
128 for (size_t i = 0; i < result.size(); ++i) {
129 EXPECT_EQ(result[i].get_value(), expected[i]) << "Mixed input test failed for block " << i;
130 }
131
132 // Verify circuit is valid (only if there are witnesses)
133 bool has_witness = key_as_witness || iv_as_witness;
134 for (bool is_witness : input_block_config) {
135 if (is_witness) {
136 has_witness = true;
137 break;
138 }
139 }
140
141 if (has_witness) {
142 bool proof_result = CircuitChecker::check(builder);
143 EXPECT_EQ(proof_result, true) << "Circuit check failed for mixed input test";
144 }
145}
146
147TEST(stdlib_aes128, encrypt_64_bytes_all_witness)
148{
149 test_aes128_combination(true, true, true);
150}
151
152TEST(stdlib_aes128, encrypt_64_bytes_all_constant)
153{
154 test_aes128_combination(false, false, false);
155}
156
157TEST(stdlib_aes128, encrypt_64_bytes_key_witness_iv_constant_input_constant)
158{
159 test_aes128_combination(true, false, false);
160}
161
162TEST(stdlib_aes128, encrypt_64_bytes_key_constant_iv_witness_input_constant)
163{
164 test_aes128_combination(false, true, false);
165}
166
167TEST(stdlib_aes128, encrypt_64_bytes_key_constant_iv_constant_input_witness)
168{
169 test_aes128_combination(false, false, true);
170}
171
172TEST(stdlib_aes128, encrypt_64_bytes_key_witness_iv_witness_input_constant)
173{
174 test_aes128_combination(true, true, false);
175}
176
177TEST(stdlib_aes128, encrypt_64_bytes_key_witness_iv_constant_input_witness)
178{
179 test_aes128_combination(true, false, true);
180}
181
182TEST(stdlib_aes128, encrypt_64_bytes_key_constant_iv_witness_input_witness)
183{
184 test_aes128_combination(false, true, true);
185}
186
187// Original test for backward compatibility
188TEST(stdlib_aes128, encrypt_64_bytes_original)
189{
192
193 uint8_t key[16]{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
194 uint8_t out[64]{ 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d,
195 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2,
196 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16,
197 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 };
198 uint8_t iv[16]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
199 uint8_t in[64]{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
200 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
201 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
202 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 };
203
204 const auto convert_bytes = [](uint8_t* data) {
205 uint256_t converted(0);
206 for (uint64_t i = 0; i < 16; ++i) {
207 uint256_t to_add = uint256_t((uint64_t)(data[i])) << uint256_t((15 - i) * 8);
208 converted += to_add;
209 }
210 return converted;
211 };
212
214
215 std::vector<field_pt> in_field{
216 witness_pt(&builder, fr(convert_bytes(in))),
217 witness_pt(&builder, fr(convert_bytes(in + 16))),
218 witness_pt(&builder, fr(convert_bytes(in + 32))),
219 witness_pt(&builder, fr(convert_bytes(in + 48))),
220 };
221
222 field_pt key_field(witness_pt(&builder, fr(convert_bytes(key))));
223 field_pt iv_field(witness_pt(&builder, fr(convert_bytes(iv))));
224
225 std::vector<fr> expected{
226 convert_bytes(out), convert_bytes(out + 16), convert_bytes(out + 32), convert_bytes(out + 48)
227 };
228
229 const auto result = stdlib::aes128::encrypt_buffer_cbc(in_field, iv_field, key_field);
230
231 for (size_t i = 0; i < 4; ++i) {
232 EXPECT_EQ(result[i].get_value(), expected[i]);
233 }
234
235 std::cout << "num gates = " << builder.get_num_finalized_gates_inefficient() << std::endl;
236
237 bool proof_result = CircuitChecker::check(builder);
238 EXPECT_EQ(proof_result, true);
239}
240
241// Mixed input tests - different combinations of constant/witness blocks
242TEST(stdlib_aes128, encrypt_64_bytes_mixed_input_first_witness_rest_constant)
243{
244 test_aes128_mixed_input(false, false, { true, false, false, false });
245}
246
247TEST(stdlib_aes128, encrypt_64_bytes_mixed_input_alternating_witness_constant)
248{
249 test_aes128_mixed_input(false, false, { true, false, true, false });
250}
251
252TEST(stdlib_aes128, encrypt_64_bytes_mixed_input_first_constant_rest_witness)
253{
254 test_aes128_mixed_input(false, false, { false, true, true, true });
255}
256
257TEST(stdlib_aes128, encrypt_64_bytes_mixed_input_key_witness_mixed_blocks)
258{
259 test_aes128_mixed_input(true, false, { true, false, true, false });
260}
261
262TEST(stdlib_aes128, encrypt_64_bytes_mixed_input_iv_witness_mixed_blocks)
263{
264 test_aes128_mixed_input(false, true, { false, true, false, true });
265}
266
267TEST(stdlib_aes128, encrypt_64_bytes_mixed_input_key_iv_witness_mixed_blocks)
268{
269 test_aes128_mixed_input(true, true, { true, false, true, false });
270}
271
272TEST(stdlib_aes128, encrypt_64_bytes_mixed_input_all_witness_blocks)
273{
274 test_aes128_mixed_input(false, false, { true, true, true, true });
275}
276
277TEST(stdlib_aes128, encrypt_64_bytes_mixed_input_all_constant_blocks)
278{
279 test_aes128_mixed_input(false, false, { false, false, false, false });
280}
281
282// ============================================================================
283// Sparse Form XOR Tests
284// ============================================================================
285// In AES circuits, XOR is performed via addition in base-9 sparse form:
286// - Each bit of a byte becomes a digit (0 or 1) in a base-9 number
287// - Adding two sparse values: digits become 0, 1, or 2
288// - Normalization maps: even digits → 0, odd digits → 1 (exactly XOR semantics)
289//
290// This allows XOR to be computed as: sparse(a) + sparse(b), then normalize
291// ============================================================================
292
293constexpr uint64_t AES_SPARSE_BASE = 9;
294
300TEST(stdlib_aes128_sparse, sparse_form_roundtrip)
301{
302 // Test all possible byte values
303 for (uint64_t byte = 0; byte < 256; ++byte) {
304 uint256_t sparse = numeric::map_into_sparse_form<AES_SPARSE_BASE>(byte);
305 uint64_t recovered = numeric::map_from_sparse_form<AES_SPARSE_BASE>(sparse);
306 EXPECT_EQ(recovered, byte) << "Roundtrip failed for byte 0x" << std::hex << byte;
307 }
308}
309
315TEST(stdlib_aes128_sparse, sparse_addition_equals_xor_native)
316{
317 // Test a variety of byte pairs
319 { 0x00, 0x00 }, // 0 XOR 0 = 0
320 { 0xFF, 0x00 }, // FF XOR 0 = FF
321 { 0xFF, 0xFF }, // FF XOR FF = 0
322 { 0xAA, 0x55 }, // Alternating bits: AA XOR 55 = FF
323 { 0x0F, 0xF0 }, // 0F XOR F0 = FF
324 { 0x12, 0x34 }, // Random: 12 XOR 34 = 26
325 { 0x2b, 0x6b }, // From AES test vectors: key[0] XOR plaintext[0]
326 };
327
328 for (const auto& [a, b] : test_cases) {
329 uint8_t expected_xor = a ^ b;
330
331 // Convert to sparse form
332 uint256_t sparse_a = numeric::map_into_sparse_form<AES_SPARSE_BASE>(a);
333 uint256_t sparse_b = numeric::map_into_sparse_form<AES_SPARSE_BASE>(b);
334
335 // Add in sparse form (this is the circuit operation)
336 uint256_t sparse_sum = sparse_a + sparse_b;
337
338 // The sum needs normalization. In the circuit, this is done via plookup.
339 // For native testing, we can verify digit-by-digit:
340 // Each digit d in sparse_sum should normalize to (d % 2)
341 uint256_t normalized = 0;
342 uint256_t power = 1;
343 uint256_t temp = sparse_sum;
344
345 for (size_t i = 0; i < 8; ++i) {
346 uint64_t digit = (temp % AES_SPARSE_BASE).data[0];
347 uint64_t normalized_digit = digit % 2; // even→0, odd→1
348 normalized += power * normalized_digit;
349 power *= AES_SPARSE_BASE;
350 temp /= AES_SPARSE_BASE;
351 }
352
353 // Convert normalized result back to byte
354 uint64_t actual_xor = numeric::map_from_sparse_form<AES_SPARSE_BASE>(normalized);
355
356 EXPECT_EQ(actual_xor, expected_xor) << "Sparse XOR failed for 0x" << std::hex << (int)a << " ^ 0x" << (int)b
357 << ": expected 0x" << (int)expected_xor << ", got 0x" << actual_xor;
358 }
359}
360
368TEST(stdlib_aes128_sparse, sparse_form_lookup_table)
369{
373
375
376 // Take an input to the AES (16 bytes)
377 uint8_t lhs[16] = {
378 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
379 };
380 uint8_t rhs[16] = {
381 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
382 };
383
384 uint8_t expected_xor[16];
385 for (size_t i = 0; i < 16; ++i) {
386 expected_xor[i] = lhs[i] ^ rhs[i];
387 }
388
389 field_ct expected_xor_field = witness_ct(&builder, fr(convert_bytes_to_uint256(expected_xor)));
390
391 // pack the 16 bytes into a field_ct
394
395 // Now use the AES input table to convert lhs and rhs to sparse form using the AES input table
398
399 // Add the sparse fields together
400 for (size_t i = 0; i < 16; ++i) {
401 lhs_sparse_fields[i] = lhs_sparse_fields[i] + rhs_sparse_fields[i];
402 // Normalize the result
403 lhs_sparse_fields[i] = plookup_read::read_from_1_to_2_table(plookup::AES_NORMALIZE, lhs_sparse_fields[i]);
404 }
405 auto output_bytes = stdlib::aes128::convert_from_sparse_bytes(&builder, std::span{ lhs_sparse_fields });
406
407 output_bytes.assert_equal(expected_xor_field);
408
409 EXPECT_TRUE(CircuitChecker::check(builder));
410 EXPECT_FALSE(builder.failed());
411}
412
418TEST(stdlib_aes128_sparse, sparse_xor_circuit)
419{
423
425
426 // Test cases: pairs of bytes and their expected XOR
428 { 0x00, 0x00, 0x00 }, { 0xFF, 0xFF, 0x00 }, { 0xAA, 0x55, 0xFF },
429 { 0x12, 0x34, 0x26 }, { 0x2b, 0x6b, 0x40 }, // key[0] XOR plaintext[0] from NIST vectors
430 };
431
432 for (const auto& [a, b, expected] : test_cases) {
433 // Convert bytes to sparse form
434 uint256_t sparse_a = numeric::map_into_sparse_form<AES_SPARSE_BASE>(a);
435 uint256_t sparse_b = numeric::map_into_sparse_form<AES_SPARSE_BASE>(b);
436
437 // Create circuit witnesses
438 field_ct field_a = witness_ct(&builder, fr(sparse_a));
439 field_ct field_b = witness_ct(&builder, fr(sparse_b));
440
441 // Add in circuit (this is the XOR operation before normalization)
442 field_ct sum = field_a + field_b;
443
444 // Normalize via plookup (this completes the XOR)
446
447 // Convert result back to byte and verify
448 uint64_t sparse_result = uint256_t(normalized.get_value()).data[0];
449 uint8_t actual = static_cast<uint8_t>(numeric::map_from_sparse_form<AES_SPARSE_BASE>(sparse_result));
450
451 EXPECT_EQ(actual, expected) << "Circuit sparse XOR failed for 0x" << std::hex << (int)a << " ^ 0x" << (int)b;
452 }
453
454 EXPECT_TRUE(CircuitChecker::check(builder));
455}
456
463TEST(stdlib_aes128_sparse, sparse_multi_xor_circuit)
464{
468
470
471 // Test 3-way and 4-way XOR
472 uint8_t a = 0x12, b = 0x34, c = 0x56, d = 0x78;
473
474 // Expected results
475 uint8_t expected_3way = a ^ b ^ c; // 0x12 ^ 0x34 ^ 0x56 = 0x70
476 uint8_t expected_4way = a ^ b ^ c ^ d; // 0x12 ^ 0x34 ^ 0x56 ^ 0x78 = 0x08
477
478 // Convert to sparse form
479 field_ct sparse_a = witness_ct(&builder, fr(numeric::map_into_sparse_form<AES_SPARSE_BASE>(a)));
480 field_ct sparse_b = witness_ct(&builder, fr(numeric::map_into_sparse_form<AES_SPARSE_BASE>(b)));
481 field_ct sparse_c = witness_ct(&builder, fr(numeric::map_into_sparse_form<AES_SPARSE_BASE>(c)));
482 field_ct sparse_d = witness_ct(&builder, fr(numeric::map_into_sparse_form<AES_SPARSE_BASE>(d)));
483
484 // 3-way XOR: add all three, then normalize
485 field_ct sum_3way = sparse_a + sparse_b + sparse_c;
487
488 uint64_t sparse_result_3way = uint256_t(normalized_3way.get_value()).data[0];
489 uint8_t actual_3way = static_cast<uint8_t>(numeric::map_from_sparse_form<AES_SPARSE_BASE>(sparse_result_3way));
490 EXPECT_EQ(actual_3way, expected_3way) << "3-way sparse XOR failed";
491
492 // 4-way XOR: add all four, then normalize
493 field_ct sum_4way = sparse_a + sparse_b + sparse_c + sparse_d;
495
496 uint64_t sparse_result_4way = uint256_t(normalized_4way.get_value()).data[0];
497 uint8_t actual_4way = static_cast<uint8_t>(numeric::map_from_sparse_form<AES_SPARSE_BASE>(sparse_result_4way));
498 EXPECT_EQ(actual_4way, expected_4way) << "4-way sparse XOR failed";
499
500 EXPECT_TRUE(CircuitChecker::check(builder));
501}
502
510TEST(stdlib_aes128_sparse, sparse_addition_limit)
511{
515
517
518 // Add 8 copies of 0xFF (all bits set)
519 // Each digit will be 8 (the maximum safe value in base-9)
520 uint8_t value = 0xFF;
521 field_ct sparse_value = witness_ct(&builder, fr(numeric::map_into_sparse_form<AES_SPARSE_BASE>(value)));
522
523 field_ct accumulated = sparse_value;
524 for (size_t i = 1; i < 8; ++i) {
525 accumulated = accumulated + sparse_value;
526 }
527
528 // XOR of 8 copies of 0xFF = 0x00 (even count = all zeros)
530
531 uint64_t sparse_result = uint256_t(normalized.get_value()).data[0];
532 uint8_t actual = static_cast<uint8_t>(numeric::map_from_sparse_form<AES_SPARSE_BASE>(sparse_result));
533
534 EXPECT_EQ(actual, 0x00) << "8-way XOR of 0xFF should be 0x00";
535
536 // Add 7 copies of 0xFF = 0xFF (odd count = all ones)
537 field_ct accumulated_7 = sparse_value;
538 for (size_t i = 1; i < 7; ++i) {
539 accumulated_7 = accumulated_7 + sparse_value;
540 }
541
543
544 uint64_t sparse_result_7 = uint256_t(normalized_7.get_value()).data[0];
545 uint8_t actual_7 = static_cast<uint8_t>(numeric::map_from_sparse_form<AES_SPARSE_BASE>(sparse_result_7));
546
547 EXPECT_EQ(actual_7, 0xFF) << "7-way XOR of 0xFF should be 0xFF";
548
549 EXPECT_TRUE(CircuitChecker::check(builder));
550}
551
558TEST(stdlib_aes128_sparse, sparse_addition_overflow)
559{
563
565
566 // Add 9 copies of 0xFF (all bits set)
567 // Each digit will be 9 (the minimum value to overflow)
568 uint8_t value = 0xFF;
569 field_ct sparse_value = witness_ct(&builder, fr(numeric::map_into_sparse_form<AES_SPARSE_BASE>(value)));
570
571 field_ct accumulated = sparse_value;
572 for (size_t i = 1; i < 9; ++i) {
573 accumulated = accumulated + sparse_value;
574 }
575
576 // 9-way addition of 0xFF overflows base-9 sparse form (digit value 9 >= base)
577 // This should throw an exception because the value exceeds the lookup table range
578 EXPECT_THROW(plookup_read::read_from_1_to_2_table(plookup::AES_NORMALIZE, accumulated), std::runtime_error)
579 << "9-way XOR of 0xFF should overflow and throw";
580}
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
void assert_equal(const field_t &rhs, std::string const &msg="field_t::assert_equal") const
Copy constraint: constrain that *this field is equal to rhs element.
Definition field.cpp:940
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:836
static field_pt read_from_1_to_2_table(const plookup::MultiTableId id, const field_pt &key_a)
Definition plookup.cpp:89
AluTraceBuilder builder
Definition alu.test.cpp:124
const std::vector< MemoryValue > data
FF a
FF b
stdlib::witness_t< bb::UltraCircuitBuilder > witness_pt
stdlib::field_t< UltraCircuitBuilder > field_pt
stdlib::witness_t< Builder > witness_ct
@ AES_NORMALIZE
Definition types.hpp:98
std::array< field_t< Builder >, 16 > convert_into_sparse_bytes(Builder *ctx, const field_t< Builder > &block_data)
Converts a 128-bit block into 16 sparse-form bytes via AES_INPUT plookup table.
Definition aes128.cpp:41
field_t< Builder > convert_from_sparse_bytes(Builder *ctx, block_span< Builder > sparse_bytes)
Definition aes128.cpp:57
std::vector< field_t< Builder > > encrypt_buffer_cbc(const std::vector< field_t< Builder > > &input, const field_t< Builder > &iv, const field_t< Builder > &key)
Main public interface: AES-128 CBC encryption.
Definition aes128.cpp:353
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:155
Inner sum(Cont< Inner, Args... > const &in)
Definition container.hpp:70
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
void test_aes128_combination(bool key_as_witness, bool iv_as_witness, bool input_as_witness)
stdlib::field_t< UltraCircuitBuilder > create_field_element(UltraCircuitBuilder &builder, const uint256_t &value, bool as_witness)
constexpr uint64_t AES_SPARSE_BASE
uint256_t convert_bytes_to_uint256(const uint8_t *data)
void test_aes128_mixed_input(bool key_as_witness, bool iv_as_witness, const std::vector< bool > &input_block_config)