Skip to content

Commit 55d1046

Browse files
Support SHA-512
1 parent 816edcc commit 55d1046

File tree

29 files changed

+2188
-834
lines changed

29 files changed

+2188
-834
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ rrs-lib = "0.1.0"
212212
rand = { version = "0.8.5", default-features = false }
213213
hex = { version = "0.4.3", default-features = false }
214214
serde-big-array = "0.5.1"
215+
ndarray = "0.16.1"
215216

216217
# default-features = false for no_std for use in guest programs
217218
itertools = { version = "0.14.0", default-features = false }

crates/circuits/sha-air/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ openvm-stark-backend = { workspace = true }
1010
sha2 = { version = "0.10", features = ["compress"] }
1111
rand.workspace = true
1212
openvm-sha-macros = { workspace = true }
13-
ndarray = "0.16"
13+
ndarray.workspace = true
1414

1515
[dev-dependencies]
1616
openvm-stark-sdk = { workspace = true }

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

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ use crate::{
2424

2525
/// Expects the message to be padded to a multiple of C::BLOCK_WORDS * C::WORD_BITS bits
2626
#[derive(Clone, Debug)]
27-
pub struct ShaAir<C: ShaConfig> {
27+
pub struct Sha2Air<C: ShaConfig> {
2828
pub bitwise_lookup_bus: BitwiseOperationLookupBus,
2929
pub row_idx_encoder: Encoder,
3030
/// Internal bus for self-interactions in this AIR.
3131
bus: PermutationCheckBus,
3232
_phantom: PhantomData<C>,
3333
}
3434

35-
impl<C: ShaConfig> ShaAir<C> {
35+
impl<C: ShaConfig> Sha2Air<C> {
3636
pub fn new(bitwise_lookup_bus: BitwiseOperationLookupBus, self_bus_idx: BusIndex) -> Self {
3737
Self {
3838
bitwise_lookup_bus,
@@ -43,13 +43,13 @@ impl<C: ShaConfig> ShaAir<C> {
4343
}
4444
}
4545

46-
impl<F, C: ShaConfig> BaseAir<F> for ShaAir<C> {
46+
impl<F, C: ShaConfig> BaseAir<F> for Sha2Air<C> {
4747
fn width(&self) -> usize {
4848
max(C::ROUND_WIDTH, C::DIGEST_WIDTH)
4949
}
5050
}
5151

52-
impl<AB: InteractionBuilder, C: ShaConfig> SubAir<AB> for ShaAir<C> {
52+
impl<AB: InteractionBuilder, C: ShaConfig> SubAir<AB> for Sha2Air<C> {
5353
/// The start column for the sub-air to use
5454
type AirContext<'a>
5555
= usize
@@ -69,7 +69,7 @@ impl<AB: InteractionBuilder, C: ShaConfig> SubAir<AB> for ShaAir<C> {
6969
}
7070
}
7171

72-
impl<C: ShaConfig> ShaAir<C> {
72+
impl<C: ShaConfig> Sha2Air<C> {
7373
/// Implements the single row constraints (i.e. imposes constraints only on local)
7474
/// Implements some sanity constraints on the row index, flags, and work variables
7575
fn eval_row<AB: InteractionBuilder>(&self, builder: &mut AB, start_col: usize) {
@@ -211,30 +211,35 @@ impl<C: ShaConfig> ShaAir<C> {
211211
for j in 0..C::WORD_U16S {
212212
let work_var_limb = if i < C::ROUNDS_PER_ROW {
213213
compose::<AB::Expr>(
214-
&local
214+
local
215215
.work_vars
216216
.a
217217
.slice(s![C::ROUNDS_PER_ROW - 1 - i, j * 16..(j + 1) * 16])
218-
.as_slice().unwrap(),
218+
.as_slice()
219+
.unwrap(),
219220
1,
220221
)
221222
} else {
222223
compose::<AB::Expr>(
223-
&local
224+
local
224225
.work_vars
225226
.e
226227
.slice(s![C::ROUNDS_PER_ROW + 3 - i, j * 16..(j + 1) * 16])
227-
.as_slice().unwrap(),
228+
.as_slice()
229+
.unwrap(),
228230
1,
229231
)
230232
};
231233
let final_hash_limb = compose::<AB::Expr>(
232-
&next.final_hash.slice(s![i, j * 2..(j + 1) * 2]).as_slice().unwrap(),
234+
next.final_hash
235+
.slice(s![i, j * 2..(j + 1) * 2])
236+
.as_slice()
237+
.unwrap(),
233238
8,
234239
);
235240

236241
carry = AB::Expr::from(AB::F::from_canonical_u32(1 << 16).inverse())
237-
* (next.prev_hash[[i,j]] + work_var_limb + carry - final_hash_limb);
242+
* (next.prev_hash[[i, j]] + work_var_limb + carry - final_hash_limb);
238243
builder
239244
.when(*next.flags.is_digest_row)
240245
.assert_bool(carry.clone());

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ pub struct ShaFlagsCols<T, const ROW_VAR_CNT: usize> {
127127
pub is_digest_row: T,
128128
pub is_last_block: T,
129129
/// We will encode the row index [0..17) using 5 cells
130-
//#[length(ROW_VAR_CNT)]
131130
pub row_idx: [T; ROW_VAR_CNT],
132131
/// The global index of the current block
133132
pub global_block_idx: T,
@@ -150,7 +149,7 @@ impl<O, T: Copy + core::ops::Add<Output = O>, const ROW_VAR_CNT: usize>
150149
}
151150
}
152151

153-
impl<'a, O, T: Copy + core::ops::Add<Output = O>> ShaFlagsColsRef<'a, T> {
152+
impl<O, T: Copy + core::ops::Add<Output = O>> ShaFlagsColsRef<'_, T> {
154153
pub fn is_not_padding_row(&self) -> O {
155154
*self.is_round_row + *self.is_digest_row
156155
}

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

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
22

3+
use crate::{ShaDigestColsRef, ShaRoundColsRef};
4+
35
pub trait ShaConfig: Send + Sync + Clone {
46
type Word: 'static
57
+ Shr<usize, Output = Self::Word>
@@ -44,48 +46,25 @@ pub trait ShaConfig: Send + Sync + Clone {
4446
const HASH_WORDS: usize;
4547
/// Number of vars needed to encode the row index with [Encoder]
4648
const ROW_VAR_CNT: usize;
47-
4849
/// Width of the ShaRoundCols
49-
const ROUND_WIDTH: usize = Self::FLAGS_WIDTH
50-
+ Self::WORK_VARS_WIDTH
51-
+ Self::MESSAGE_HELPER_WIDTH
52-
+ Self::MESSAGE_SCHEDULE_WIDTH;
50+
const ROUND_WIDTH: usize = ShaRoundColsRef::<u8>::width::<Self>();
5351
/// Width of the ShaDigestCols
54-
const DIGEST_WIDTH: usize = Self::FLAGS_WIDTH
55-
+ Self::WORK_VARS_WIDTH
56-
+ Self::MESSAGE_HELPER_WIDTH
57-
+ Self::MESSAGE_SCHEDULE_WIDTH
58-
+ Self::WORD_U8S * Self::HASH_WORDS
59-
+ Self::WORD_U16S * Self::HASH_WORDS;
60-
/// Width of the ShaFlagsCols
61-
const FLAGS_WIDTH: usize = Self::ROW_VAR_CNT + 6;
62-
/// Width of the ShaWorkVarsCols
63-
const WORK_VARS_WIDTH: usize =
64-
2 * Self::WORD_BITS * Self::ROUNDS_PER_ROW + 2 * Self::WORD_U16S * Self::ROUNDS_PER_ROW;
65-
// Width of the ShaMessageHelperCols
66-
const MESSAGE_HELPER_WIDTH: usize =
67-
Self::WORD_U8S * (Self::ROUNDS_PER_ROW - 1) + 3 * Self::WORD_U16S * Self::ROUNDS_PER_ROW;
68-
/// Width of the ShaMessageScheduleCols
69-
const MESSAGE_SCHEDULE_WIDTH: usize =
70-
Self::WORD_BITS * Self::ROUNDS_PER_ROW + Self::WORD_U8S * Self::ROUNDS_PER_ROW;
71-
72-
/// Size of the buffer of the first 4 rows of a block (each row's size)
73-
const BUFFER_SIZE: usize = Self::ROUNDS_PER_ROW * Self::WORD_U8S;
52+
const DIGEST_WIDTH: usize = ShaDigestColsRef::<u8>::width::<Self>();
7453
/// Width of the ShaCols
7554
const WIDTH: usize = if Self::ROUND_WIDTH > Self::DIGEST_WIDTH {
7655
Self::ROUND_WIDTH
7756
} else {
7857
Self::DIGEST_WIDTH
7958
};
80-
}
59+
/// Size of the buffer of the first 4 rows of a block (each row's size)
60+
const BUFFER_SIZE: usize = Self::ROUNDS_PER_ROW * Self::WORD_U8S;
8161

82-
/// We can notice that `carry_a`'s and `carry_e`'s are always the same on invalid rows
83-
/// To optimize the trace generation of invalid rows, we precompute those values.
84-
/// This trait also stores the constants K and H for the given SHA config.
85-
pub trait ShaPrecomputedValues<T> {
62+
/// To optimize the trace generation of invalid rows, we precompute those values.
8663
// these should be appropriately sized for the config
8764
fn get_invalid_carry_a(round_num: usize) -> &'static [u32];
8865
fn get_invalid_carry_e(round_num: usize) -> &'static [u32];
66+
67+
/// We also store the SHA constants K and H
8968
fn get_k() -> &'static [Self::Word];
9069
fn get_h() -> &'static [Self::Word];
9170
}

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

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

44
use openvm_circuit::arch::{
55
instructions::riscv::RV32_CELL_BITS,
@@ -22,21 +22,19 @@ use openvm_stark_backend::{
2222
use openvm_stark_sdk::utils::create_seeded_rng;
2323
use rand::Rng;
2424

25-
use crate::{
26-
compose, small_sig0_field, Sha256Config, Sha512Config, ShaAir, ShaConfig,
27-
};
25+
use crate::{compose, small_sig0_field, Sha256Config, Sha512Config, Sha2Air, ShaConfig};
2826

2927
// A wrapper AIR purely for testing purposes
3028
#[derive(Clone, Debug)]
3129
pub struct ShaTestAir<C: ShaConfig> {
32-
pub sub_air: ShaAir<C>,
30+
pub sub_air: Sha2Air<C>,
3331
}
3432

3533
impl<F: Field, C: ShaConfig> BaseAirWithPublicValues<F> for ShaTestAir<C> {}
3634
impl<F: Field, C: ShaConfig> PartitionedBaseAir<F> for ShaTestAir<C> {}
3735
impl<F: Field, C: ShaConfig> BaseAir<F> for ShaTestAir<C> {
3836
fn width(&self) -> usize {
39-
<ShaAir<C> as BaseAir<F>>::width(&self.sub_air)
37+
<Sha2Air<C> as BaseAir<F>>::width(&self.sub_air)
4038
}
4139
}
4240

@@ -103,7 +101,7 @@ fn rand_sha_test<C: ShaConfig + 'static>() {
103101
.collect();
104102
let chip = ShaTestChip {
105103
air: ShaTestAir {
106-
sub_air: ShaAir::<C>::new(bitwise_bus, SELF_BUS_IDX),
104+
sub_air: Sha2Air::<C>::new(bitwise_bus, SELF_BUS_IDX),
107105
},
108106
bitwise_lookup_chip: bitwise_chip.clone(),
109107
records: random_records,
@@ -125,14 +123,13 @@ fn rand_sha512_test() {
125123

126124
// A wrapper Chip to test that the final_hash is properly constrained.
127125
// This chip implements a malicious trace gen that violates the final_hash constraints.
128-
pub struct ShaTestBadFinalHashChip<C: ShaConfig + ShaPrecomputedValues<C::Word>> {
126+
pub struct ShaTestBadFinalHashChip<C: ShaConfig> {
129127
pub air: ShaTestAir<C>,
130128
pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip<8>,
131129
pub records: Vec<(Vec<u8>, bool)>, // length of inner vec should be C::BLOCK_U8S
132130
}
133131

134-
impl<SC: StarkGenericConfig, C: ShaConfig + ShaPrecomputedValues<C::Word> + 'static> Chip<SC>
135-
for ShaTestBadFinalHashChip<C>
132+
impl<SC: StarkGenericConfig, C: ShaConfig + 'static> Chip<SC> for ShaTestBadFinalHashChip<C>
136133
where
137134
Val<SC>: PrimeField32,
138135
{
@@ -170,12 +167,15 @@ where
170167
let mut last_digest_row: crate::ShaRoundColsRefMut<Val<SC>> =
171168
ShaRoundColsRefMut::from::<C>(last_digest_row.borrow_mut());
172169
// fix the intermed_4 for the digest row
173-
generate_intermed_4::<Val<SC>, C>(&ShaRoundColsRef::from_mut::<C>(&last_round_row), &mut last_digest_row);
170+
generate_intermed_4::<Val<SC>, C>(
171+
&ShaRoundColsRef::from_mut::<C>(&last_round_row),
172+
&mut last_digest_row,
173+
);
174174
}
175175
}
176176

177177
let non_padded_height = self.records.len() * C::ROWS_PER_BLOCK;
178-
let width = <ShaAir<C> as BaseAir<Val<SC>>>::width(&self.air.sub_air);
178+
let width = <Sha2Air<C> as BaseAir<Val<SC>>>::width(&self.air.sub_air);
179179
// recalculate the missing cells (second pass of generate_trace)
180180
trace.values[width..]
181181
.par_chunks_mut(width * C::ROWS_PER_BLOCK)
@@ -190,7 +190,7 @@ where
190190

191191
// Copy of private method in Sha256Air used for testing
192192
/// Puts the correct intermed_4 in the `next_row`
193-
fn generate_intermed_4<F: PrimeField32, C: ShaConfig + ShaPrecomputedValues<C::Word>>(
193+
fn generate_intermed_4<F: PrimeField32, C: ShaConfig>(
194194
local_cols: &ShaRoundColsRef<F>,
195195
next_cols: &mut ShaRoundColsRefMut<F>,
196196
) {
@@ -209,10 +209,9 @@ fn generate_intermed_4<F: PrimeField32, C: ShaConfig + ShaPrecomputedValues<C::W
209209
.collect::<Vec<_>>(),
210210
]
211211
.concat();
212-
213-
212+
214213
// length of inner vec is C::WORD_U16S
215-
let w_limbs: Vec<Vec<F>> = w
214+
let w_limbs: Vec<Vec<F>> = w
216215
.iter()
217216
.map(|x| {
218217
(0..C::WORD_U16S)
@@ -231,7 +230,7 @@ fn generate_intermed_4<F: PrimeField32, C: ShaConfig + ShaPrecomputedValues<C::W
231230
}
232231
}
233232

234-
impl<C: ShaConfig + ShaPrecomputedValues<C::Word>> ChipUsageGetter for ShaTestBadFinalHashChip<C> {
233+
impl<C: ShaConfig> ChipUsageGetter for ShaTestBadFinalHashChip<C> {
235234
fn air_name(&self) -> String {
236235
get_air_name(&self.air)
237236
}
@@ -244,18 +243,25 @@ impl<C: ShaConfig + ShaPrecomputedValues<C::Word>> ChipUsageGetter for ShaTestBa
244243
}
245244
}
246245

247-
fn test_sha_final_hash_constraints<C: ShaConfig + ShaPrecomputedValues<C::Word> + 'static>() {
246+
fn test_sha_final_hash_constraints<C: ShaConfig + 'static>() {
248247
let mut rng = create_seeded_rng();
249248
let tester = VmChipTestBuilder::default();
250249
let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS);
251250
let bitwise_chip = SharedBitwiseOperationLookupChip::<RV32_CELL_BITS>::new(bitwise_bus);
252251
let len = rng.gen_range(1..100);
253252
let random_records: Vec<_> = (0..len)
254-
.map(|_| ((0..C::BLOCK_U8S).map(|_| rng.gen::<u8>()).collect::<Vec<_>>(), true))
253+
.map(|_| {
254+
(
255+
(0..C::BLOCK_U8S)
256+
.map(|_| rng.gen::<u8>())
257+
.collect::<Vec<_>>(),
258+
true,
259+
)
260+
})
255261
.collect();
256262
let chip = ShaTestBadFinalHashChip {
257263
air: ShaTestAir {
258-
sub_air: ShaAir::<C>::new(bitwise_bus, SELF_BUS_IDX),
264+
sub_air: Sha2Air::<C>::new(bitwise_bus, SELF_BUS_IDX),
259265
},
260266
bitwise_lookup_chip: bitwise_chip.clone(),
261267
records: random_records,

0 commit comments

Comments
 (0)