risc0_zkp/core/hash/
blake2b.rs

1// Copyright 2025 RISC Zero, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A Blake2b HashSuite.
16use alloc::{boxed::Box, rc::Rc, vec::Vec};
17use core::marker::PhantomData;
18
19use blake2::{
20    digest::{Update, VariableOutput},
21    Blake2bVar,
22};
23use rand_core::{impls, RngCore};
24use risc0_core::field::{
25    baby_bear::{BabyBear, BabyBearElem, BabyBearExtElem},
26    Elem, ExtElem,
27};
28
29use super::{HashFn, HashSuite, Rng, RngFactory};
30use crate::core::digest::Digest;
31
32/// Hash function trait.
33pub trait Blake2b: Send + Sync {
34    /// A function producing a hash from a list of u8.
35    fn blake2b<T: AsRef<[u8]>>(data: T) -> [u8; 32];
36}
37
38/// Implementation of blake2b using CPU.
39pub struct Blake2bCpuImpl;
40
41/// Type alias for Blake2b HashSuite using CPU.
42pub type Blake2bCpuHashSuite = Blake2bHashSuite<Blake2bCpuImpl>;
43
44impl Blake2b for Blake2bCpuImpl {
45    fn blake2b<T: AsRef<[u8]>>(data: T) -> [u8; 32] {
46        let mut result = [0; 32];
47        let mut hasher = Blake2bVar::new(32).expect("Initializing Blake2bVar failed");
48
49        hasher.update(data.as_ref());
50        hasher
51            .finalize_variable(&mut result)
52            .expect("Finalizing Blake2bVar failed");
53        result
54    }
55}
56
57struct Blake2bRngFactory<T: Blake2b> {
58    phantom: PhantomData<T>,
59}
60
61impl<T: Blake2b> Blake2bRngFactory<T> {
62    fn new() -> Self {
63        Self {
64            phantom: PhantomData,
65        }
66    }
67}
68
69impl<T: Blake2b + 'static> RngFactory<BabyBear> for Blake2bRngFactory<T> {
70    fn new_rng(&self) -> Box<dyn Rng<BabyBear>> {
71        let rng: Blake2bRng<T> = Blake2bRng::new();
72        Box::new(rng)
73    }
74}
75
76/// Blake2b HashSuite.
77/// We are using a generic hasher to allow different implementations.
78pub struct Blake2bHashSuite<T: Blake2b> {
79    phantom: PhantomData<T>,
80}
81
82impl<T: Blake2b + 'static> Blake2bHashSuite<T> {
83    /// Create a new HashSuite
84    pub fn new_suite() -> HashSuite<BabyBear> {
85        HashSuite {
86            name: "blake2b".into(),
87            hashfn: Rc::new(Blake2bHashFn::<T>::new()),
88            rng: Rc::new(Blake2bRngFactory::<T>::new()),
89        }
90    }
91}
92
93/// Blake2b HashFn.
94struct Blake2bHashFn<T: Blake2b> {
95    phantom: PhantomData<T>,
96}
97
98impl<T: Blake2b> Blake2bHashFn<T> {
99    fn new() -> Self {
100        Self {
101            phantom: PhantomData,
102        }
103    }
104}
105
106impl<T: Blake2b> HashFn<BabyBear> for Blake2bHashFn<T> {
107    fn hash_pair(&self, a: &Digest, b: &Digest) -> Box<Digest> {
108        let concat = [a.as_bytes(), b.as_bytes()].concat();
109        Box::new(Digest::from(T::blake2b(concat)))
110    }
111
112    fn hash_elem_slice(&self, slice: &[BabyBearElem]) -> Box<Digest> {
113        let mut data = Vec::<u8>::new();
114        for el in slice {
115            data.extend_from_slice(el.as_u32_montgomery().to_be_bytes().as_slice());
116        }
117        Box::new(Digest::from(T::blake2b(data)))
118    }
119
120    fn hash_ext_elem_slice(&self, slice: &[BabyBearExtElem]) -> Box<Digest> {
121        let mut data = Vec::<u8>::new();
122        for ext_el in slice {
123            for el in ext_el.subelems() {
124                data.extend_from_slice(el.as_u32_montgomery().to_be_bytes().as_slice());
125            }
126        }
127        Box::new(Digest::from(T::blake2b(data)))
128    }
129}
130
131/// Blake2b-based random number generator.
132pub struct Blake2bRng<T: Blake2b> {
133    current: [u8; 32],
134    hasher: PhantomData<T>,
135}
136
137impl<T: Blake2b> Blake2bRng<T> {
138    fn new() -> Self {
139        Self {
140            current: [0; 32],
141            hasher: Default::default(),
142        }
143    }
144}
145
146impl<T: Blake2b> Rng<BabyBear> for Blake2bRng<T> {
147    fn mix(&mut self, val: &Digest) {
148        let concat = [self.current.as_ref(), val.as_bytes()].concat();
149        self.current = T::blake2b(concat);
150    }
151
152    fn random_bits(&mut self, bits: usize) -> u32 {
153        ((1 << bits) - 1) & self.next_u32()
154    }
155
156    fn random_elem(&mut self) -> BabyBearElem {
157        BabyBearElem::random(self)
158    }
159
160    fn random_ext_elem(&mut self) -> BabyBearExtElem {
161        BabyBearExtElem::random(self)
162    }
163}
164
165impl<T: Blake2b> RngCore for Blake2bRng<T> {
166    fn next_u32(&mut self) -> u32 {
167        let next = T::blake2b(self.current);
168        self.current = next;
169        ((next[0] as u32) << 24)
170            + ((next[1] as u32) << 16)
171            + ((next[2] as u32) << 8)
172            + (next[3] as u32)
173    }
174
175    fn next_u64(&mut self) -> u64 {
176        impls::next_u64_via_u32(self)
177    }
178
179    fn fill_bytes(&mut self, dest: &mut [u8]) {
180        impls::fill_bytes_via_next(self, dest);
181    }
182}