halo2/halo2_gadgets/src/sha256.rs

167 lines
5.4 KiB
Rust
Raw Normal View History

2022-05-10 13:26:49 -07:00
//! The [SHA-256] hash function.
2021-05-03 20:14:32 -07:00
//!
//! [SHA-256]: https://tools.ietf.org/html/rfc6234
use std::cmp::min;
use std::convert::TryInto;
use std::fmt;
use group::ff::Field;
use halo2_proofs::{
2021-05-03 20:14:32 -07:00
circuit::{Chip, Layouter},
plonk::Error,
};
mod table16;
pub use table16::{BlockWord, Table16Chip, Table16Config};
/// The size of a SHA-256 block, in 32-bit words.
pub const BLOCK_SIZE: usize = 16;
/// The size of a SHA-256 digest, in 32-bit words.
const DIGEST_SIZE: usize = 8;
/// The set of circuit instructions required to use the [`Sha256`] gadget.
pub trait Sha256Instructions<F: Field>: Chip<F> {
2021-05-03 20:14:32 -07:00
/// Variable representing the SHA-256 internal state.
type State: Clone + fmt::Debug;
/// Variable representing a 32-bit word of the input block to the SHA-256 compression
/// function.
2021-07-19 07:23:20 -07:00
type BlockWord: Copy + fmt::Debug + Default;
2021-05-03 20:14:32 -07:00
/// Places the SHA-256 IV in the circuit, returning the initial state variable.
2021-04-20 04:26:32 -07:00
fn initialization_vector(&self, layouter: &mut impl Layouter<F>) -> Result<Self::State, Error>;
2021-05-03 20:14:32 -07:00
/// Creates an initial state from the output state of a previous block
fn initialization(
2021-04-20 04:26:32 -07:00
&self,
layouter: &mut impl Layouter<F>,
2021-05-03 20:14:32 -07:00
init_state: &Self::State,
) -> Result<Self::State, Error>;
/// Starting from the given initialized state, processes a block of input and returns the
/// final state.
fn compress(
2021-04-20 04:26:32 -07:00
&self,
layouter: &mut impl Layouter<F>,
2021-05-03 20:14:32 -07:00
initialized_state: &Self::State,
input: [Self::BlockWord; BLOCK_SIZE],
) -> Result<Self::State, Error>;
/// Converts the given state into a message digest.
fn digest(
2021-04-20 04:26:32 -07:00
&self,
layouter: &mut impl Layouter<F>,
2021-05-03 20:14:32 -07:00
state: &Self::State,
) -> Result<[Self::BlockWord; DIGEST_SIZE], Error>;
}
/// The output of a SHA-256 circuit invocation.
#[derive(Debug)]
pub struct Sha256Digest<BlockWord>([BlockWord; DIGEST_SIZE]);
/// A gadget that constrains a SHA-256 invocation. It supports input at a granularity of
/// 32 bits.
#[derive(Debug)]
pub struct Sha256<F: Field, CS: Sha256Instructions<F>> {
2021-04-20 04:26:32 -07:00
chip: CS,
2021-05-03 20:14:32 -07:00
state: CS::State,
cur_block: Vec<CS::BlockWord>,
length: usize,
}
impl<F: Field, Sha256Chip: Sha256Instructions<F>> Sha256<F, Sha256Chip> {
2021-05-03 20:14:32 -07:00
/// Create a new hasher instance.
2021-04-20 04:26:32 -07:00
pub fn new(chip: Sha256Chip, mut layouter: impl Layouter<F>) -> Result<Self, Error> {
let state = chip.initialization_vector(&mut layouter)?;
2021-05-03 20:14:32 -07:00
Ok(Sha256 {
2021-04-20 04:26:32 -07:00
chip,
state,
2021-05-03 20:14:32 -07:00
cur_block: Vec::with_capacity(BLOCK_SIZE),
length: 0,
})
}
/// Digest data, updating the internal state.
pub fn update(
&mut self,
2021-04-20 04:26:32 -07:00
mut layouter: impl Layouter<F>,
2021-05-03 20:14:32 -07:00
mut data: &[Sha256Chip::BlockWord],
) -> Result<(), Error> {
self.length += data.len() * 32;
// Fill the current block, if possible.
let remaining = BLOCK_SIZE - self.cur_block.len();
let (l, r) = data.split_at(min(remaining, data.len()));
self.cur_block.extend_from_slice(l);
data = r;
// If we still don't have a full block, we are done.
if self.cur_block.len() < BLOCK_SIZE {
return Ok(());
}
// Process the now-full current block.
2021-04-20 04:26:32 -07:00
self.state = self.chip.compress(
2021-05-03 20:14:32 -07:00
&mut layouter,
&self.state,
self.cur_block[..]
.try_into()
.expect("cur_block.len() == BLOCK_SIZE"),
)?;
self.cur_block.clear();
// Process any additional full blocks.
let mut chunks_iter = data.chunks_exact(BLOCK_SIZE);
for chunk in &mut chunks_iter {
2021-04-20 04:26:32 -07:00
self.state = self.chip.initialization(&mut layouter, &self.state)?;
self.state = self.chip.compress(
2021-05-03 20:14:32 -07:00
&mut layouter,
&self.state,
chunk.try_into().expect("chunk.len() == BLOCK_SIZE"),
)?;
}
// Cache the remaining partial block, if any.
let rem = chunks_iter.remainder();
self.cur_block.extend_from_slice(rem);
Ok(())
}
/// Retrieve result and consume hasher instance.
pub fn finalize(
mut self,
2021-04-20 04:26:32 -07:00
mut layouter: impl Layouter<F>,
2021-05-03 20:14:32 -07:00
) -> Result<Sha256Digest<Sha256Chip::BlockWord>, Error> {
// Pad the remaining block
if !self.cur_block.is_empty() {
2021-07-19 07:23:20 -07:00
let padding = vec![Sha256Chip::BlockWord::default(); BLOCK_SIZE - self.cur_block.len()];
2021-05-03 20:14:32 -07:00
self.cur_block.extend_from_slice(&padding);
2021-04-20 04:26:32 -07:00
self.state = self.chip.initialization(&mut layouter, &self.state)?;
self.state = self.chip.compress(
2021-05-03 20:14:32 -07:00
&mut layouter,
&self.state,
self.cur_block[..]
.try_into()
.expect("cur_block.len() == BLOCK_SIZE"),
)?;
}
2021-04-20 04:26:32 -07:00
self.chip
.digest(&mut layouter, &self.state)
.map(Sha256Digest)
2021-05-03 20:14:32 -07:00
}
/// Convenience function to compute hash of the data. It will handle hasher creation,
/// data feeding and finalization.
pub fn digest(
2021-04-20 04:26:32 -07:00
chip: Sha256Chip,
mut layouter: impl Layouter<F>,
2021-05-03 20:14:32 -07:00
data: &[Sha256Chip::BlockWord],
) -> Result<Sha256Digest<Sha256Chip::BlockWord>, Error> {
2021-04-20 04:26:32 -07:00
let mut hasher = Self::new(chip, layouter.namespace(|| "init"))?;
2021-05-03 20:14:32 -07:00
hasher.update(layouter.namespace(|| "update"), data)?;
hasher.finalize(layouter.namespace(|| "finalize"))
}
}