snarkvm_circuit_algorithms/keccak/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
// Copyright 2024 Aleo Network Foundation
// This file is part of the snarkVM library.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
mod hash;
#[cfg(all(test, feature = "console"))]
use snarkvm_circuit_types::environment::assert_scope;
#[cfg(test)]
use snarkvm_utilities::{TestRng, Uniform};
use crate::Hash;
use snarkvm_circuit_types::{Boolean, U64, environment::prelude::*};
/// The Keccak-224 hash function.
pub type Keccak224<E> = Keccak<E, { KeccakType::Keccak as u8 }, 224>;
/// The Keccak-256 hash function.
pub type Keccak256<E> = Keccak<E, { KeccakType::Keccak as u8 }, 256>;
/// The Keccak-384 hash function.
pub type Keccak384<E> = Keccak<E, { KeccakType::Keccak as u8 }, 384>;
/// The Keccak-512 hash function.
pub type Keccak512<E> = Keccak<E, { KeccakType::Keccak as u8 }, 512>;
/// The SHA3-224 hash function.
pub type Sha3_224<E> = Keccak<E, { KeccakType::Sha3 as u8 }, 224>;
/// The SHA3-256 hash function.
pub type Sha3_256<E> = Keccak<E, { KeccakType::Sha3 as u8 }, 256>;
/// The SHA3-384 hash function.
pub type Sha3_384<E> = Keccak<E, { KeccakType::Sha3 as u8 }, 384>;
/// The SHA3-512 hash function.
pub type Sha3_512<E> = Keccak<E, { KeccakType::Sha3 as u8 }, 512>;
/// A helper to specify the hash type.
enum KeccakType {
Keccak,
Sha3,
}
/// The rows and columns are 5-bit lanes.
const MODULO: usize = 5;
/// The permutation type `l`.
const L: usize = 6;
/// The number of rounds in a full-round operation.
const NUM_ROUNDS: usize = 12 + 2 * L;
/// The permutation width `b`.
const PERMUTATION_WIDTH: usize = 1600;
/// The sponge construction `Sponge[f, pad, r]` is a function that takes a variable-length input
/// and produces a fixed-length output (the hash value).
///
/// The permutation `f` is a function that takes a fixed-length input and produces a fixed-length output,
/// defined as `f = Keccak-f[b]`, where `b := 25 * 2^l` is the width of the permutation,
/// and `l` is the log width of the permutation.
/// For our case, `l = 6`, thus `b = 1600`.
///
/// The padding rule `pad` is a function that takes a variable-length input and produces a fixed-length output.
/// In Keccak, `pad` is a multi-rate padding, defined as `pad(M) = M || 0x01 || 0x00…0x00 || 0x80`,
/// where `M` is the input data, and `0x01 || 0x00…0x00 || 0x80` is the padding.
/// In SHA-3, `pad` is a SHAKE, defined as `pad(M) = M || 0x06 || 0x00…0x00 || 0x80`,
/// where `M` is the input data, and `0x06 || 0x00…0x00 || 0x80` is the padding.
///
/// The bitrate `r` is the number of bits that are absorbed into the sponge state in each iteration
/// of the absorbing phase.
///
/// In addition, the capacity is defined as `c := b - r`.
#[derive(Clone, Debug, Default)]
pub struct Keccak<E: Environment, const TYPE: u8, const VARIANT: usize> {
/// The round constants `RC[t] ∈ GF(2)` are defined as the
/// output of a linear feedback shift register (LFSR).
round_constants: Vec<U64<E>>,
/// Precomputations for the ρ step.
rotl: [usize; MODULO * MODULO],
}
impl<E: Environment, const TYPE: u8, const VARIANT: usize> Keccak<E, TYPE, VARIANT> {
/// Initializes a new Keccak hash function.
pub fn new() -> Self {
Self {
round_constants: Self::ROUND_CONSTANTS.into_iter().map(|e| U64::constant(console::U64::new(e))).collect(),
rotl: Self::rotl_offsets(),
}
}
}
impl<E: Environment, const TYPE: u8, const VARIANT: usize> Keccak<E, TYPE, VARIANT> {
/// The values `ROUND_CONSTANTS[t] ∈ GF(2)` are defined as the
/// output of a binary linear feedback shift register (LFSR):
/// ```text
/// ROUND_CONSTANTS[t] = (x^t) mod (x^8 + x^6 + x^5 + x^4 + 1) mod x in GF(2)[x]
/// ```
/// where `t ∈ {0, 1, …, NUM_ROUNDS}`.
const ROUND_CONSTANTS: [u64; NUM_ROUNDS] = [
0x0000000000000001,
0x0000000000008082,
0x800000000000808A,
0x8000000080008000,
0x000000000000808B,
0x0000000080000001,
0x8000000080008081,
0x8000000000008009,
0x000000000000008A,
0x0000000000000088,
0x0000000080008009,
0x000000008000000A,
0x000000008000808B,
0x800000000000008B,
0x8000000000008089,
0x8000000000008003,
0x8000000000008002,
0x8000000000000080,
0x000000000000800A,
0x800000008000000A,
0x8000000080008081,
0x8000000000008080,
0x0000000080000001,
0x8000000080008008,
];
/// Returns the ROTL offsets for the ρ step.
///
/// The offsets are defined as follows:
/// ```text
/// for t = 0 to 23 do
/// offset[t] = (t + 1)(t + 2)/2
/// end for
/// ```
///
/// This method transposes the offsets to match the access pattern (i.e. for y, then for x).
fn rotl_offsets() -> [usize; MODULO * MODULO] {
let mut rotl = [0; MODULO * MODULO];
let mut x: usize = 1;
let mut y: usize = 0;
for t in 0..=23 {
let rotr = ((t + 1) * (t + 2) / 2) % 64;
rotl[x + (y * MODULO)] = (64 - rotr) % 64;
// Update x and y.
let x_old = x;
x = y;
y = (2 * x_old + 3 * y) % MODULO;
}
rotl
}
}