Skip to main content

midnight_circuits/hash/ripemd160/
mod.rs

1// This file is part of MIDNIGHT-ZK.
2// Copyright (C) Midnight Foundation
3// SPDX-License-Identifier: Apache-2.0
4// Licensed under the Apache License, Version 2.0 (the "License");
5// You may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7// http://www.apache.org/licenses/LICENSE-2.0
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14//! Implementation in-circuit of the RIPEMD-160 hash function.
15
16#![allow(non_snake_case)]
17mod ripemd160_chip;
18mod types;
19mod utils;
20
21use midnight_proofs::{circuit::Layouter, plonk::Error};
22use ripemd::Digest;
23pub use ripemd160_chip::{
24    RipeMD160Chip, RipeMD160Config, NB_RIPEMD160_ADVICE_COLS, NB_RIPEMD160_FIXED_COLS,
25};
26
27use crate::{
28    instructions::{hash::HashCPU, DecompositionInstructions, HashInstructions},
29    types::AssignedByte,
30    CircuitField,
31};
32
33impl<F: CircuitField> HashCPU<u8, [u8; 20]> for RipeMD160Chip<F> {
34    fn hash(inputs: &[u8]) -> [u8; 20] {
35        let output = ripemd::Ripemd160::digest(inputs);
36        output.into_iter().collect::<Vec<_>>().try_into().unwrap()
37    }
38}
39
40impl<F: CircuitField> HashInstructions<F, AssignedByte<F>, [AssignedByte<F>; 20]>
41    for RipeMD160Chip<F>
42{
43    fn hash(
44        &self,
45        layouter: &mut impl Layouter<F>,
46        inputs: &[AssignedByte<F>],
47    ) -> Result<[AssignedByte<F>; 20], Error> {
48        let mut output_bytes = Vec::with_capacity(20);
49
50        // We convert each `AssignedWord` returned by `self.ripemd160` into 4 bytes.
51        for word in self.ripemd160(layouter, inputs)? {
52            let bytes = self.native_gadget.assigned_to_le_bytes(layouter, &word.0, Some(4))?;
53            output_bytes.extend(bytes)
54        }
55
56        Ok(output_bytes.try_into().unwrap())
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use midnight_curves::Fq as Scalar;
63
64    use crate::{
65        field::NativeGadget, hash::ripemd160::RipeMD160Chip, instructions::hash::tests::test_hash,
66        types::AssignedByte,
67    };
68
69    #[test]
70    fn test_ripemd160_hash() {
71        fn test_wrapper(input_size: usize, cost_model: bool) {
72            test_hash::<
73                Scalar,
74                AssignedByte<Scalar>,
75                [AssignedByte<Scalar>; 20],
76                RipeMD160Chip<Scalar>,
77                NativeGadget<Scalar, _, _>,
78            >(cost_model, "RIPEMD160", input_size)
79        }
80
81        const RIPEMD160_BLOCK_SIZE: usize = 64;
82        const RIPEMD160_EDGE_PADDING: usize = 55;
83        test_wrapper(2 * RIPEMD160_BLOCK_SIZE, true);
84
85        test_wrapper(RIPEMD160_BLOCK_SIZE, false);
86        test_wrapper(RIPEMD160_BLOCK_SIZE - 1, false);
87        test_wrapper(RIPEMD160_BLOCK_SIZE - 2, false);
88        test_wrapper(4 * RIPEMD160_BLOCK_SIZE, false);
89
90        test_wrapper(RIPEMD160_EDGE_PADDING, false);
91        test_wrapper(RIPEMD160_EDGE_PADDING - 1, false);
92
93        test_wrapper(0, false);
94        test_wrapper(1, false);
95        test_wrapper(2, false);
96    }
97}