1use midnight_proofs::{
41 circuit::{Chip, Layouter, Region, Value},
42 plonk::{
43 Advice, Column, ConstraintSystem, Constraints, Error, Expression, Fixed, Selector,
44 TableColumn,
45 },
46 poly::Rotation,
47};
48use num_integer::Integer;
49
50use crate::{
51 field::{decomposition::chip::P2RDecompositionChip, NativeChip, NativeGadget},
52 hash::sha512::{
53 types::{
54 AssignedMessageWord, AssignedPlain, AssignedPlainSpreaded, AssignedSpreaded,
55 CompressionState, LimbsOfA, LimbsOfE,
56 },
57 utils::{
58 expr_pow2_ip, expr_pow4_ip, gen_spread_table, get_even_and_odd_bits, negate_spreaded,
59 spread, spreaded_Sigma_0, spreaded_Sigma_1, spreaded_maj, spreaded_sigma_0,
60 spreaded_sigma_1, u64_in_be_limbs, MASK_EVN_128,
61 },
62 },
63 instructions::{assignments::AssignmentInstructions, DecompositionInstructions},
64 types::{AssignedByte, AssignedNative},
65 utils::{
66 util::{fe_to_u128, fe_to_u64, u128_to_fe, u64_to_fe},
67 ComposableChip,
68 },
69 CircuitField,
70};
71
72pub const NB_SHA512_ADVICE_COLS: usize = 8;
74
75pub const NB_SHA512_FIXED_COLS: usize = 2;
77
78#[rustfmt::skip]
79const ROUND_CONSTANTS: [u64; 80] = [
80 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
81 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
82 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
83 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
84 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
85 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
86 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
87 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
88 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
89 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
90 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
91 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
92 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
93 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
94 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
95 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
96 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
97 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
98 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
99 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
100];
101
102#[rustfmt::skip]
103const IV: [u64; 8] = [
104 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
105 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
106];
107
108enum Parity {
110 Evn,
111 Odd,
112}
113
114#[derive(Clone, Debug)]
116struct SpreadTable {
117 nbits_col: TableColumn,
118 plain_col: TableColumn,
119 sprdd_col: TableColumn,
120}
121
122#[derive(Clone, Debug)]
124pub struct Sha512Config {
125 advice_cols: [Column<Advice>; NB_SHA512_ADVICE_COLS],
126 fixed_cols: [Column<Fixed>; NB_SHA512_FIXED_COLS],
127
128 q_lookup: Selector,
129 table: SpreadTable,
130
131 q_maj: Selector,
132 q_half_ch: Selector,
133 q_Sigma_0: Selector,
134 q_Sigma_1: Selector,
135 q_sigma_0: Selector,
136 q_sigma_1: Selector,
137
138 q_13x4_12: Selector,
139 q_13_12_5_6_13_13_2: Selector,
140 q_13_10_13_10_4_13_1: Selector,
141 q_3_13x3_3_11_1_1_5_1: Selector,
142 q_add_mod_2_64: Selector,
143}
144
145#[derive(Clone, Debug)]
147pub struct Sha512Chip<F: CircuitField> {
148 config: Sha512Config,
149 pub(super) native_gadget: NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>,
150}
151
152impl<F: CircuitField> Chip<F> for Sha512Chip<F> {
153 type Config = Sha512Config;
154 type Loaded = ();
155
156 fn config(&self) -> &Self::Config {
157 &self.config
158 }
159
160 fn loaded(&self) -> &Self::Loaded {
161 &()
162 }
163}
164
165impl<F: CircuitField> ComposableChip<F> for Sha512Chip<F> {
166 type SharedResources = (
167 [Column<Advice>; NB_SHA512_ADVICE_COLS],
168 [Column<Fixed>; NB_SHA512_FIXED_COLS],
169 );
170
171 type InstructionDeps = NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>;
172
173 fn new(config: &Sha512Config, native_gadget: &Self::InstructionDeps) -> Self {
174 Self {
175 config: config.clone(),
176 native_gadget: native_gadget.clone(),
177 }
178 }
179
180 fn configure(
181 meta: &mut ConstraintSystem<F>,
182 shared_res: &Self::SharedResources,
183 ) -> Sha512Config {
184 let fixed_cols = shared_res.1;
185
186 let advice_cols = [6, 0, 7, 1, 2, 3, 4, 5].map(|i| shared_res.0[i]);
191 for (i, column) in advice_cols.iter().enumerate() {
192 if i != 0 && i != 2 {
193 meta.enable_equality(*column);
194 }
195 }
196
197 let q_lookup = meta.complex_selector();
198 let table = SpreadTable {
199 nbits_col: meta.lookup_table_column(),
200 plain_col: meta.lookup_table_column(),
201 sprdd_col: meta.lookup_table_column(),
202 };
203
204 let q_maj = meta.selector();
205 let q_half_ch = meta.selector();
206 let q_Sigma_0 = meta.selector();
207 let q_Sigma_1 = meta.selector();
208 let q_sigma_0 = meta.selector();
209 let q_sigma_1 = meta.selector();
210
211 let q_13x4_12 = meta.selector();
212 let q_13_12_5_6_13_13_2 = meta.selector();
213 let q_13_10_13_10_4_13_1 = meta.selector();
214 let q_3_13x3_3_11_1_1_5_1 = meta.selector();
215 let q_add_mod_2_64 = meta.selector();
216
217 (0..2).for_each(|idx| {
218 meta.lookup("plain-spreaded lookup", |meta| {
219 let q_lookup = meta.query_selector(q_lookup);
220
221 let nbits = meta.query_fixed(fixed_cols[idx], Rotation(0));
222 let plain = meta.query_advice(advice_cols[2 * idx], Rotation(0));
223 let sprdd = meta.query_advice(advice_cols[2 * idx + 1], Rotation(0));
224
225 vec![
226 (q_lookup.clone() * nbits, table.nbits_col),
227 (q_lookup.clone() * plain, table.plain_col),
228 (q_lookup * sprdd, table.sprdd_col),
229 ]
230 });
231 });
232
233 meta.create_gate("Maj(A, B, C)", |meta| {
234 let sA = meta.query_advice(advice_cols[5], Rotation(-1));
236 let sB = meta.query_advice(advice_cols[6], Rotation(-1));
237 let sC = meta.query_advice(advice_cols[5], Rotation(0));
238 let s_odd_13a = meta.query_advice(advice_cols[1], Rotation(-1));
239 let s_odd_13b = meta.query_advice(advice_cols[1], Rotation(0));
240 let s_odd_13c = meta.query_advice(advice_cols[1], Rotation(1));
241 let s_odd_13d = meta.query_advice(advice_cols[1], Rotation(2));
242 let s_odd_12 = meta.query_advice(advice_cols[1], Rotation(3));
243 let s_evn_13a = meta.query_advice(advice_cols[3], Rotation(-1));
244 let s_evn_13b = meta.query_advice(advice_cols[3], Rotation(0));
245 let s_evn_13c = meta.query_advice(advice_cols[3], Rotation(1));
246 let s_evn_13d = meta.query_advice(advice_cols[3], Rotation(2));
247 let s_evn_12 = meta.query_advice(advice_cols[3], Rotation(3));
248
249 let s_evn = expr_pow4_ip(
250 [51, 38, 25, 12, 0],
251 [&s_evn_13a, &s_evn_13b, &s_evn_13c, &s_evn_13d, &s_evn_12],
252 );
253 let s_odd = expr_pow4_ip(
254 [51, 38, 25, 12, 0],
255 [&s_odd_13a, &s_odd_13b, &s_odd_13c, &s_odd_13d, &s_odd_12],
256 );
257
258 let id = (sA + sB + sC) - (s_evn + Expression::from(2) * s_odd);
259
260 Constraints::with_selector(q_maj, vec![("Maj", id)])
261 });
262
263 meta.create_gate("half Ch(E, F, G)", |meta| {
264 let sX = meta.query_advice(advice_cols[5], Rotation(-1));
266 let sY = meta.query_advice(advice_cols[6], Rotation(-1));
267 let s_odd_13a = meta.query_advice(advice_cols[1], Rotation(-1));
268 let s_odd_13b = meta.query_advice(advice_cols[1], Rotation(0));
269 let s_odd_13c = meta.query_advice(advice_cols[1], Rotation(1));
270 let s_odd_13d = meta.query_advice(advice_cols[1], Rotation(2));
271 let s_odd_12 = meta.query_advice(advice_cols[1], Rotation(3));
272 let s_evn_13a = meta.query_advice(advice_cols[3], Rotation(-1));
273 let s_evn_13b = meta.query_advice(advice_cols[3], Rotation(0));
274 let s_evn_13c = meta.query_advice(advice_cols[3], Rotation(1));
275 let s_evn_13d = meta.query_advice(advice_cols[3], Rotation(2));
276 let s_evn_12 = meta.query_advice(advice_cols[3], Rotation(3));
277 let summand_1 = meta.query_advice(advice_cols[4], Rotation(0));
278 let summand_2 = meta.query_advice(advice_cols[5], Rotation(0));
279 let sum = meta.query_advice(advice_cols[6], Rotation(0));
280
281 let s_evn = expr_pow4_ip(
282 [51, 38, 25, 12, 0],
283 [&s_evn_13a, &s_evn_13b, &s_evn_13c, &s_evn_13d, &s_evn_12],
284 );
285 let s_odd = expr_pow4_ip(
286 [51, 38, 25, 12, 0],
287 [&s_odd_13a, &s_odd_13b, &s_odd_13c, &s_odd_13d, &s_odd_12],
288 );
289
290 let sprdd_id = (sX + sY) - (s_evn + Expression::from(2) * s_odd);
291 let sum_id = (summand_1 + summand_2) - sum;
292
293 Constraints::with_selector(
294 q_half_ch,
295 vec![
296 ("Half-Ch spreadded", sprdd_id),
297 ("Half Ch sum (2 terms)", sum_id),
298 ],
299 )
300 });
301
302 meta.create_gate("Σ₀(A)", |meta| {
303 let s13a = meta.query_advice(advice_cols[5], Rotation(-1));
305 let s12 = meta.query_advice(advice_cols[6], Rotation(-1));
306 let s05 = meta.query_advice(advice_cols[5], Rotation(0));
307 let s06 = meta.query_advice(advice_cols[6], Rotation(0));
308 let s13b = meta.query_advice(advice_cols[5], Rotation(1));
309 let s13c = meta.query_advice(advice_cols[6], Rotation(1));
310 let s02 = meta.query_advice(advice_cols[5], Rotation(2));
311 let s_evn_13a = meta.query_advice(advice_cols[1], Rotation(-1));
312 let s_evn_13b = meta.query_advice(advice_cols[1], Rotation(0));
313 let s_evn_13c = meta.query_advice(advice_cols[1], Rotation(1));
314 let s_evn_13d = meta.query_advice(advice_cols[1], Rotation(2));
315 let s_evn_12 = meta.query_advice(advice_cols[1], Rotation(3));
316 let s_odd_13a = meta.query_advice(advice_cols[3], Rotation(-1));
317 let s_odd_13b = meta.query_advice(advice_cols[3], Rotation(0));
318 let s_odd_13c = meta.query_advice(advice_cols[3], Rotation(1));
319 let s_odd_13d = meta.query_advice(advice_cols[3], Rotation(2));
320 let s_odd_12 = meta.query_advice(advice_cols[3], Rotation(3));
321
322 let s_1st_rot = expr_pow4_ip(
323 [51, 38, 36, 23, 11, 6, 0],
324 [&s13b, &s13c, &s02, &s13a, &s12, &s05, &s06],
325 );
326 let s_2nd_rot = expr_pow4_ip(
327 [58, 45, 32, 30, 17, 5, 0],
328 [&s06, &s13b, &s13c, &s02, &s13a, &s12, &s05],
329 );
330 let s_3rd_rot = expr_pow4_ip(
331 [59, 53, 40, 27, 25, 12, 0],
332 [&s05, &s06, &s13b, &s13c, &s02, &s13a, &s12],
333 );
334
335 let s_evn = expr_pow4_ip(
336 [51, 38, 25, 12, 0],
337 [&s_evn_13a, &s_evn_13b, &s_evn_13c, &s_evn_13d, &s_evn_12],
338 );
339 let s_odd = expr_pow4_ip(
340 [51, 38, 25, 12, 0],
341 [&s_odd_13a, &s_odd_13b, &s_odd_13c, &s_odd_13d, &s_odd_12],
342 );
343
344 let id = (s_1st_rot + s_2nd_rot + s_3rd_rot) - (s_evn + Expression::from(2) * s_odd);
345
346 Constraints::with_selector(q_Sigma_0, vec![("Sigma_0", id)])
347 });
348
349 meta.create_gate("Σ₁(E)", |meta| {
350 let s13a = meta.query_advice(advice_cols[5], Rotation(-1));
352 let s10a = meta.query_advice(advice_cols[6], Rotation(-1));
353 let s13b = meta.query_advice(advice_cols[5], Rotation(0));
354 let s10b = meta.query_advice(advice_cols[6], Rotation(0));
355 let s04 = meta.query_advice(advice_cols[5], Rotation(1));
356 let s13c = meta.query_advice(advice_cols[6], Rotation(1));
357 let s01 = meta.query_advice(advice_cols[5], Rotation(2));
358 let s_evn_13a = meta.query_advice(advice_cols[1], Rotation(-1));
359 let s_evn_13b = meta.query_advice(advice_cols[1], Rotation(0));
360 let s_evn_13c = meta.query_advice(advice_cols[1], Rotation(1));
361 let s_evn_13d = meta.query_advice(advice_cols[1], Rotation(2));
362 let s_evn_12 = meta.query_advice(advice_cols[1], Rotation(3));
363 let s_odd_13a = meta.query_advice(advice_cols[3], Rotation(-1));
364 let s_odd_13b = meta.query_advice(advice_cols[3], Rotation(0));
365 let s_odd_13c = meta.query_advice(advice_cols[3], Rotation(1));
366 let s_odd_13d = meta.query_advice(advice_cols[3], Rotation(2));
367 let s_odd_12 = meta.query_advice(advice_cols[3], Rotation(3));
368
369 let s_1st_rot = expr_pow4_ip(
370 [51, 50, 37, 27, 14, 4, 0],
371 [&s13c, &s01, &s13a, &s10a, &s13b, &s10b, &s04],
372 );
373 let s_2nd_rot = expr_pow4_ip(
374 [60, 47, 46, 33, 23, 10, 0],
375 [&s04, &s13c, &s01, &s13a, &s10a, &s13b, &s10b],
376 );
377 let s_3rd_rot = expr_pow4_ip(
378 [51, 41, 37, 24, 23, 10, 0],
379 [&s13b, &s10b, &s04, &s13c, &s01, &s13a, &s10a],
380 );
381
382 let s_evn = expr_pow4_ip(
383 [51, 38, 25, 12, 0],
384 [&s_evn_13a, &s_evn_13b, &s_evn_13c, &s_evn_13d, &s_evn_12],
385 );
386 let s_odd = expr_pow4_ip(
387 [51, 38, 25, 12, 0],
388 [&s_odd_13a, &s_odd_13b, &s_odd_13c, &s_odd_13d, &s_odd_12],
389 );
390
391 let id = (s_1st_rot + s_2nd_rot + s_3rd_rot) - (s_evn + Expression::from(2) * s_odd);
392
393 Constraints::with_selector(q_Sigma_1, vec![("Sigma_1", id)])
394 });
395
396 meta.create_gate("σ₀(W)", |meta| {
397 let s03a = meta.query_advice(advice_cols[5], Rotation(-1));
399 let s13a = meta.query_advice(advice_cols[6], Rotation(-1));
400 let s13b = meta.query_advice(advice_cols[5], Rotation(0));
401 let s13c = meta.query_advice(advice_cols[6], Rotation(0));
402 let s03b = meta.query_advice(advice_cols[5], Rotation(1));
403 let s11 = meta.query_advice(advice_cols[6], Rotation(1));
404 let s01a = meta.query_advice(advice_cols[5], Rotation(2));
405 let s01b = meta.query_advice(advice_cols[6], Rotation(2));
406 let s05 = meta.query_advice(advice_cols[5], Rotation(3));
407 let s01c = meta.query_advice(advice_cols[6], Rotation(3));
408 let s_evn_13a = meta.query_advice(advice_cols[1], Rotation(-1));
409 let s_evn_13b = meta.query_advice(advice_cols[1], Rotation(0));
410 let s_evn_13c = meta.query_advice(advice_cols[1], Rotation(1));
411 let s_evn_13d = meta.query_advice(advice_cols[1], Rotation(2));
412 let s_evn_12 = meta.query_advice(advice_cols[1], Rotation(3));
413 let s_odd_13a = meta.query_advice(advice_cols[3], Rotation(-1));
414 let s_odd_13b = meta.query_advice(advice_cols[3], Rotation(0));
415 let s_odd_13c = meta.query_advice(advice_cols[3], Rotation(1));
416 let s_odd_13d = meta.query_advice(advice_cols[3], Rotation(2));
417 let s_odd_12 = meta.query_advice(advice_cols[3], Rotation(3));
418
419 let s_1st_shift = expr_pow4_ip(
420 [54, 41, 28, 15, 12, 1, 0],
421 [&s03a, &s13a, &s13b, &s13c, &s03b, &s11, &s01a],
422 );
423 let s_2nd_rot = expr_pow4_ip(
424 [63, 60, 47, 34, 21, 18, 7, 6, 5, 0],
425 [
426 &s01c, &s03a, &s13a, &s13b, &s13c, &s03b, &s11, &s01a, &s01b, &s05,
427 ],
428 );
429 let s_3rd_rot = expr_pow4_ip(
430 [63, 62, 57, 56, 53, 40, 27, 14, 11, 0],
431 [
432 &s01a, &s01b, &s05, &s01c, &s03a, &s13a, &s13b, &s13c, &s03b, &s11,
433 ],
434 );
435
436 let s_evn = expr_pow4_ip(
437 [51, 38, 25, 12, 0],
438 [&s_evn_13a, &s_evn_13b, &s_evn_13c, &s_evn_13d, &s_evn_12],
439 );
440 let s_odd = expr_pow4_ip(
441 [51, 38, 25, 12, 0],
442 [&s_odd_13a, &s_odd_13b, &s_odd_13c, &s_odd_13d, &s_odd_12],
443 );
444
445 let id = (s_1st_shift + s_2nd_rot + s_3rd_rot) - (s_evn + Expression::from(2) * s_odd);
446
447 Constraints::with_selector(q_sigma_0, vec![("sigma_0", id)])
448 });
449
450 meta.create_gate("σ₁(W)", |meta| {
451 let s03a = meta.query_advice(advice_cols[5], Rotation(-1));
453 let s13a = meta.query_advice(advice_cols[6], Rotation(-1));
454 let s13b = meta.query_advice(advice_cols[5], Rotation(0));
455 let s13c = meta.query_advice(advice_cols[6], Rotation(0));
456 let s03b = meta.query_advice(advice_cols[5], Rotation(1));
457 let s11 = meta.query_advice(advice_cols[6], Rotation(1));
458 let s01a = meta.query_advice(advice_cols[5], Rotation(2));
459 let s01b = meta.query_advice(advice_cols[6], Rotation(2));
460 let s05 = meta.query_advice(advice_cols[5], Rotation(3));
461 let s01c = meta.query_advice(advice_cols[6], Rotation(3));
462 let s_evn_13a = meta.query_advice(advice_cols[1], Rotation(-1));
463 let s_evn_13b = meta.query_advice(advice_cols[1], Rotation(0));
464 let s_evn_13c = meta.query_advice(advice_cols[1], Rotation(1));
465 let s_evn_13d = meta.query_advice(advice_cols[1], Rotation(2));
466 let s_evn_12 = meta.query_advice(advice_cols[1], Rotation(3));
467 let s_odd_13a = meta.query_advice(advice_cols[3], Rotation(-1));
468 let s_odd_13b = meta.query_advice(advice_cols[3], Rotation(0));
469 let s_odd_13c = meta.query_advice(advice_cols[3], Rotation(1));
470 let s_odd_13d = meta.query_advice(advice_cols[3], Rotation(2));
471 let s_odd_12 = meta.query_advice(advice_cols[3], Rotation(3));
472
473 let s_1st_shift = expr_pow4_ip(
474 [55, 42, 29, 16, 13, 2, 1, 0],
475 [&s03a, &s13a, &s13b, &s13c, &s03b, &s11, &s01a, &s01b],
476 );
477 let s_2nd_rot = expr_pow4_ip(
478 [53, 52, 51, 46, 45, 42, 29, 16, 3, 0],
479 [
480 &s11, &s01a, &s01b, &s05, &s01c, &s03a, &s13a, &s13b, &s13c, &s03b,
481 ],
482 );
483 let s_3rd_rot = expr_pow4_ip(
484 [51, 38, 25, 22, 11, 10, 9, 4, 3, 0],
485 [
486 &s13a, &s13b, &s13c, &s03b, &s11, &s01a, &s01b, &s05, &s01c, &s03a,
487 ],
488 );
489
490 let s_evn = expr_pow4_ip(
491 [51, 38, 25, 12, 0],
492 [&s_evn_13a, &s_evn_13b, &s_evn_13c, &s_evn_13d, &s_evn_12],
493 );
494 let s_odd = expr_pow4_ip(
495 [51, 38, 25, 12, 0],
496 [&s_odd_13a, &s_odd_13b, &s_odd_13c, &s_odd_13d, &s_odd_12],
497 );
498
499 let id = (s_1st_shift + s_2nd_rot + s_3rd_rot) - (s_evn + Expression::from(2) * s_odd);
500
501 Constraints::with_selector(q_sigma_1, vec![("sigma_1", id)])
502 });
503
504 meta.create_gate("13x4-12 decomposition", |meta| {
505 let p13a = meta.query_advice(advice_cols[0], Rotation(-1));
508 let p13b = meta.query_advice(advice_cols[0], Rotation(0));
509 let p13c = meta.query_advice(advice_cols[0], Rotation(1));
510 let p13d = meta.query_advice(advice_cols[0], Rotation(2));
511 let p12 = meta.query_advice(advice_cols[0], Rotation(3));
512 let output = meta.query_advice(advice_cols[4], Rotation(-1));
513
514 let id = expr_pow2_ip([51, 38, 25, 12, 0], [&p13a, &p13b, &p13c, &p13d, &p12]) - output;
515
516 Constraints::with_selector(q_13x4_12, vec![("13x4-12 decomposition", id)])
517 });
518
519 meta.create_gate("13-12-5-6-13-13-2 decomposition", |meta| {
520 let p13a = meta.query_advice(advice_cols[0], Rotation(-1));
522 let p12 = meta.query_advice(advice_cols[2], Rotation(-1));
523 let p05 = meta.query_advice(advice_cols[0], Rotation(0));
524 let p06 = meta.query_advice(advice_cols[2], Rotation(0));
525 let p13b = meta.query_advice(advice_cols[0], Rotation(1));
526 let p13c = meta.query_advice(advice_cols[2], Rotation(1));
527 let p02 = meta.query_advice(advice_cols[0], Rotation(2));
528 let s13a = meta.query_advice(advice_cols[1], Rotation(-1));
529 let s12 = meta.query_advice(advice_cols[3], Rotation(-1));
530 let s05 = meta.query_advice(advice_cols[1], Rotation(0));
531 let s06 = meta.query_advice(advice_cols[3], Rotation(0));
532 let s13b = meta.query_advice(advice_cols[1], Rotation(1));
533 let s13c = meta.query_advice(advice_cols[3], Rotation(1));
534 let s02 = meta.query_advice(advice_cols[1], Rotation(2));
535 let plain = meta.query_advice(advice_cols[4], Rotation(-1));
536 let sprdd = meta.query_advice(advice_cols[4], Rotation(0));
537
538 let plain_id = expr_pow2_ip(
539 [51, 39, 34, 28, 15, 2, 0],
540 [&p13a, &p12, &p05, &p06, &p13b, &p13c, &p02],
541 ) - plain;
542 let sprdd_id = expr_pow4_ip(
543 [51, 39, 34, 28, 15, 2, 0],
544 [&s13a, &s12, &s05, &s06, &s13b, &s13c, &s02],
545 ) - sprdd;
546
547 Constraints::with_selector(
548 q_13_12_5_6_13_13_2,
549 vec![
550 ("13_12_5_6_13_13_2 decomposition plain", plain_id),
551 ("13_12_5_6_13_13_2 decomposition sprdd", sprdd_id),
552 ],
553 )
554 });
555
556 meta.create_gate("13-10-13-10-4-13-1 decomposition", |meta| {
557 let p13a = meta.query_advice(advice_cols[0], Rotation(-1));
559 let p10a = meta.query_advice(advice_cols[2], Rotation(-1));
560 let p13b = meta.query_advice(advice_cols[0], Rotation(0));
561 let p10b = meta.query_advice(advice_cols[2], Rotation(0));
562 let p04 = meta.query_advice(advice_cols[0], Rotation(1));
563 let p13c = meta.query_advice(advice_cols[2], Rotation(1));
564 let p01 = meta.query_advice(advice_cols[0], Rotation(2));
565 let s13a = meta.query_advice(advice_cols[1], Rotation(-1));
566 let s10a = meta.query_advice(advice_cols[3], Rotation(-1));
567 let s13b = meta.query_advice(advice_cols[1], Rotation(0));
568 let s10b = meta.query_advice(advice_cols[3], Rotation(0));
569 let s04 = meta.query_advice(advice_cols[1], Rotation(1));
570 let s13c = meta.query_advice(advice_cols[3], Rotation(1));
571 let s01 = meta.query_advice(advice_cols[1], Rotation(2));
572 let plain = meta.query_advice(advice_cols[4], Rotation(-1));
573 let sprdd = meta.query_advice(advice_cols[4], Rotation(0));
574
575 let plain_id = expr_pow2_ip(
576 [51, 41, 28, 18, 14, 1, 0],
577 [&p13a, &p10a, &p13b, &p10b, &p04, &p13c, &p01],
578 ) - plain;
579 let sprdd_id = expr_pow4_ip(
580 [51, 41, 28, 18, 14, 1, 0],
581 [&s13a, &s10a, &s13b, &s10b, &s04, &s13c, &s01],
582 ) - sprdd;
583
584 Constraints::with_selector(
585 q_13_10_13_10_4_13_1,
586 vec![
587 ("13_10_13_10_4_13_1 decomposition plain", plain_id),
588 ("13_10_13_10_4_13_1 decomposition sprdd", sprdd_id),
589 ],
590 )
591 });
592
593 meta.create_gate("3-13x3-3-11-1-1-5-1 decomposition", |meta| {
594 let w03a = meta.query_advice(advice_cols[0], Rotation(-1));
597 let w13a = meta.query_advice(advice_cols[2], Rotation(-1));
598 let w13b = meta.query_advice(advice_cols[0], Rotation(0));
599 let w13c = meta.query_advice(advice_cols[2], Rotation(0));
600 let w03b = meta.query_advice(advice_cols[0], Rotation(1));
601 let w11 = meta.query_advice(advice_cols[2], Rotation(1));
602 let w01a = meta.query_advice(advice_cols[7], Rotation(-1));
603 let w01b = meta.query_advice(advice_cols[7], Rotation(0));
604 let w05 = meta.query_advice(advice_cols[0], Rotation(2));
605 let w01c = meta.query_advice(advice_cols[7], Rotation(1));
606 let plain = meta.query_advice(advice_cols[4], Rotation(-1));
607
608 let plain_id = expr_pow2_ip(
609 [61, 48, 35, 22, 19, 8, 7, 6, 1, 0],
610 [
611 &w03a, &w13a, &w13b, &w13c, &w03b, &w11, &w01a, &w01b, &w05, &w01c,
612 ],
613 ) - plain;
614
615 let w_01a_check = w01a.clone() * (w01a - Expression::from(1));
617 let w_01b_check = w01b.clone() * (w01b - Expression::from(1));
618 let w_01c_check = w01c.clone() * (w01c - Expression::from(1));
619
620 Constraints::with_selector(
621 q_3_13x3_3_11_1_1_5_1,
622 vec![
623 ("q_3_13x3_3_11_1_1_5_1 decomposition ", plain_id),
624 ("W.1a 1-bit check", w_01a_check),
625 ("W.1b 1-bit check", w_01b_check),
626 ("W.1c 1-bit check", w_01c_check),
627 ],
628 )
629 });
630
631 meta.create_gate("add mod 2^64", |meta| {
632 let s0 = meta.query_advice(advice_cols[5], Rotation(-1));
634 let s1 = meta.query_advice(advice_cols[6], Rotation(-1));
635 let s2 = meta.query_advice(advice_cols[5], Rotation(0));
636 let s3 = meta.query_advice(advice_cols[6], Rotation(0));
637 let s4 = meta.query_advice(advice_cols[4], Rotation(1));
638 let s5 = meta.query_advice(advice_cols[5], Rotation(1));
639 let s6 = meta.query_advice(advice_cols[6], Rotation(1));
640
641 let carry = meta.query_advice(advice_cols[2], Rotation(2));
642 let result = meta.query_advice(advice_cols[4], Rotation(-1));
643
644 let summands = [s0, s1, s2, s3, s4, s5, s6];
645 let lhs = summands.into_iter().reduce(|acc, x| acc + x).unwrap();
646 let rhs = result + carry * Expression::Constant(u128_to_fe(1u128 << 64));
647
648 Constraints::with_selector(q_add_mod_2_64, vec![("add_mod_2_64", lhs - rhs)])
649 });
650
651 Sha512Config {
652 advice_cols,
653 fixed_cols,
654
655 q_lookup,
656 table,
657
658 q_maj,
659 q_half_ch,
660 q_Sigma_0,
661 q_Sigma_1,
662 q_sigma_0,
663 q_sigma_1,
664 q_13x4_12,
665
666 q_13_12_5_6_13_13_2,
667 q_13_10_13_10_4_13_1,
668 q_3_13x3_3_11_1_1_5_1,
669 q_add_mod_2_64,
670 }
671 }
672
673 fn load(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
674 let SpreadTable {
675 nbits_col,
676 plain_col,
677 sprdd_col,
678 } = self.config().table;
679
680 layouter.assign_table(
681 || "spread table",
682 |mut table| {
683 for (index, triple) in gen_spread_table::<F>().enumerate() {
684 table.assign_cell(|| "nbits", nbits_col, index, || Value::known(triple.0))?;
685 table.assign_cell(|| "plain", plain_col, index, || Value::known(triple.1))?;
686 table.assign_cell(|| "sprdd", sprdd_col, index, || Value::known(triple.2))?;
687 }
688 Ok(())
689 },
690 )
691 }
692}
693
694impl<F: CircuitField> Sha512Chip<F> {
695 pub(super) fn sha512(
697 &self,
698 layouter: &mut impl Layouter<F>,
699 input_bytes: &[AssignedByte<F>],
700 ) -> Result<[AssignedPlain<F, 64>; 8], Error> {
701 let mut state = CompressionState::<F>::fixed(layouter, &self.native_gadget, IV)?;
702
703 for block_bytes in self.pad(layouter, input_bytes)?.chunks(128) {
704 let block = self.block_from_bytes(layouter, block_bytes.try_into().unwrap())?;
705 let message_blocks = self.message_schedule(layouter, &block)?;
706 let mut compression_state = state.clone();
707 for i in 0..80 {
708 compression_state = self.compression_round(
709 layouter,
710 &compression_state,
711 ROUND_CONSTANTS[i],
712 &message_blocks[i],
713 )?;
714 }
715 state = state.add(self, layouter, &compression_state)?;
716 }
717
718 Ok(state.plain())
719 }
720
721 fn pad(
723 &self,
724 layouter: &mut impl Layouter<F>,
725 bytes: &[AssignedByte<F>],
726 ) -> Result<Vec<AssignedByte<F>>, Error> {
727 let l = 8 * bytes.len();
728 let k = 1024 - (l + 129) % 1024;
729
730 let mut padded = bytes.to_vec();
731 padded.push(self.native_gadget.assign_fixed(layouter, 128u8)?); padded.extend(vec![self.native_gadget.assign_fixed(layouter, 0u8)?; k / 8]);
733 for byte in u128::to_be_bytes(l as u128) {
734 padded.push(self.native_gadget.assign_fixed(layouter, byte)?);
735 }
736
737 Ok(padded)
738 }
739
740 pub(super) fn block_from_bytes(
744 &self,
745 layouter: &mut impl Layouter<F>,
746 bytes: &[AssignedByte<F>; 128],
747 ) -> Result<[AssignedPlain<F, 64>; 16], Error> {
748 Ok(bytes
749 .chunks(8)
750 .map(|word_bytes| {
751 self.native_gadget
752 .assigned_from_be_bytes(layouter, word_bytes)
753 .map(AssignedPlain)
754 })
755 .collect::<Result<Vec<_>, Error>>()?
756 .try_into()
757 .unwrap())
758 }
759
760 pub(super) fn message_schedule(
764 &self,
765 layouter: &mut impl Layouter<F>,
766 block: &[AssignedPlain<F, 64>; 16],
767 ) -> Result<[AssignedPlain<F, 64>; 80], Error> {
768 let message_word = self.prepare_message_word(layouter, &[block[0].clone()])?;
769 let mut message_words: [AssignedMessageWord<F>; 80] =
770 core::array::from_fn(|_| message_word.clone());
771
772 for word_idx in 1..16 {
775 message_words[word_idx] =
776 self.prepare_message_word(layouter, &[block[word_idx].clone()])?;
777 }
778 for word_idx in 16..80 {
782 let sigma0_w_i_minus_15 = &self.sigma_0(layouter, &message_words[word_idx - 15])?;
783 let sigma1_w_i_minus_2 = &self.sigma_1(layouter, &message_words[word_idx - 2])?;
784 message_words[word_idx] = self.prepare_message_word(
785 layouter,
786 &[
787 message_words[word_idx - 16].combined_plain.clone(),
788 message_words[word_idx - 7].combined_plain.clone(),
789 sigma0_w_i_minus_15.clone(),
790 sigma1_w_i_minus_2.clone(),
791 ],
792 )?;
793 }
794
795 Ok(message_words.map(|w| w.combined_plain))
796 }
797
798 pub(super) fn compression_round(
800 &self,
801 layouter: &mut impl Layouter<F>,
802 state: &CompressionState<F>,
803 round_k: u64,
804 round_w: &AssignedPlain<F, 64>,
805 ) -> Result<CompressionState<F>, Error> {
806 let round_k = AssignedPlain::<F, 64>::fixed(layouter, &self.native_gadget, round_k)?;
807
808 let Sigma_0_of_a = self.Sigma_0(layouter, &state.a)?;
809 let Maj_of_a_b_c = self.maj(
810 layouter,
811 &state.a.combined.spreaded,
812 &state.b.spreaded,
813 &state.c.spreaded,
814 )?;
815 let Sigma_1_of_e = self.Sigma_1(layouter, &state.e)?;
816 let Ch_of_e_f_g = self.ch(
817 layouter,
818 &state.e.combined.spreaded,
819 &state.f.spreaded,
820 &state.g.spreaded,
821 )?;
822
823 let next_a_summands = [
824 state.h.clone(),
825 Sigma_1_of_e.clone(),
826 Ch_of_e_f_g.clone(),
827 round_k.clone(),
828 round_w.clone(),
829 Sigma_0_of_a,
830 Maj_of_a_b_c,
831 ];
832
833 let next_e_summands = [
834 state.d.clone(),
835 state.h.clone(),
836 Sigma_1_of_e,
837 Ch_of_e_f_g,
838 round_k.clone(),
839 round_w.clone(),
840 ];
841
842 Ok(CompressionState {
843 a: self.prepare_A(layouter, &next_a_summands)?,
844 b: state.a.combined.clone(),
845 c: state.b.clone(),
846 d: state.c.plain.clone(),
847 e: self.prepare_E(layouter, &next_e_summands)?,
848 f: state.e.combined.clone(),
849 g: state.f.clone(),
850 h: state.g.plain.clone(),
851 })
852 }
853
854 fn maj(
856 &self,
857 layouter: &mut impl Layouter<F>,
858 sprdd_a: &AssignedSpreaded<F, 64>,
859 sprdd_b: &AssignedSpreaded<F, 64>,
860 sprdd_c: &AssignedSpreaded<F, 64>,
861 ) -> Result<AssignedPlain<F, 64>, Error> {
862 let adv_cols = self.config().advice_cols;
900
901 layouter.assign_region(
902 || "Maj(A, B, C)",
903 |mut region| {
904 self.config().q_maj.enable(&mut region, 1)?;
905
906 sprdd_a.0.copy_advice(|| "~A", &mut region, adv_cols[5], 0)?;
907 sprdd_b.0.copy_advice(|| "~B", &mut region, adv_cols[6], 0)?;
908 sprdd_c.0.copy_advice(|| "~C", &mut region, adv_cols[5], 1)?;
909
910 let val_of_sprdd_forms: Value<[u128; 3]> = Value::from_iter([
911 sprdd_a.0.value().copied().map(fe_to_u128),
912 sprdd_b.0.value().copied().map(fe_to_u128),
913 sprdd_c.0.value().copied().map(fe_to_u128),
914 ])
915 .map(|sprdd_forms: Vec<u128>| sprdd_forms.try_into().unwrap());
916
917 self.assign_sprdd_13x4_12(
918 &mut region,
919 val_of_sprdd_forms.map(spreaded_maj),
920 Parity::Odd,
921 0,
922 )
923 },
924 )
925 }
926
927 fn ch(
929 &self,
930 layouter: &mut impl Layouter<F>,
931 sprdd_E: &AssignedSpreaded<F, 64>,
932 sprdd_F: &AssignedSpreaded<F, 64>,
933 sprdd_G: &AssignedSpreaded<F, 64>,
934 ) -> Result<AssignedPlain<F, 64>, Error> {
935 let adv_cols = self.config().advice_cols;
988
989 let sprdd_E_val = sprdd_E.0.value().copied().map(fe_to_u128);
990 let sprdd_F_val = sprdd_F.0.value().copied().map(fe_to_u128);
991 let sprdd_G_val = sprdd_G.0.value().copied().map(fe_to_u128);
992 let sprdd_nE_val = sprdd_E_val.map(negate_spreaded);
993
994 let EpF_val = sprdd_E_val + sprdd_F_val;
995 let nEpG_val = sprdd_nE_val + sprdd_G_val;
996 let sprdd_nE_val: Value<F> = sprdd_nE_val.map(u128_to_fe);
997
998 let mask_evn_128: AssignedNative<F> =
999 (self.native_gadget).assign_fixed(layouter, u128_to_fe(MASK_EVN_128))?;
1000
1001 layouter.assign_region(
1002 || "Ch(E, F, G)",
1003 |mut region| {
1004 self.config().q_half_ch.enable(&mut region, 1)?;
1005 self.config().q_half_ch.enable(&mut region, 6)?;
1006
1007 sprdd_E.0.copy_advice(|| "~E", &mut region, adv_cols[5], 0)?;
1008 sprdd_E.0.copy_advice(|| "~E", &mut region, adv_cols[4], 6)?;
1009
1010 sprdd_F.0.copy_advice(|| "~F", &mut region, adv_cols[6], 0)?;
1011 sprdd_G.0.copy_advice(|| "~G", &mut region, adv_cols[6], 5)?;
1012
1013 let sprdd_nE = region.assign_advice(|| "~(¬E)", adv_cols[5], 5, || sprdd_nE_val)?;
1014 sprdd_nE.copy_advice(|| "~(¬E)", &mut region, adv_cols[5], 6)?;
1015
1016 mask_evn_128.copy_advice(|| "MASK_EVN_128", &mut region, adv_cols[6], 6)?;
1017
1018 let odd_EF = self.assign_sprdd_13x4_12(&mut region, EpF_val, Parity::Odd, 0)?;
1019 odd_EF.0.copy_advice(|| "Odd_EF", &mut region, adv_cols[4], 1)?;
1020
1021 let odd_nEG = self.assign_sprdd_13x4_12(&mut region, nEpG_val, Parity::Odd, 5)?;
1022 odd_nEG.0.copy_advice(|| "Odd_nEG", &mut region, adv_cols[5], 1)?;
1023
1024 let ret_val = odd_EF.0.value().copied() + odd_nEG.0.value().copied();
1025 region
1026 .assign_advice(|| "Ret", adv_cols[6], 1, || ret_val)
1027 .map(AssignedPlain::<F, 64>)
1028 },
1029 )
1030 }
1031
1032 fn Sigma_0(
1034 &self,
1035 layouter: &mut impl Layouter<F>,
1036 a: &LimbsOfA<F>,
1037 ) -> Result<AssignedPlain<F, 64>, Error> {
1038 let adv_cols = self.config().advice_cols;
1078
1079 layouter.assign_region(
1080 || "Σ₀(A)",
1081 |mut region| {
1082 self.config().q_Sigma_0.enable(&mut region, 1)?;
1083
1084 a.spreaded_limb_13a.0.copy_advice(|| "~A.13a", &mut region, adv_cols[5], 0)?;
1086 a.spreaded_limb_12.0.copy_advice(|| "~A.12", &mut region, adv_cols[6], 0)?;
1087 a.spreaded_limb_05.0.copy_advice(|| "~A.05", &mut region, adv_cols[5], 1)?;
1088 a.spreaded_limb_06.0.copy_advice(|| "~A.06", &mut region, adv_cols[6], 1)?;
1089 a.spreaded_limb_13b.0.copy_advice(|| "~A.13b", &mut region, adv_cols[5], 2)?;
1090 a.spreaded_limb_13c.0.copy_advice(|| "~A.13c", &mut region, adv_cols[6], 2)?;
1091 a.spreaded_limb_02.0.copy_advice(|| "~A.02", &mut region, adv_cols[5], 3)?;
1092
1093 let val_of_sprdd_limbs: Value<[u128; 7]> = Value::from_iter([
1098 a.spreaded_limb_13a.0.value().copied().map(fe_to_u128),
1099 a.spreaded_limb_12.0.value().copied().map(fe_to_u128),
1100 a.spreaded_limb_05.0.value().copied().map(fe_to_u128),
1101 a.spreaded_limb_06.0.value().copied().map(fe_to_u128),
1102 a.spreaded_limb_13b.0.value().copied().map(fe_to_u128),
1103 a.spreaded_limb_13c.0.value().copied().map(fe_to_u128),
1104 a.spreaded_limb_02.0.value().copied().map(fe_to_u128),
1105 ])
1106 .map(|limbs: Vec<u128>| limbs.try_into().unwrap());
1107
1108 self.assign_sprdd_13x4_12(
1109 &mut region,
1110 val_of_sprdd_limbs.map(spreaded_Sigma_0),
1111 Parity::Evn,
1112 0,
1113 )
1114 },
1115 )
1116 }
1117
1118 fn Sigma_1(
1120 &self,
1121 layouter: &mut impl Layouter<F>,
1122 e: &LimbsOfE<F>,
1123 ) -> Result<AssignedPlain<F, 64>, Error> {
1124 let adv_cols = self.config().advice_cols;
1164
1165 layouter.assign_region(
1166 || "Σ₁(E)",
1167 |mut region| {
1168 self.config().q_Sigma_1.enable(&mut region, 1)?;
1169
1170 e.spreaded_limb_13a.0.copy_advice(|| "~E.13a", &mut region, adv_cols[5], 0)?;
1172 e.spreaded_limb_10a.0.copy_advice(|| "~E.10a", &mut region, adv_cols[6], 0)?;
1173 e.spreaded_limb_13b.0.copy_advice(|| "~E.13b", &mut region, adv_cols[5], 1)?;
1174 e.spreaded_limb_10b.0.copy_advice(|| "~E.10b", &mut region, adv_cols[6], 1)?;
1175 e.spreaded_limb_04.0.copy_advice(|| "~E.04", &mut region, adv_cols[5], 2)?;
1176 e.spreaded_limb_13c.0.copy_advice(|| "~E.13c", &mut region, adv_cols[6], 2)?;
1177 e.spreaded_limb_01.0.copy_advice(|| "~E.01", &mut region, adv_cols[5], 3)?;
1178
1179 let val_of_sprdd_limbs: Value<[u128; 7]> = Value::from_iter([
1184 e.spreaded_limb_13a.0.value().copied().map(fe_to_u128),
1185 e.spreaded_limb_10a.0.value().copied().map(fe_to_u128),
1186 e.spreaded_limb_13b.0.value().copied().map(fe_to_u128),
1187 e.spreaded_limb_10b.0.value().copied().map(fe_to_u128),
1188 e.spreaded_limb_04.0.value().copied().map(fe_to_u128),
1189 e.spreaded_limb_13c.0.value().copied().map(fe_to_u128),
1190 e.spreaded_limb_01.0.value().copied().map(fe_to_u128),
1191 ])
1192 .map(|limbs: Vec<u128>| limbs.try_into().unwrap());
1193
1194 self.assign_sprdd_13x4_12(
1195 &mut region,
1196 val_of_sprdd_limbs.map(spreaded_Sigma_1),
1197 Parity::Evn,
1198 0,
1199 )
1200 },
1201 )
1202 }
1203
1204 fn sigma_0(
1206 &self,
1207 layouter: &mut impl Layouter<F>,
1208 w: &AssignedMessageWord<F>,
1209 ) -> Result<AssignedPlain<F, 64>, Error> {
1210 let adv_cols = self.config().advice_cols;
1253
1254 layouter.assign_region(
1255 || "σ₀(W)",
1256 |mut region| {
1257 self.config().q_sigma_0.enable(&mut region, 1)?;
1258
1259 w.spreaded_w_03a.0.copy_advice(|| "~W.03a", &mut region, adv_cols[5], 0)?;
1261 w.spreaded_w_13a.0.copy_advice(|| "~W.13a", &mut region, adv_cols[6], 0)?;
1262 w.spreaded_w_13b.0.copy_advice(|| "~W.13b", &mut region, adv_cols[5], 1)?;
1263 w.spreaded_w_13c.0.copy_advice(|| "~W.13c", &mut region, adv_cols[6], 1)?;
1264 w.spreaded_w_03b.0.copy_advice(|| "~W.03b", &mut region, adv_cols[5], 2)?;
1265 w.spreaded_w_11.0.copy_advice(|| "~W.11", &mut region, adv_cols[6], 2)?;
1266 w.spreaded_w_01a.0.copy_advice(|| "~W.01a", &mut region, adv_cols[5], 3)?;
1267 w.spreaded_w_01b.0.copy_advice(|| "~W.01b", &mut region, adv_cols[6], 3)?;
1268 w.spreaded_w_05.0.copy_advice(|| "~W.05", &mut region, adv_cols[5], 4)?;
1269 w.spreaded_w_01c.0.copy_advice(|| "~W.01c", &mut region, adv_cols[6], 4)?;
1270
1271 let val_of_sprdd_limbs: Value<[u128; 10]> = Value::from_iter([
1276 w.spreaded_w_03a.0.value().copied().map(fe_to_u128),
1277 w.spreaded_w_13a.0.value().copied().map(fe_to_u128),
1278 w.spreaded_w_13b.0.value().copied().map(fe_to_u128),
1279 w.spreaded_w_13c.0.value().copied().map(fe_to_u128),
1280 w.spreaded_w_03b.0.value().copied().map(fe_to_u128),
1281 w.spreaded_w_11.0.value().copied().map(fe_to_u128),
1282 w.spreaded_w_01a.0.value().copied().map(fe_to_u128),
1283 w.spreaded_w_01b.0.value().copied().map(fe_to_u128),
1284 w.spreaded_w_05.0.value().copied().map(fe_to_u128),
1285 w.spreaded_w_01c.0.value().copied().map(fe_to_u128),
1286 ])
1287 .map(|limbs: Vec<u128>| limbs.try_into().unwrap());
1288
1289 self.assign_sprdd_13x4_12(
1290 &mut region,
1291 val_of_sprdd_limbs.map(spreaded_sigma_0),
1292 Parity::Evn,
1293 0,
1294 )
1295 },
1296 )
1297 }
1298
1299 fn sigma_1(
1301 &self,
1302 layouter: &mut impl Layouter<F>,
1303 w: &AssignedMessageWord<F>,
1304 ) -> Result<AssignedPlain<F, 64>, Error> {
1305 let adv_cols = self.config().advice_cols;
1348
1349 layouter.assign_region(
1350 || "σ₁(W)",
1351 |mut region| {
1352 self.config().q_sigma_1.enable(&mut region, 1)?;
1353
1354 w.spreaded_w_03a.0.copy_advice(|| "~W.03a", &mut region, adv_cols[5], 0)?;
1356 w.spreaded_w_13a.0.copy_advice(|| "~W.13a", &mut region, adv_cols[6], 0)?;
1357 w.spreaded_w_13b.0.copy_advice(|| "~W.13b", &mut region, adv_cols[5], 1)?;
1358 w.spreaded_w_13c.0.copy_advice(|| "~W.13c", &mut region, adv_cols[6], 1)?;
1359 w.spreaded_w_03b.0.copy_advice(|| "~W.03b", &mut region, adv_cols[5], 2)?;
1360 w.spreaded_w_11.0.copy_advice(|| "~W.11", &mut region, adv_cols[6], 2)?;
1361 w.spreaded_w_01a.0.copy_advice(|| "~W.01a", &mut region, adv_cols[5], 3)?;
1362 w.spreaded_w_01b.0.copy_advice(|| "~W.01b", &mut region, adv_cols[6], 3)?;
1363 w.spreaded_w_05.0.copy_advice(|| "~W.05", &mut region, adv_cols[5], 4)?;
1364 w.spreaded_w_01c.0.copy_advice(|| "~W.01c", &mut region, adv_cols[6], 4)?;
1365
1366 let val_of_sprdd_limbs: Value<[u128; 10]> = Value::from_iter([
1371 w.spreaded_w_03a.0.value().copied().map(fe_to_u128),
1372 w.spreaded_w_13a.0.value().copied().map(fe_to_u128),
1373 w.spreaded_w_13b.0.value().copied().map(fe_to_u128),
1374 w.spreaded_w_13c.0.value().copied().map(fe_to_u128),
1375 w.spreaded_w_03b.0.value().copied().map(fe_to_u128),
1376 w.spreaded_w_11.0.value().copied().map(fe_to_u128),
1377 w.spreaded_w_01a.0.value().copied().map(fe_to_u128),
1378 w.spreaded_w_01b.0.value().copied().map(fe_to_u128),
1379 w.spreaded_w_05.0.value().copied().map(fe_to_u128),
1380 w.spreaded_w_01c.0.value().copied().map(fe_to_u128),
1381 ])
1382 .map(|limbs: Vec<u128>| limbs.try_into().unwrap());
1383
1384 self.assign_sprdd_13x4_12(
1385 &mut region,
1386 val_of_sprdd_limbs.map(spreaded_sigma_1),
1387 Parity::Evn,
1388 0,
1389 )
1390 },
1391 )
1392 }
1393
1394 fn assign_sprdd_13x4_12(
1426 &self,
1427 region: &mut Region<'_, F>,
1428 value: Value<u128>,
1429 even_or_odd: Parity,
1430 offset: usize,
1431 ) -> Result<AssignedPlain<F, 64>, Error> {
1432 self.config().q_13x4_12.enable(region, offset + 1)?;
1433
1434 let (evn_val, odd_val) = value.map(get_even_and_odd_bits).unzip();
1435
1436 let [evn0_13, evn1_13, evn2_13, evn3_13, evn4_12] =
1437 evn_val.map(|v| u64_in_be_limbs(v, [13, 13, 13, 13, 12])).transpose_array();
1438
1439 let [odd0_13, odd1_13, odd2_13, odd3_13, odd4_12] =
1440 odd_val.map(|v| u64_in_be_limbs(v, [13, 13, 13, 13, 12])).transpose_array();
1441
1442 let idx = match even_or_odd {
1443 Parity::Evn => 0,
1444 Parity::Odd => 1,
1445 };
1446
1447 self.assign_plain_and_spreaded::<13>(region, evn0_13, offset, idx)?;
1448 self.assign_plain_and_spreaded::<13>(region, evn1_13, offset + 1, idx)?;
1449 self.assign_plain_and_spreaded::<13>(region, evn2_13, offset + 2, idx)?;
1450 self.assign_plain_and_spreaded::<13>(region, evn3_13, offset + 3, idx)?;
1451 self.assign_plain_and_spreaded::<12>(region, evn4_12, offset + 4, idx)?;
1452
1453 self.assign_plain_and_spreaded::<13>(region, odd0_13, offset, 1 - idx)?;
1454 self.assign_plain_and_spreaded::<13>(region, odd1_13, offset + 1, 1 - idx)?;
1455 self.assign_plain_and_spreaded::<13>(region, odd2_13, offset + 2, 1 - idx)?;
1456 self.assign_plain_and_spreaded::<13>(region, odd3_13, offset + 3, 1 - idx)?;
1457 self.assign_plain_and_spreaded::<12>(region, odd4_12, offset + 4, 1 - idx)?;
1458
1459 let out_col = self.config().advice_cols[4];
1460 match even_or_odd {
1461 Parity::Evn => {
1462 region.assign_advice(|| "Evn", out_col, offset, || evn_val.map(u64_to_fe))
1463 }
1464 Parity::Odd => {
1465 region.assign_advice(|| "Odd", out_col, offset, || odd_val.map(u64_to_fe))
1466 }
1467 }
1468 .map(AssignedPlain)
1469 }
1470
1471 fn prepare_A(
1478 &self,
1479 layouter: &mut impl Layouter<F>,
1480 summands: &[AssignedPlain<F, 64>],
1481 ) -> Result<LimbsOfA<F>, Error> {
1482 let zero = AssignedPlain::<F, 64>::fixed(layouter, &self.native_gadget, 0)?;
1516
1517 layouter.assign_region(
1518 || "decompose A in 13-12-5-6-13-13-2 limbs",
1519 |mut region| {
1520 self.config().q_13_12_5_6_13_13_2.enable(&mut region, 1)?;
1521
1522 let a_plain = self.assign_add_mod_2_64(&mut region, summands, &zero)?;
1523 let a_sprdd_val =
1524 a_plain.0.value().copied().map(fe_to_u64).map(spread).map(u128_to_fe);
1525 let a_sprdd = region
1526 .assign_advice(|| "~A", self.config().advice_cols[4], 1, || a_sprdd_val)
1527 .map(AssignedSpreaded)?;
1528
1529 let [val_13a, val_12, val_05, val_06, val_13b, val_13c, val_02] = a_plain
1530 .0
1531 .value()
1532 .copied()
1533 .map(|a| u64_in_be_limbs(fe_to_u64(a), [13, 12, 5, 6, 13, 13, 2]))
1534 .transpose_array();
1535
1536 let limb_13a = self.assign_plain_and_spreaded(&mut region, val_13a, 0, 0)?;
1537 let limb_12 = self.assign_plain_and_spreaded(&mut region, val_12, 0, 1)?;
1538 let limb_05 = self.assign_plain_and_spreaded(&mut region, val_05, 1, 0)?;
1539 let limb_06 = self.assign_plain_and_spreaded(&mut region, val_06, 1, 1)?;
1540 let limb_13b = self.assign_plain_and_spreaded(&mut region, val_13b, 2, 0)?;
1541 let limb_13c = self.assign_plain_and_spreaded(&mut region, val_13c, 2, 1)?;
1542 let limb_02 = self.assign_plain_and_spreaded(&mut region, val_02, 3, 0)?;
1543
1544 Ok(LimbsOfA {
1545 combined: AssignedPlainSpreaded {
1546 plain: a_plain,
1547 spreaded: a_sprdd,
1548 },
1549 spreaded_limb_13a: limb_13a.spreaded,
1550 spreaded_limb_12: limb_12.spreaded,
1551 spreaded_limb_05: limb_05.spreaded,
1552 spreaded_limb_06: limb_06.spreaded,
1553 spreaded_limb_13b: limb_13b.spreaded,
1554 spreaded_limb_13c: limb_13c.spreaded,
1555 spreaded_limb_02: limb_02.spreaded,
1556 })
1557 },
1558 )
1559 }
1560
1561 fn prepare_E(
1568 &self,
1569 layouter: &mut impl Layouter<F>,
1570 summands: &[AssignedPlain<F, 64>],
1571 ) -> Result<LimbsOfE<F>, Error> {
1572 let zero = AssignedPlain::<F, 64>::fixed(layouter, &self.native_gadget, 0)?;
1606
1607 layouter.assign_region(
1608 || "decompose E in 13-10-13-10-4-13-1 limbs",
1609 |mut region| {
1610 self.config().q_13_10_13_10_4_13_1.enable(&mut region, 1)?;
1611
1612 let e_plain = self.assign_add_mod_2_64(&mut region, summands, &zero)?;
1613 let e_sprdd_val =
1614 e_plain.0.value().copied().map(fe_to_u64).map(spread).map(u128_to_fe);
1615 let e_sprdd = region
1616 .assign_advice(|| "~E", self.config().advice_cols[4], 1, || e_sprdd_val)
1617 .map(AssignedSpreaded)?;
1618
1619 let [val_13a, val_10a, val_13b, val_10b, val_04, val_13c, val_01] = e_plain
1620 .0
1621 .value()
1622 .copied()
1623 .map(|e| u64_in_be_limbs(fe_to_u64(e), [13, 10, 13, 10, 4, 13, 1]))
1624 .transpose_array();
1625
1626 let limb_13a = self.assign_plain_and_spreaded(&mut region, val_13a, 0, 0)?;
1627 let limb_10a = self.assign_plain_and_spreaded(&mut region, val_10a, 0, 1)?;
1628 let limb_13b = self.assign_plain_and_spreaded(&mut region, val_13b, 1, 0)?;
1629 let limb_10b = self.assign_plain_and_spreaded(&mut region, val_10b, 1, 1)?;
1630 let limb_04 = self.assign_plain_and_spreaded(&mut region, val_04, 2, 0)?;
1631 let limb_13c = self.assign_plain_and_spreaded(&mut region, val_13c, 2, 1)?;
1632 let limb_01 = self.assign_plain_and_spreaded(&mut region, val_01, 3, 0)?;
1633
1634 Ok(LimbsOfE {
1635 combined: AssignedPlainSpreaded {
1636 plain: e_plain,
1637 spreaded: e_sprdd,
1638 },
1639 spreaded_limb_13a: limb_13a.spreaded,
1640 spreaded_limb_10a: limb_10a.spreaded,
1641 spreaded_limb_13b: limb_13b.spreaded,
1642 spreaded_limb_10b: limb_10b.spreaded,
1643 spreaded_limb_04: limb_04.spreaded,
1644 spreaded_limb_13c: limb_13c.spreaded,
1645 spreaded_limb_01: limb_01.spreaded,
1646 })
1647 },
1648 )
1649 }
1650
1651 fn prepare_message_word(
1655 &self,
1656 layouter: &mut impl Layouter<F>,
1657 summands: &[AssignedPlain<F, 64>],
1658 ) -> Result<AssignedMessageWord<F>, Error> {
1659 let zero = AssignedPlain::<F, 64>::fixed(layouter, &self.native_gadget, 0)?;
1696
1697 layouter.assign_region(
1698 || "prepare message word",
1699 |mut region| {
1700 self.config().q_3_13x3_3_11_1_1_5_1.enable(&mut region, 1)?;
1701
1702 let w_i_plain = self.assign_add_mod_2_64(&mut region, summands, &zero)?;
1703
1704 let [val_03a, val_13a, val_13b, val_13c, val_03b, val_11, val_01a, val_01b, val_05, val_01c] =
1705 w_i_plain.0.value().copied()
1706 .map(|w| u64_in_be_limbs(fe_to_u64(w), [3, 13, 13, 13, 3, 11, 1, 1, 5, 1]))
1707 .transpose_array();
1708 let limb_03a = self.assign_plain_and_spreaded(&mut region, val_03a, 0, 0)?;
1709 let limb_13a = self.assign_plain_and_spreaded(&mut region, val_13a, 0, 1)?;
1710 let limb_13b = self.assign_plain_and_spreaded(&mut region, val_13b, 1, 0)?;
1711 let limb_13c = self.assign_plain_and_spreaded(&mut region, val_13c, 1, 1)?;
1712 let limb_03b = self.assign_plain_and_spreaded(&mut region, val_03b, 2, 0)?;
1713 let limb_11 = self.assign_plain_and_spreaded(&mut region, val_11, 2, 1)?;
1714 let limb_05 = self.assign_plain_and_spreaded(&mut region, val_05, 3, 0)?;
1715
1716 let col = self.config().advice_cols[7];
1718 let limb_01a = region.assign_advice(|| "W.01a", col, 0, || val_01a.map(u64_to_fe))?;
1719 let limb_01b = region.assign_advice(|| "W.01b", col, 1, || val_01b.map(u64_to_fe))?;
1720 let limb_01c = region.assign_advice(|| "W.01c", col, 2, || val_01c.map(u64_to_fe))?;
1721
1722 Ok(AssignedMessageWord {
1723 combined_plain: w_i_plain,
1724 spreaded_w_03a: limb_03a.spreaded,
1725 spreaded_w_13a: limb_13a.spreaded,
1726 spreaded_w_13b: limb_13b.spreaded,
1727 spreaded_w_13c: limb_13c.spreaded,
1728 spreaded_w_03b: limb_03b.spreaded,
1729 spreaded_w_11: limb_11.spreaded,
1730 spreaded_w_01a: AssignedSpreaded(limb_01a),
1731 spreaded_w_01b: AssignedSpreaded(limb_01b),
1732 spreaded_w_05: limb_05.spreaded,
1733 spreaded_w_01c: AssignedSpreaded(limb_01c),
1734 })
1735 },
1736 )
1737 }
1738
1739 fn assign_plain_and_spreaded<const L: usize>(
1755 &self,
1756 region: &mut Region<'_, F>,
1757 plain_val: Value<u64>,
1758 offset: usize,
1759 lookup_idx: usize,
1760 ) -> Result<AssignedPlainSpreaded<F, L>, Error> {
1761 self.config().q_lookup.enable(region, offset)?;
1762
1763 let nbits_col = self.config().fixed_cols[lookup_idx]; let plain_col = self.config().advice_cols[2 * lookup_idx]; let sprdd_col = self.config().advice_cols[2 * lookup_idx + 1]; let nbits_val = Value::known(F::from(L as u64));
1768 let sprdd_val = plain_val.map(spread).map(u128_to_fe);
1769 let plain_val = plain_val.map(u64_to_fe);
1770
1771 region.assign_fixed(|| "nbits", nbits_col, offset, || nbits_val)?;
1772 let plain = region.assign_advice(|| "plain", plain_col, offset, || plain_val)?;
1773 let spreaded = region.assign_advice(|| "sprdd", sprdd_col, offset, || sprdd_val)?;
1774
1775 Ok(AssignedPlainSpreaded {
1776 plain: AssignedPlain(plain),
1777 spreaded: AssignedSpreaded(spreaded),
1778 })
1779 }
1780
1781 fn assign_add_mod_2_64(
1792 &self,
1793 region: &mut Region<'_, F>,
1794 summands: &[AssignedPlain<F, 64>],
1795 zero: &AssignedPlain<F, 64>,
1796 ) -> Result<AssignedPlain<F, 64>, Error> {
1797 assert!(summands.len() <= 7);
1811
1812 self.config().q_add_mod_2_64.enable(region, 1)?;
1813 let adv_cols = self.config().advice_cols;
1814
1815 let mut summands = summands.to_vec();
1816 summands.resize(7, zero.clone());
1817
1818 let (carry_val, sum_val): (Value<u64>, Value<F>) =
1819 Value::<Vec<F>>::from_iter(summands.iter().map(|s| s.0.value().copied()))
1820 .map(|v| v.into_iter().map(fe_to_u128).sum())
1821 .map(|s: u128| s.div_rem(&(1 << 64)))
1822 .map(|(carry, r)| (carry as u64, u128_to_fe(r)))
1823 .unzip();
1824
1825 summands[0].0.copy_advice(|| "S0", region, adv_cols[5], 0)?;
1826 summands[1].0.copy_advice(|| "S1", region, adv_cols[6], 0)?;
1827 summands[2].0.copy_advice(|| "S2", region, adv_cols[5], 1)?;
1828 summands[3].0.copy_advice(|| "S3", region, adv_cols[6], 1)?;
1829 summands[4].0.copy_advice(|| "S4", region, adv_cols[4], 2)?;
1830 summands[5].0.copy_advice(|| "S5", region, adv_cols[5], 2)?;
1831 summands[6].0.copy_advice(|| "S6", region, adv_cols[6], 2)?;
1832 let _carry: AssignedPlainSpreaded<F, 3> =
1833 self.assign_plain_and_spreaded(region, carry_val, 3, 1)?;
1834 region.assign_advice(|| "sum", adv_cols[4], 0, || sum_val).map(AssignedPlain)
1835 }
1836}
1837
1838impl<F: CircuitField> CompressionState<F> {
1839 pub fn add(
1841 &self,
1842 sha512_chip: &Sha512Chip<F>,
1843 layouter: &mut impl Layouter<F>,
1844 other: &Self,
1845 ) -> Result<Self, Error> {
1846 let a = sha512_chip.prepare_A(layouter, &[self.a.plain(), other.a.plain()])?;
1847 let b = sha512_chip.prepare_A(layouter, &[self.b.plain.clone(), other.b.plain.clone()])?;
1848 let c = sha512_chip.prepare_A(layouter, &[self.c.plain.clone(), other.c.plain.clone()])?;
1849 let d = sha512_chip.prepare_A(layouter, &[self.d.clone(), other.d.clone()])?;
1850 let e = sha512_chip.prepare_E(layouter, &[self.e.plain(), other.e.plain()])?;
1853 let f = sha512_chip.prepare_E(layouter, &[self.f.plain.clone(), other.f.plain.clone()])?;
1854 let g = sha512_chip.prepare_E(layouter, &[self.g.plain.clone(), other.g.plain.clone()])?;
1855 let h = sha512_chip.prepare_E(layouter, &[self.h.clone(), other.h.clone()])?;
1856 Ok(Self {
1859 a,
1860 b: b.combined,
1861 c: c.combined,
1862 d: d.combined.plain,
1863 e,
1864 f: f.combined,
1865 g: g.combined,
1866 h: h.combined.plain,
1867 })
1868 }
1869}
1870
1871#[cfg(any(test, feature = "testing"))]
1872use midnight_proofs::plonk::Instance;
1873
1874#[cfg(any(test, feature = "testing"))]
1875use crate::{field::decomposition::chip::P2RDecompositionConfig, testing_utils::FromScratch};
1876
1877#[cfg(any(test, feature = "testing"))]
1878impl<F: CircuitField> FromScratch<F> for Sha512Chip<F> {
1879 type Config = (Sha512Config, P2RDecompositionConfig);
1880
1881 fn new_from_scratch(config: &Self::Config) -> Self {
1882 Self {
1883 config: config.0.clone(),
1884 native_gadget: NativeGadget::new_from_scratch(&config.1),
1885 }
1886 }
1887
1888 fn configure_from_scratch(
1889 meta: &mut ConstraintSystem<F>,
1890 advice_columns: &mut Vec<Column<Advice>>,
1891 fixed_columns: &mut Vec<Column<Fixed>>,
1892 instance_columns: &[Column<Instance>; 2],
1893 ) -> Self::Config {
1894 use std::cmp::max;
1895
1896 use crate::field::{
1897 decomposition::pow2range::Pow2RangeChip,
1898 native::{NB_ARITH_COLS, NB_ARITH_FIXED_COLS},
1899 };
1900
1901 let nb_advice_needed = max(NB_ARITH_COLS, NB_SHA512_ADVICE_COLS);
1902 let nb_fixed_needed = max(NB_ARITH_FIXED_COLS, NB_SHA512_FIXED_COLS);
1903
1904 while advice_columns.len() < nb_advice_needed {
1905 advice_columns.push(meta.advice_column());
1906 }
1907 while fixed_columns.len() < nb_fixed_needed {
1908 fixed_columns.push(meta.fixed_column());
1909 }
1910
1911 let native_config = NativeChip::configure(
1912 meta,
1913 &(
1914 advice_columns[..NB_ARITH_COLS].try_into().unwrap(),
1915 fixed_columns[..NB_ARITH_FIXED_COLS].try_into().unwrap(),
1916 *instance_columns,
1917 ),
1918 );
1919
1920 let pow2range_config = Pow2RangeChip::configure(meta, &advice_columns[1..=4]);
1921 let core_decomposition_config =
1922 P2RDecompositionChip::configure(meta, &(native_config, pow2range_config));
1923
1924 let sha512_config = Sha512Chip::configure(
1925 meta,
1926 &(
1927 advice_columns[..NB_SHA512_ADVICE_COLS].try_into().unwrap(),
1928 fixed_columns[..NB_SHA512_FIXED_COLS].try_into().unwrap(),
1929 ),
1930 );
1931
1932 (sha512_config, core_decomposition_config)
1933 }
1934
1935 fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
1936 self.native_gadget.load_from_scratch(layouter)?;
1937 self.load(layouter)
1938 }
1939}