Skip to content

Commit cc43df8

Browse files
Add SHA-384 support
1 parent c45ce03 commit cc43df8

File tree

17 files changed

+275
-7355
lines changed

17 files changed

+275
-7355
lines changed

Cargo.lock

Lines changed: 0 additions & 7292 deletions
This file was deleted.

crates/circuits/sha-air/src/config.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,72 @@ pub const SHA512_H: [u64; 8] = [
279279
0x5be0cd19137e2179,
280280
];
281281

282+
#[derive(Clone)]
283+
pub struct Sha384Config;
284+
285+
impl ShaConfig for Sha384Config {
286+
// ==== Do not change these constants! ====
287+
type Word = u64;
288+
/// Number of bits in a SHA384 word
289+
const WORD_BITS: usize = 64;
290+
/// Number of words in a SHA384 block
291+
const BLOCK_WORDS: usize = 16;
292+
/// Number of rows per block
293+
const ROWS_PER_BLOCK: usize = 21;
294+
/// Number of rounds per row
295+
const ROUNDS_PER_ROW: usize = 4;
296+
/// Number of rounds per block
297+
const ROUNDS_PER_BLOCK: usize = 80;
298+
/// Number of words in a SHA384 hash
299+
const HASH_WORDS: usize = 8;
300+
/// Number of vars needed to encode the row index with [Encoder]
301+
const ROW_VAR_CNT: usize = 6;
302+
303+
fn get_invalid_carry_a(round_num: usize) -> &'static [u32] {
304+
&SHA384_INVALID_CARRY_A[round_num]
305+
}
306+
fn get_invalid_carry_e(round_num: usize) -> &'static [u32] {
307+
&SHA384_INVALID_CARRY_E[round_num]
308+
}
309+
fn get_k() -> &'static [u64] {
310+
&SHA384_K
311+
}
312+
fn get_h() -> &'static [u64] {
313+
&SHA384_H
314+
}
315+
}
316+
317+
pub(crate) const SHA384_INVALID_CARRY_A: [[u32; Sha384Config::WORD_U16S];
318+
Sha384Config::ROUNDS_PER_ROW] = [
319+
[1571481603, 1428841901, 1050676523, 793575075],
320+
[1233315842, 1822329223, 112923808, 1874228927],
321+
[1245603842, 927240770, 1579759431, 70557227],
322+
[195532801, 594312107, 1429379950, 220407092],
323+
];
324+
325+
pub(crate) const SHA384_INVALID_CARRY_E: [[u32; Sha384Config::WORD_U16S];
326+
Sha384Config::ROUNDS_PER_ROW] = [
327+
[1067980802, 1508061099, 1418826213, 1232569491],
328+
[1453086722, 1702524575, 152427899, 238512408],
329+
[1623674882, 701393097, 1002035664, 4776891],
330+
[1888911362, 184963225, 1151849224, 1034237098],
331+
];
332+
333+
/// SHA384 constant K's
334+
pub const SHA384_K: [u64; 80] = SHA512_K;
335+
336+
/// SHA384 initial hash values
337+
pub const SHA384_H: [u64; 8] = [
338+
0xcbbb9d5dc1059ed8,
339+
0x629a292a367cd507,
340+
0x9159015a3070dd17,
341+
0x152fecd8f70e5939,
342+
0x67332667ffc00b31,
343+
0x8eb44a8768581511,
344+
0xdb0c2e0d64f98fa7,
345+
0x47b5481dbefa4fa4,
346+
];
347+
282348
// Needed to avoid compile errors in utils.rs
283349
// not sure why this doesn't inf loop
284350
pub trait RotateRight {

crates/circuits/sha-air/src/tests.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{ShaDigestColsRefMut, ShaRoundColsRef, ShaRoundColsRefMut};
1+
use crate::{Sha384Config, ShaDigestColsRefMut, ShaRoundColsRef, ShaRoundColsRefMut};
22
use std::{borrow::BorrowMut, cmp::max, sync::Arc};
33

44
use openvm_circuit::arch::{
@@ -22,7 +22,7 @@ use openvm_stark_backend::{
2222
use openvm_stark_sdk::utils::create_seeded_rng;
2323
use rand::Rng;
2424

25-
use crate::{compose, small_sig0_field, Sha256Config, Sha512Config, Sha2Air, ShaConfig};
25+
use crate::{compose, small_sig0_field, Sha256Config, Sha2Air, Sha512Config, ShaConfig};
2626

2727
// A wrapper AIR purely for testing purposes
2828
#[derive(Clone, Debug)]
@@ -121,6 +121,11 @@ fn rand_sha512_test() {
121121
rand_sha_test::<Sha512Config>();
122122
}
123123

124+
#[test]
125+
fn rand_sha384_test() {
126+
rand_sha_test::<Sha384Config>();
127+
}
128+
124129
// A wrapper Chip to test that the final_hash is properly constrained.
125130
// This chip implements a malicious trace gen that violates the final_hash constraints.
126131
pub struct ShaTestBadFinalHashChip<C: ShaConfig> {
@@ -282,3 +287,9 @@ fn test_sha256_final_hash_constraints() {
282287
fn test_sha512_final_hash_constraints() {
283288
test_sha_final_hash_constraints::<Sha512Config>();
284289
}
290+
291+
#[test]
292+
#[should_panic]
293+
fn test_sha384_final_hash_constraints() {
294+
test_sha_final_hash_constraints::<Sha384Config>();
295+
}

crates/circuits/sha-air/src/trace.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@ impl<C: ShaConfig> Sha2Air<C> {
563563

564564
/// The following functions do the calculations in native field since they will be called on padding rows
565565
/// which can overflow and we need to make sure it matches the AIR constraints
566-
/// Puts the correct carrys in the `next_row`, the resulting carrys can be out of bound
566+
/// Puts the correct carries in the `next_row`, the resulting carries can be out of bounds
567567
pub fn generate_carry_ae<F: PrimeField32>(
568568
local_cols: ShaRoundColsRef<F>,
569569
next_cols: &mut ShaRoundColsRefMut<F>,

docs/specs/ISA.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,8 @@ meaning all memory cells are constrained to be bytes.
546546
| Name | Operands | Description |
547547
| ----------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
548548
| SHA256_RV32 | `a,b,c,1,2` | `[r32{0}(a):32]_2 = sha256([r32{0}(b)..r32{0}(b)+r32{0}(c)]_2)`. Does the necessary padding. Performs memory reads with block size `16` and writes with block size `32`. |
549-
| SHA512_RV32 | `a,b,c,1,2` | `[r32{0}(a):64]_2 = sha512([r32{0}(b)..r32{0}(b)+r32{0}(c)]_2)`. Does the necessary padding. Performs memory reads with block size `16` and writes with block size `64`. |
549+
| SHA512_RV32 | `a,b,c,1,2` | `[r32{0}(a):64]_2 = sha512([r32{0}(b)..r32{0}(b)+r32{0}(c)]_2)`. Does the necessary padding. Performs memory reads with block size `16` and writes with block size `32`. |
550+
| SHA384_RV32 | `a,b,c,1,2` | `[r32{0}(a):64]_2 = sha384([r32{0}(b)..r32{0}(b)+r32{0}(c)]_2)`. Does the necessary padding. Performs memory reads with block size `16` and writes with block size `32`. Writes 64 bytes to memory: the first 48 are the SHA-384 digest and the last 16 are zeros. |
550551

551552
### BigInt Extension
552553

docs/specs/RISCV.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ the guest must take care to validate all data and account for behavior in cases
8484
| ----------- | --- | ----------- | ------ | ------ | ---------------------------------------- |
8585
| sha256 | R | 0001011 | 100 | 0x1 | `[rd:32]_2 = sha256([rs1..rs1 + rs2]_2)` |
8686
| sha512 | R | 0001011 | 100 | 0x2 | `[rd:32]_2 = sha512([rs1..rs1 + rs2]_2)` |
87+
| sha384 | R | 0001011 | 100 | 0x3 | `[rd:32]_2 = sha384([rs1..rs1 + rs2]_2)` |
8788

8889
## BigInt Extension
8990

docs/specs/transpiler.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ Each VM extension's behavior is specified below.
157157
| ----------- | ----------------------------------------------- |
158158
| sha256 | SHA256_RV32 `ind(rd), ind(rs1), ind(rs2), 1, 2` |
159159
| sha512 | SHA512_RV32 `ind(rd), ind(rs1), ind(rs2), 1, 2` |
160+
| sha384 | SHA384_RV32 `ind(rd), ind(rs1), ind(rs2), 1, 2` |
160161

161162
### BigInt Extension
162163

extensions/sha256/circuit/src/extension.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use openvm_rv32im_circuit::{
1414
Rv32MExecutor, Rv32MPeriphery,
1515
};
1616
use openvm_sha256_transpiler::Rv32Sha2Opcode;
17-
use openvm_sha_air::{Sha256Config, Sha512Config};
17+
use openvm_sha_air::{Sha256Config, Sha384Config, Sha512Config};
1818
use openvm_stark_backend::p3_field::PrimeField32;
1919
use serde::{Deserialize, Serialize};
2020

@@ -53,6 +53,7 @@ pub struct Sha2;
5353
pub enum Sha2Executor<F: PrimeField32> {
5454
Sha256(Sha2VmChip<F, Sha256Config>),
5555
Sha512(Sha2VmChip<F, Sha512Config>),
56+
Sha384(Sha2VmChip<F, Sha384Config>),
5657
}
5758

5859
#[derive(From, ChipUsageGetter, Chip, AnyEnum)]
@@ -95,13 +96,23 @@ impl<F: PrimeField32> VmExtension<F> for Sha2 {
9596
let sha512_chip = Sha2VmChip::<F, Sha512Config>::new(
9697
builder.system_port(),
9798
builder.system_config().memory_config.pointer_max_bits,
98-
bitwise_lu_chip,
99+
bitwise_lu_chip.clone(),
99100
builder.new_bus_idx(),
100101
Rv32Sha2Opcode::CLASS_OFFSET,
101102
builder.system_base().offline_memory(),
102103
);
103104
inventory.add_executor(sha512_chip, vec![Rv32Sha2Opcode::SHA512.global_opcode()])?;
104105

106+
let sha384_chip = Sha2VmChip::<F, Sha384Config>::new(
107+
builder.system_port(),
108+
builder.system_config().memory_config.pointer_max_bits,
109+
bitwise_lu_chip,
110+
builder.new_bus_idx(),
111+
Rv32Sha2Opcode::CLASS_OFFSET,
112+
builder.system_base().offline_memory(),
113+
);
114+
inventory.add_executor(sha384_chip, vec![Rv32Sha2Opcode::SHA384.global_opcode()])?;
115+
105116
Ok(inventory)
106117
}
107118
}

extensions/sha256/circuit/src/sha256_chip/air.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::cmp::min;
2+
use std::convert::TryInto;
23

34
use openvm_circuit::{
45
arch::ExecutionBridge,
@@ -457,7 +458,7 @@ impl<C: ShaChipConfig> Sha2VmAir<C> {
457458
.when(not::<AB::Expr>(is_last_row.clone()))
458459
.assert_eq(*next_cols.control.len, *local_cols.control.len);
459460

460-
// Read ptr should increment by [SHA256_READ_SIZE] for the first 4 rows and stay the same otherwise
461+
// Read ptr should increment by [C::READ_SIZE] for the first 4 rows and stay the same otherwise
461462
let read_ptr_delta =
462463
*local_cols.inner.flags.is_first_4_rows * AB::Expr::from_canonical_usize(C::READ_SIZE);
463464
builder
@@ -510,7 +511,8 @@ impl<C: ShaChipConfig> Sha2VmAir<C> {
510511
)
511512
.eval(builder, *local_cols.inner.flags.is_first_4_rows);
512513
}
513-
Sha2Variant::Sha512 => {
514+
// Sha512 and Sha384 have the same read size so we put them together
515+
Sha2Variant::Sha512 | Sha2Variant::Sha384 => {
514516
let message: [AB::Var; Sha512Config::READ_SIZE] =
515517
message.try_into().unwrap_or_else(|_| {
516518
panic!("message is not the correct size");
@@ -610,10 +612,10 @@ impl<C: ShaChipConfig> Sha2VmAir<C> {
610612
// the number of reads that happened to read the entire message: we do 4 reads per block
611613
let time_delta = (*local_cols.inner.flags.local_block_idx + AB::Expr::ONE)
612614
* AB::Expr::from_canonical_usize(4);
613-
// Every time we read the message we increment the read pointer by SHA256_READ_SIZE
615+
// Every time we read the message we increment the read pointer by C::READ_SIZE
614616
let read_ptr_delta = time_delta.clone() * AB::Expr::from_canonical_usize(C::READ_SIZE);
615617

616-
let result: Vec<AB::Var> = (0..C::DIGEST_SIZE)
618+
let result: Vec<AB::Var> = (0..C::HASH_SIZE)
617619
.map(|i| {
618620
// The limbs are written in big endian order to the memory so need to be reversed
619621
local_cols.inner.final_hash[[i / C::WORD_U8S, C::WORD_U8S - i % C::WORD_U8S - 1]]
@@ -630,7 +632,7 @@ impl<C: ShaChipConfig> Sha2VmAir<C> {
630632
debug_assert_eq!(C::NUM_WRITES, 1);
631633
debug_assert_eq!(local_cols.writes_aux_base.len(), 1);
632634
debug_assert_eq!(local_cols.writes_aux_prev_data.nrows(), 1);
633-
let prev_data: [AB::Var; Sha256Config::DIGEST_SIZE] = local_cols
635+
let prev_data: [AB::Var; Sha256Config::HASH_SIZE] = local_cols
634636
.writes_aux_prev_data
635637
.row(0)
636638
.to_vec()
@@ -654,13 +656,20 @@ impl<C: ShaChipConfig> Sha2VmAir<C> {
654656
)
655657
.eval(builder, is_last_row.clone());
656658
}
657-
Sha2Variant::Sha512 => {
659+
Sha2Variant::Sha512 | Sha2Variant::Sha384 => {
658660
debug_assert_eq!(C::NUM_WRITES, 2);
659661
debug_assert_eq!(local_cols.writes_aux_base.len(), 2);
660662
debug_assert_eq!(local_cols.writes_aux_prev_data.nrows(), 2);
663+
664+
// For Sha384, set the last 16 cells to 0
665+
let mut truncated_result: Vec<AB::Expr> =
666+
result.iter().map(|x| (*x).into()).collect();
667+
for x in truncated_result.iter_mut().skip(C::DIGEST_SIZE) {
668+
*x = AB::Expr::ZERO;
669+
}
670+
661671
// write the digest in two halves because we only support writes up to 32 bytes
662672
for i in 0..Sha512Config::NUM_WRITES {
663-
// for i in 0..1 {
664673
let prev_data: [AB::Var; Sha512Config::WRITE_SIZE] = local_cols
665674
.writes_aux_prev_data
666675
.row(i)
@@ -677,8 +686,9 @@ impl<C: ShaChipConfig> Sha2VmAir<C> {
677686
dst_ptr_val.clone()
678687
+ AB::Expr::from_canonical_usize(i * Sha512Config::WRITE_SIZE),
679688
),
680-
result
689+
truncated_result
681690
[i * Sha512Config::WRITE_SIZE..(i + 1) * Sha512Config::WRITE_SIZE]
691+
.to_vec()
682692
.try_into()
683693
.unwrap_or_else(|_| {
684694
panic!("result is not the correct size");

extensions/sha256/circuit/src/sha256_chip/columns.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ pub struct ShaVmDigestCols<
5353
const ROUNDS_PER_ROW: usize,
5454
const ROUNDS_PER_ROW_MINUS_ONE: usize,
5555
const ROW_VAR_CNT: usize,
56-
const DIGEST_SIZE: usize,
5756
const NUM_WRITES: usize,
5857
const WRITE_SIZE: usize,
5958
> {
@@ -85,8 +84,6 @@ pub struct ShaVmDigestCols<
8584
pub writes_aux_prev_data: [[T; WRITE_SIZE]; NUM_WRITES],
8685
}
8786

88-
pub type Sha256VmDigestCols<F> = ShaVmDigestCols<F, 32, 4, 2, 8, 4, 3, 5, 32, 1, 32>;
89-
9087
/// These are the columns that are used on both round and digest rows
9188
#[repr(C)]
9289
#[derive(Clone, Copy, Debug, ColsRef)]

extensions/sha256/circuit/src/sha256_chip/config.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use openvm_instructions::riscv::RV32_CELL_BITS;
22
use openvm_sha256_transpiler::Rv32Sha2Opcode;
3-
use openvm_sha_air::{Sha256Config, Sha512Config, ShaConfig};
3+
use openvm_sha_air::{Sha256Config, Sha384Config, Sha512Config, ShaConfig};
44

55
use super::{ShaVmControlColsRef, ShaVmDigestColsRef, ShaVmRoundColsRef};
66

77
pub enum Sha2Variant {
88
Sha256,
99
Sha512,
10+
Sha384,
1011
}
1112

1213
pub trait ShaChipConfig: ShaConfig {
@@ -44,13 +45,15 @@ pub trait ShaChipConfig: ShaConfig {
4445

4546
/// Number of cells to read in a single memory access
4647
const READ_SIZE: usize = Self::WORD_U8S * Self::ROUNDS_PER_ROW;
47-
/// Number of cells in the digest
48-
const DIGEST_SIZE: usize = Self::WORD_U8S * Self::HASH_WORDS;
49-
/// Digest will be written in NUM_WRITES parts of equal size
50-
/// NUM_WRITES must divide DIGEST_SIZE
48+
/// Number of cells in the digest before truncation (Sha384 truncates the digest)
49+
const HASH_SIZE: usize = Self::WORD_U8S * Self::HASH_WORDS;
50+
/// Number of cells in the digest after truncation
51+
const DIGEST_SIZE: usize;
52+
53+
/// Number of parts to write the hash in. Must divide HASH_SIZE
5154
const NUM_WRITES: usize;
5255
/// Size of each write
53-
const WRITE_SIZE: usize = Self::DIGEST_SIZE / Self::NUM_WRITES;
56+
const WRITE_SIZE: usize = Self::HASH_SIZE / Self::NUM_WRITES;
5457
}
5558

5659
/// Register reads to get dst, src, len
@@ -62,6 +65,8 @@ impl ShaChipConfig for Sha256Config {
6265
const MESSAGE_LENGTH_BITS: usize = 64;
6366
const NUM_WRITES: usize = 1;
6467
const OPCODE: Rv32Sha2Opcode = Rv32Sha2Opcode::SHA256;
68+
// no truncation
69+
const DIGEST_SIZE: usize = Self::HASH_SIZE;
6570
}
6671

6772
// Currently same as Sha256Config, but can configure later
@@ -72,4 +77,17 @@ impl ShaChipConfig for Sha512Config {
7277
// Use 2 writes because we only support writes up to 32 bytes
7378
const NUM_WRITES: usize = 2;
7479
const OPCODE: Rv32Sha2Opcode = Rv32Sha2Opcode::SHA512;
80+
// no truncation
81+
const DIGEST_SIZE: usize = Self::HASH_SIZE;
82+
}
83+
84+
impl ShaChipConfig for Sha384Config {
85+
const VARIANT: Sha2Variant = Sha2Variant::Sha384;
86+
const OPCODE_NAME: &'static str = "SHA384";
87+
const MESSAGE_LENGTH_BITS: usize = 128;
88+
// Use 2 writes because we only support writes up to 32 bytes
89+
const NUM_WRITES: usize = 2;
90+
const OPCODE: Rv32Sha2Opcode = Rv32Sha2Opcode::SHA384;
91+
// Sha284 truncates the output to 48 cells
92+
const DIGEST_SIZE: usize = 48;
7593
}

0 commit comments

Comments
 (0)