Skip to main content

sbpf_runtime/syscalls/
crypto.rs

1use {
2    crate::config::ExecutionCost,
3    blake3::Hasher as Blake3Hasher,
4    sbpf_vm::{
5        compute::ComputeMeter,
6        errors::{SbpfVmError, SbpfVmResult},
7        memory::Memory,
8    },
9    sha2::Sha256,
10    sha3::Keccak256,
11};
12
13trait Hasher {
14    fn new() -> Self;
15    fn update(&mut self, data: &[u8]);
16    fn finalize(self) -> Vec<u8>;
17}
18
19impl Hasher for Sha256 {
20    fn new() -> Self {
21        sha2::Digest::new()
22    }
23    fn update(&mut self, data: &[u8]) {
24        sha2::Digest::update(self, data);
25    }
26    fn finalize(self) -> Vec<u8> {
27        sha2::Digest::finalize(self).to_vec()
28    }
29}
30
31impl Hasher for Keccak256 {
32    fn new() -> Self {
33        sha3::Digest::new()
34    }
35    fn update(&mut self, data: &[u8]) {
36        sha3::Digest::update(self, data);
37    }
38    fn finalize(self) -> Vec<u8> {
39        sha3::Digest::finalize(self).to_vec()
40    }
41}
42
43impl Hasher for Blake3Hasher {
44    fn new() -> Self {
45        blake3::Hasher::new()
46    }
47    fn update(&mut self, data: &[u8]) {
48        blake3::Hasher::update(self, data);
49    }
50    fn finalize(self) -> Vec<u8> {
51        blake3::Hasher::finalize(&self).as_bytes().to_vec()
52    }
53}
54
55fn read_slices(memory: &Memory, vals_addr: u64, vals_len: u64) -> SbpfVmResult<Vec<(u64, u64)>> {
56    let mut slices = Vec::with_capacity(vals_len as usize);
57    for i in 0..vals_len {
58        let slice_addr = vals_addr.saturating_add(i.saturating_mul(16));
59        let ptr = memory.read_u64(slice_addr)?;
60        let len = memory.read_u64(slice_addr.saturating_add(8))?;
61        slices.push((ptr, len));
62    }
63    Ok(slices)
64}
65
66fn hash_slices<H: Hasher>(
67    memory: &mut Memory,
68    compute: &ComputeMeter,
69    costs: &ExecutionCost,
70    vals_addr: u64,
71    vals_len: u64,
72    result_addr: u64,
73) -> SbpfVmResult<u64> {
74    if vals_len > costs.sha256_max_slices {
75        return Err(SbpfVmError::TooManySlices);
76    }
77
78    compute.consume(costs.sha256_base_cost)?;
79
80    let mut hasher = H::new();
81    if vals_len > 0 {
82        for (ptr, len) in read_slices(memory, vals_addr, vals_len)? {
83            let cost = costs
84                .mem_op_base_cost
85                .max(costs.sha256_byte_cost.saturating_mul(len / 2));
86            compute.consume(cost)?;
87            hasher.update(memory.read_bytes(ptr, len as usize)?);
88        }
89    }
90
91    memory.write_bytes(result_addr, &hasher.finalize())?;
92    Ok(0)
93}
94
95pub fn sol_sha256(
96    registers: [u64; 5],
97    memory: &mut Memory,
98    compute: &ComputeMeter,
99    costs: &ExecutionCost,
100) -> SbpfVmResult<u64> {
101    hash_slices::<Sha256>(
102        memory,
103        compute,
104        costs,
105        registers[0],
106        registers[1],
107        registers[2],
108    )
109}
110
111pub fn sol_keccak256(
112    registers: [u64; 5],
113    memory: &mut Memory,
114    compute: &ComputeMeter,
115    costs: &ExecutionCost,
116) -> SbpfVmResult<u64> {
117    hash_slices::<Keccak256>(
118        memory,
119        compute,
120        costs,
121        registers[0],
122        registers[1],
123        registers[2],
124    )
125}
126
127pub fn sol_blake3(
128    registers: [u64; 5],
129    memory: &mut Memory,
130    compute: &ComputeMeter,
131    costs: &ExecutionCost,
132) -> SbpfVmResult<u64> {
133    hash_slices::<Blake3Hasher>(
134        memory,
135        compute,
136        costs,
137        registers[0],
138        registers[1],
139        registers[2],
140    )
141}
142
143#[cfg(test)]
144mod tests {
145    use {
146        super::*,
147        crate::syscalls::tests::test_helpers::{costs, make_memory, meter},
148        sbpf_vm::{errors::SbpfVmError, memory::Memory},
149    };
150
151    fn setup_single_slice(memory: &mut Memory, data: &[u8]) -> (u64, u64) {
152        let data_addr = Memory::HEAP_START;
153        memory.write_bytes(data_addr, data).unwrap();
154
155        let slices_addr = Memory::HEAP_START + 64;
156        memory.write_u64(slices_addr, data_addr).unwrap();
157        memory
158            .write_u64(slices_addr + 8, data.len() as u64)
159            .unwrap();
160
161        let result_addr = Memory::HEAP_START + 128;
162        (slices_addr, result_addr)
163    }
164
165    fn reference_sha256(data: &[u8]) -> Vec<u8> {
166        use sha2::Digest;
167        sha2::Sha256::digest(data).to_vec()
168    }
169
170    fn reference_keccak256(data: &[u8]) -> Vec<u8> {
171        use sha3::Digest;
172        sha3::Keccak256::digest(data).to_vec()
173    }
174
175    fn reference_blake3(data: &[u8]) -> Vec<u8> {
176        blake3::hash(data).as_bytes().to_vec()
177    }
178
179    #[test]
180    fn test_sha256_known_input() {
181        let mut memory = make_memory();
182        let (slices_addr, result_addr) = setup_single_slice(&mut memory, b"hello");
183
184        let registers = [slices_addr, 1, result_addr, 0, 0];
185        sol_sha256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
186
187        let result = memory.read_bytes(result_addr, 32).unwrap();
188        assert_eq!(result, reference_sha256(b"hello").as_slice());
189    }
190
191    #[test]
192    fn test_sha256_empty_slices() {
193        let mut memory = make_memory();
194        let result_addr = Memory::HEAP_START + 128;
195
196        let registers = [0, 0, result_addr, 0, 0];
197        sol_sha256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
198
199        let result = memory.read_bytes(result_addr, 32).unwrap();
200        assert_eq!(result, reference_sha256(b"").as_slice());
201    }
202
203    #[test]
204    fn test_sha256_multiple_slices_concatenated() {
205        let mut memory = make_memory();
206        memory.write_bytes(Memory::HEAP_START, b"he").unwrap();
207        memory.write_bytes(Memory::HEAP_START + 8, b"llo").unwrap();
208
209        let slices_addr = Memory::HEAP_START + 64;
210        memory.write_u64(slices_addr, Memory::HEAP_START).unwrap();
211        memory.write_u64(slices_addr + 8, 2).unwrap();
212        memory
213            .write_u64(slices_addr + 16, Memory::HEAP_START + 8)
214            .unwrap();
215        memory.write_u64(slices_addr + 24, 3).unwrap();
216
217        let result_addr = Memory::HEAP_START + 128;
218        let registers = [slices_addr, 2, result_addr, 0, 0];
219        sol_sha256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
220
221        let result = memory.read_bytes(result_addr, 32).unwrap();
222        assert_eq!(result, reference_sha256(b"hello").as_slice());
223    }
224
225    #[test]
226    fn test_sha256_too_many_slices() {
227        let mut memory = make_memory();
228        let result_addr = Memory::HEAP_START + 128;
229        let registers = [Memory::HEAP_START, 20_001, result_addr, 0, 0];
230        assert!(matches!(
231            sol_sha256(registers, &mut memory, &meter(1_000_000), &costs()),
232            Err(SbpfVmError::TooManySlices)
233        ));
234    }
235
236    #[test]
237    fn test_sha256_compute_exhausted() {
238        let mut memory = make_memory();
239        let result_addr = Memory::HEAP_START + 128;
240        let registers = [0, 0, result_addr, 0, 0];
241        assert!(matches!(
242            sol_sha256(registers, &mut memory, &meter(84), &costs()),
243            Err(SbpfVmError::ComputeBudgetExceeded { .. })
244        ));
245    }
246
247    #[test]
248    fn test_keccak256_known_input() {
249        let mut memory = make_memory();
250        let (slices_addr, result_addr) = setup_single_slice(&mut memory, b"hello");
251
252        let registers = [slices_addr, 1, result_addr, 0, 0];
253        sol_keccak256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
254
255        let result = memory.read_bytes(result_addr, 32).unwrap();
256        assert_eq!(result, reference_keccak256(b"hello").as_slice());
257    }
258
259    #[test]
260    fn test_keccak256_empty_slices() {
261        let mut memory = make_memory();
262        let result_addr = Memory::HEAP_START + 128;
263        let registers = [0, 0, result_addr, 0, 0];
264        sol_keccak256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
265
266        let result = memory.read_bytes(result_addr, 32).unwrap();
267        assert_eq!(result, reference_keccak256(b"").as_slice());
268    }
269
270    #[test]
271    fn test_keccak256_differs_from_sha256() {
272        let mut memory = make_memory();
273        let (slices_addr, result_addr) = setup_single_slice(&mut memory, b"hello");
274
275        let registers = [slices_addr, 1, result_addr, 0, 0];
276        sol_keccak256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
277
278        let keccak_result = memory.read_bytes(result_addr, 32).unwrap().to_vec();
279        assert_ne!(keccak_result, reference_sha256(b"hello"));
280    }
281
282    #[test]
283    fn test_blake3_known_input() {
284        let mut memory = make_memory();
285        let (slices_addr, result_addr) = setup_single_slice(&mut memory, b"hello");
286
287        let registers = [slices_addr, 1, result_addr, 0, 0];
288        sol_blake3(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
289
290        let result = memory.read_bytes(result_addr, 32).unwrap();
291        assert_eq!(result, reference_blake3(b"hello").as_slice());
292    }
293
294    #[test]
295    fn test_blake3_empty_slices() {
296        let mut memory = make_memory();
297        let result_addr = Memory::HEAP_START + 128;
298        let registers = [0, 0, result_addr, 0, 0];
299        sol_blake3(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
300
301        let result = memory.read_bytes(result_addr, 32).unwrap();
302        assert_eq!(result, reference_blake3(b"").as_slice());
303    }
304
305    #[test]
306    fn test_all_three_hashes_differ_on_same_input() {
307        let mut memory = make_memory();
308
309        let (slices_addr, result_addr) = setup_single_slice(&mut memory, b"test");
310        let registers = [slices_addr, 1, result_addr, 0, 0];
311
312        sol_sha256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
313        let sha_out = memory.read_bytes(result_addr, 32).unwrap().to_vec();
314
315        sol_keccak256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
316        let keccak_out = memory.read_bytes(result_addr, 32).unwrap().to_vec();
317
318        sol_blake3(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
319        let blake_out = memory.read_bytes(result_addr, 32).unwrap().to_vec();
320
321        assert_ne!(sha_out, keccak_out);
322        assert_ne!(sha_out, blake_out);
323        assert_ne!(keccak_out, blake_out);
324    }
325}