35template <
typename Builder>
36template <
size_t lane_index>
40 constexpr size_t left_bits = ROTATIONS[lane_index];
43 constexpr size_t right_bits = 64 - ROTATIONS[lane_index];
49 constexpr size_t num_left_tables = left_bits / max_bits_per_table + (left_bits % max_bits_per_table > 0 ? 1 : 0);
50 constexpr size_t num_right_tables = right_bits / max_bits_per_table + (right_bits % max_bits_per_table > 0 ? 1 : 0);
55 constexpr uint256_t slice_divisor = BASE.
pow(right_bits);
56 const auto [left, right] = input.
divmod(slice_divisor);
60 uint256_t left_normalized = normalize_sparse(left);
61 uint256_t right_normalized = normalize_sparse(right);
106 auto compute_lookup_witnesses_for_limb = [&]<
size_t limb_bits,
size_t num_lookups>(
uint256_t& normalized) {
108 bb::constexpr_for<0, num_lookups, 1>([&]<
size_t i> {
109 constexpr size_t num_bits_processed = i * max_bits_per_table;
115 constexpr size_t bit_slice = (num_bits_processed + max_bits_per_table > limb_bits)
116 ? limb_bits % max_bits_per_table
117 : max_bits_per_table;
120 lookup[ColumnIdx::C1].push_back(input);
121 lookup[ColumnIdx::C2].push_back(normalized);
123 constexpr uint64_t divisor =
numeric::pow64(
static_cast<uint64_t
>(BASE), bit_slice);
124 constexpr uint64_t msb_divisor = divisor /
static_cast<uint64_t
>(BASE);
127 const auto [normalized_quotient, normalized_slice] = normalized.divmod(divisor);
130 const uint64_t normalized_msb = (
static_cast<uint64_t
>(normalized_slice) / msb_divisor);
131 lookup[ColumnIdx::C3].push_back(normalized_msb);
135 const auto [input_quotient, input_slice] = input.divmod(divisor);
137 { {
static_cast<uint64_t
>(input_slice), 0 }, { normalized_slice, normalized_msb } });
140 input = input_quotient;
141 normalized = normalized_quotient;
149 compute_lookup_witnesses_for_limb.template operator()<right_bits, num_right_tables>(right_normalized);
150 compute_lookup_witnesses_for_limb.template operator()<left_bits, num_left_tables>(left_normalized);
156 const auto accumulator_witnesses = limb.
context->create_gates_from_plookup_accumulators(
163 accumulator_witnesses[ColumnIdx::C3][num_left_tables + num_right_tables - 1]);
169 if (num_left_tables == 0) {
175 limb.
get_context(), accumulator_witnesses[ColumnIdx::C2][num_right_tables]);
178 constexpr uint256_t shift = BASE.
pow(ROTATIONS[lane_index]);
179 return (left_output + right_output * shift);
201 for (
size_t i = 0; i < NUM_KECCAK_LANES; ++i) {
258 auto& state = internal.
state;
260 for (
size_t i = 0; i < 5; ++i) {
271 twisted_state[5 + i],
272 twisted_state[10 + i],
273 twisted_state[15 + i],
274 twisted_state[20 + i] });
281 for (
size_t i = 0; i < 5; ++i) {
282 const auto non_shifted_equivalent = (
C[(i + 4) % 5]);
283 const auto shifted_equivalent =
C[(i + 1) % 5] * BASE;
284 D[i] = (non_shifted_equivalent + shifted_equivalent);
305 for (
size_t i = 0; i < 5; ++i) {
307 const auto [D_quotient, lo_native] = D_native.
divmod(BASE);
308 const uint256_t hi_native = D_quotient / divisor;
309 const uint256_t mid_native = D_quotient - hi_native * divisor;
316 D[i].assert_equal((hi * multiplicand).add_two(mid * 11, lo));
337 D[i] = accumulators[ColumnIdx::C2][0];
341 const field_ct most_significant_slice = accumulators[ColumnIdx::C1][accumulators[ColumnIdx::C1].size() - 1];
346 const field_ct target = -most_significant_slice + maximum;
349 "input to KECCAK_THETA_OUTPUT too large!");
354 for (
size_t i = 0; i < 5; ++i) {
355 for (
size_t j = 0; j < 5; ++j) {
356 state[j * 5 + i] = state[j * 5 + i] + D[i];
389 constexpr_for<0, NUM_KECCAK_LANES, 1>(
390 [&]<
size_t i>() { internal.
state[i] = normalize_and_rotate<i>(internal.
state[i], internal.
state_msb[i]); });
406 for (
size_t j = 0; j < 5; ++j) {
407 for (
size_t i = 0; i < 5; ++i) {
408 B[j * 5 + i] = internal.
state[j * 5 + i];
412 for (
size_t y = 0; y < 5; ++y) {
413 for (
size_t x = 0; x < 5; ++x) {
414 size_t u = (0 * x + 1 * y) % 5;
415 size_t v = (2 * x + 3 * y) % 5;
417 internal.
state[v * 5 + u] = B[5 * y + x];
441 auto& state = internal.
state;
443 for (
size_t y = 0; y < 5; ++y) {
445 for (
size_t x = 0; x < 5; ++x) {
446 const auto A = state[y * 5 + x];
447 const auto B = state[y * 5 + ((x + 1) % 5)];
448 const auto C = state[y * 5 + ((x + 2) % 5)];
451 lane_outputs[x] = (A + A + CHI_OFFSET).add_two(-B,
C);
453 for (
size_t x = 0; x < 5; ++x) {
456 internal.
state[y * 5 + x] = accumulators[ColumnIdx::C2][0];
457 internal.
state_msb[y * 5 + x] = accumulators[ColumnIdx::C3][accumulators[ColumnIdx::C3].size() - 1];
473 const field_ct xor_result = internal.
state[0] + SPARSE_RC[round];
476 internal.
state[0] = normalize_and_rotate<0>(xor_result, internal.
state_msb[0]);
479 if (round != NUM_KECCAK_ROUNDS - 1) {
480 compute_twisted_state(internal);
486 for (
size_t i = 0; i < NUM_KECCAK_ROUNDS; ++i) {
495template <
typename Builder>
500 const size_t l = input_buffer.size();
502 const size_t num_blocks = l / (BLOCK_SIZE / 8);
504 for (
size_t i = 0; i < num_blocks; ++i) {
506 for (
size_t j = 0; j < LIMBS_PER_BLOCK; ++j) {
507 internal.
state[j] = input_buffer[j];
510 for (
size_t j = LIMBS_PER_BLOCK; j < NUM_KECCAK_LANES; ++j) {
515 for (
size_t j = 0; j < LIMBS_PER_BLOCK; ++j) {
516 internal.
state[j] += input_buffer[i * LIMBS_PER_BLOCK + j];
521 compute_twisted_state(internal);
522 keccakf1600(internal);
531 for (
size_t i = 0; i < 4; ++i) {
535 little_endian_limb_bytes[0] = limb_bytes[7];
536 little_endian_limb_bytes[1] = limb_bytes[6];
537 little_endian_limb_bytes[2] = limb_bytes[5];
538 little_endian_limb_bytes[3] = limb_bytes[4];
539 little_endian_limb_bytes[4] = limb_bytes[3];
540 little_endian_limb_bytes[5] = limb_bytes[2];
541 little_endian_limb_bytes[6] = limb_bytes[1];
542 little_endian_limb_bytes[7] = limb_bytes[0];
543 result.
write(little_endian_limb_bytes);
563 const size_t input_size = input.
size();
565 const size_t max_blocks = (input_size + BLOCK_SIZE) / BLOCK_SIZE;
566 const size_t max_blocks_length = (BLOCK_SIZE * (max_blocks));
570 const size_t byte_difference = max_blocks_length - input_size;
572 for (
size_t i = 0; i < byte_difference; ++i) {
575 block_bytes.
write(padding_bytes);
579 const auto terminating_byte = input_size;
580 const auto terminating_block_byte = block_bytes.
size() - 1;
586 for (
size_t i = 0; i < block_bytes.
size(); i += 8) {
588 for (
size_t j = 0; j < 8; ++j) {
589 temp[j] = block_bytes[i + j];
591 block_bytes[i] = temp[7];
592 block_bytes[i + 1] = temp[6];
593 block_bytes[i + 2] = temp[5];
594 block_bytes[i + 3] = temp[4];
595 block_bytes[i + 4] = temp[3];
596 block_bytes[i + 5] = temp[2];
597 block_bytes[i + 6] = temp[1];
598 block_bytes[i + 7] = temp[0];
600 const size_t byte_size = block_bytes.
size();
602 const size_t num_limbs = byte_size / WORD_SIZE;
606 for (
size_t i = 0; i < num_limbs; ++i) {
608 if (i * WORD_SIZE + WORD_SIZE > byte_size) {
609 const size_t slice_size = byte_size - (i * WORD_SIZE);
610 const size_t byte_shift = (WORD_SIZE - slice_size) * 8;
611 sliced =
field_ct(block_bytes.
slice(i * WORD_SIZE, slice_size));
614 sliced =
field_ct(block_bytes.
slice(i * WORD_SIZE, WORD_SIZE));
616 sliced_buffer.emplace_back(sliced);
619 return sliced_buffer;
626template <
typename Builder>
635 for (
size_t i = 0; i < state.size(); ++i) {
637 internal.
state[i] = accumulators[ColumnIdx::C2][0];
638 internal.
state_msb[i] = accumulators[ColumnIdx::C3][accumulators[ColumnIdx::C3].size() - 1];
640 compute_twisted_state(internal);
641 keccakf1600(internal);
643 return extended_2_normal(internal);
649template <
typename Builder>
652 const size_t input_size)
655 const size_t num_blocks = input_size / (BLOCK_SIZE / 8);
656 for (
size_t i = 0; i < num_blocks; ++i) {
658 for (
size_t j = 0; j < LIMBS_PER_BLOCK; ++j) {
659 internal.
state[j] = input_buffer[j];
661 for (
size_t j = LIMBS_PER_BLOCK; j < NUM_KECCAK_LANES; ++j) {
665 for (
size_t j = 0; j < LIMBS_PER_BLOCK; ++j) {
669 internal.
state[j], input_buffer[i * LIMBS_PER_BLOCK + j], 64,
true);
679template <
typename Builder>
684 if (ctx ==
nullptr) {
687 const std::vector<uint8_t> result = hash_native(input.
get_value());
688 for (
size_t i = 0; i < 32; ++i) {
689 output[i] = result[i];
695 auto formatted_slices = format_input_lanes(input);
699 sponge_absorb_with_permutation_opcode(internal, formatted_slices, formatted_slices.size());
701 auto result = sponge_squeeze_for_permutation_opcode(internal.
state, ctx);
710 const auto constant_case = [&] {
712 const std::vector<uint8_t> result = hash_native(input.
get_value());
713 for (
size_t i = 0; i < 32; ++i) {
714 output[i] = result[i];
719 if (ctx ==
nullptr) {
720 return constant_case();
724 auto formatted_slices = format_input_lanes(input);
732 for (
size_t i = 0; i < formatted_slices.size(); ++i) {
733 const auto accumulators =
735 converted_buffer[i] = accumulators[ColumnIdx::C2][0];
736 msb_buffer[i] = accumulators[ColumnIdx::C3][accumulators[ColumnIdx::C3].size() - 1];
739 sponge_absorb(internal, converted_buffer, msb_buffer);
741 auto result = sponge_squeeze(internal);
747template <
typename Builder>
754 for (
size_t i = 0; i < internal.
state.size(); ++i) {
756 conversion[i] = output_limb;
764template <
typename Builder>
771 for (
size_t i = 0; i < 4; ++i) {
774 little_endian_limb_bytes[0] = limb_bytes[7];
775 little_endian_limb_bytes[1] = limb_bytes[6];
776 little_endian_limb_bytes[2] = limb_bytes[5];
777 little_endian_limb_bytes[3] = limb_bytes[4];
778 little_endian_limb_bytes[4] = limb_bytes[3];
779 little_endian_limb_bytes[5] = limb_bytes[2];
780 little_endian_limb_bytes[6] = limb_bytes[1];
781 little_endian_limb_bytes[7] = limb_bytes[0];
782 result.
write(little_endian_limb_bytes);
796 std::string in =
"abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01";
799 for (
size_t i = 0; i < num_iterations; i++) {
804template class keccak<bb::UltraCircuitBuilder>;
805template class keccak<bb::MegaCircuitBuilder>;
#define BB_ASSERT_GT(left, right,...)
constexpr uint256_t pow(const uint256_t &exponent) const
constexpr std::pair< uint256_t, uint256_t > divmod(const uint256_t &b) const
Container type for lookup table reads.
std::vector< BasicTable::LookupEntry > lookup_entries
Generate the plookup tables used for the RHO round of the Keccak hash algorithm.
static constexpr size_t TABLE_BITS
Represents a dynamic array of bytes in-circuit.
byte_array slice(size_t offset) const
Slice bytes from the byte array starting at offset. Does not add any constraints.
byte_array & write(byte_array const &other)
Appends the contents of another byte_array (other) to the end of this one.
std::vector< uint8_t > get_value() const
A helper converting a byte_array into the vector of its uint8_t values.
Builder * get_context() const
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
static field_t accumulate(const std::vector< field_t > &input)
Efficiently compute the sum of vector entries. Using big_add_gate we reduce the number of gates neede...
void create_range_constraint(size_t num_bits, std::string const &msg="field_t::range_constraint") const
Let x = *this.normalize(), constrain x.v < 2^{num_bits}.
Builder * get_context() const
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
field_t normalize() const
Return a new element, where the in-circuit witness contains the actual represented value (multiplicat...
uint32_t get_witness_index() const
Get the witness index of the current field element.
static void rho(keccak_state &state)
RHO round.
static void sponge_absorb_with_permutation_opcode(keccak_state &internal, std::vector< field_ct > &input_buffer, const size_t input_size)
static byte_array_ct sponge_squeeze_for_permutation_opcode(std::array< field_ct, NUM_KECCAK_LANES > lanes, Builder *context)
static void pi(keccak_state &state)
PI.
static void theta(keccak_state &state)
THETA round.
static std::vector< field_ct > format_input_lanes(byte_array_ct &input)
Convert the input buffer into 8-bit keccak lanes in little-endian form. Additionally,...
static void compute_twisted_state(keccak_state &internal)
Compute twisted representation of hash lane.
static void chi(keccak_state &state)
CHI.
static byte_array_ct sponge_squeeze(keccak_state &internal)
static field_t< Builder > normalize_and_rotate(const field_ct &limb, field_ct &msb)
Normalize a base-11 limb and left-rotate by keccak::ROTATIONS[lane_index] bits. This method also extr...
static std::array< field_ct, NUM_KECCAK_LANES > permutation_opcode(std::array< field_ct, NUM_KECCAK_LANES > state, Builder *context)
static std::array< field_ct, NUM_KECCAK_LANES > extended_2_normal(keccak_state &internal)
static byte_array_ct hash_using_permutation_opcode(byte_array_ct &input)
static void keccakf1600(keccak_state &state)
static byte_array_ct hash(byte_array_ct &input)
static void sponge_absorb(keccak_state &internal, const std::vector< field_ct > &input_buffer, const std::vector< field_ct > &msb_buffer)
static void iota(keccak_state &state, size_t round)
IOTA.
static field_pt create_logic_constraint(field_pt &a, field_pt &b, size_t num_bits, bool is_xor_gate, const std::function< std::pair< uint256_t, uint256_t >(uint256_t, uint256_t, size_t)> &get_chunk=[](uint256_t left, uint256_t right, size_t chunk_size) { uint256_t left_chunk=left &((uint256_t(1)<< chunk_size) - 1);uint256_t right_chunk=right &((uint256_t(1)<< chunk_size) - 1);return std::make_pair(left_chunk, right_chunk);})
A logical AND or XOR over a variable number of bits.
static witness_t create_constant_witness(Builder *parent_context, const bb::fr &in)
StrictMock< MockContext > context
bn254::witness_ct witness_ct
stdlib::field_t< Builder > field_ct
constexpr uint64_t pow64(const uint64_t input, const uint64_t exponent)
@ KECCAK_NORMALIZE_AND_ROTATE
void generate_keccak_test_circuit(Builder &builder, size_t num_iterations)
Generate a simple keccak circuit for testing purposes.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
std::array< field_ct, NUM_KECCAK_LANES > state
std::array< field_ct, NUM_KECCAK_LANES > twisted_state
std::array< field_ct, NUM_KECCAK_LANES > state_msb