winter_crypto/hash/rescue/rp64_256_jive/
mod.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2//
3// This source code is licensed under the MIT license found in the
4// LICENSE file in the root directory of this source tree.
5
6use core::ops::Range;
7
8use math::{fields::f64::BaseElement, FieldElement, StarkField};
9
10use super::{super::mds::mds_f64_8x8::mds_multiply, exp_acc, Digest, ElementHasher, Hasher};
11
12mod digest;
13pub use digest::ElementDigest;
14
15#[cfg(test)]
16mod tests;
17
18// CONSTANTS
19// ================================================================================================
20
21/// Sponge state is set to 8 field elements or 64 bytes; 4 elements are reserved for rate and
22/// the remaining 4 elements are reserved for capacity.
23const STATE_WIDTH: usize = 8;
24
25/// The rate portion of the state is located in elements 4 through 7.
26const RATE_RANGE: Range<usize> = 4..8;
27const RATE_WIDTH: usize = RATE_RANGE.end - RATE_RANGE.start;
28
29/// Jive compression mode doesn't consider rate and capacity registers.
30const INPUT1_RANGE: Range<usize> = 0..4;
31const INPUT2_RANGE: Range<usize> = 4..8;
32
33/// The capacity portion of the state is located in elements 0, 1, 2, and 3.
34const CAPACITY_RANGE: Range<usize> = 0..4;
35
36/// The output of the hash function is a digest which consists of 4 field elements or 32 bytes.
37///
38/// The digest is returned from state elements 4, 5, 6, and 7 (the first four elements of the
39/// rate portion).
40const DIGEST_RANGE: Range<usize> = 4..8;
41const DIGEST_SIZE: usize = DIGEST_RANGE.end - DIGEST_RANGE.start;
42
43/// The number of rounds is set to 7 to target 128-bit security level with 40% security margin;
44/// computed using algorithm 7 from <https://eprint.iacr.org/2020/1143.pdf>
45const NUM_ROUNDS: usize = 7;
46
47/// S-Box and Inverse S-Box powers;
48/// computed using algorithm 6 from <https://eprint.iacr.org/2020/1143.pdf>
49///
50/// The constants are defined for tests only because the exponentiations in the code are unrolled
51/// for efficiency reasons.
52#[cfg(test)]
53const ALPHA: u64 = 7;
54#[cfg(test)]
55const INV_ALPHA: u64 = 10540996611094048183;
56
57// HASHER IMPLEMENTATION
58// ================================================================================================
59
60/// Implementation of [Hasher] trait for Rescue Prime hash function with 256-bit output.
61///
62/// The hash function is implemented according to the Rescue Prime
63/// [specifications](https://eprint.iacr.org/2020/1143.pdf) with the following exception:
64/// * We set the number of rounds to 7, which implies a 40% security margin instead of the 50%
65///   margin used in the specifications (a 50% margin rounds up to 8 rounds). The primary motivation
66///   for this is that having the number of rounds be one less than a power of two simplifies AIR
67///   design for computations involving the hash function.
68/// * When hashing a sequence of elements, implement the Hirose padding rule. However, it also means
69///   that our instantiation of Rescue Prime cannot be used in a stream mode as the number of
70///   elements to be hashed must be known upfront.
71/// * We use the first 4 elements of the state (rather than the last 4 elements of the state) for
72///   capacity and the remaining 8 elements for rate. The output of the hash function comes from the
73///   first four elements of the rate portion of the state (elements 4, 5, 6, and 7). This
74///   effectively applies a fixed bit permutation before and after XLIX permutation. We assert
75///   without proof that this does not affect security of the construction.
76/// * Instead of using Vandermonde matrices as a standard way of generating an MDS matrix as
77///   described in Rescue Prime paper, we use a methodology developed by Polygon Zero to find an MDS
78///   matrix with coefficients which are small powers of two in frequency domain. This allows us to
79///   dramatically reduce MDS matrix multiplication time. Using a different MDS matrix does not
80///   affect security of the hash function as any MDS matrix satisfies Rescue Prime construction (as
81///   described in section 4.2 of the paper).
82///
83/// The parameters used to instantiate the function are:
84/// * Field: 64-bit prime field with modulus 2^64 - 2^32 + 1.
85/// * State width: 8 field elements.
86/// * Capacity size: 4 field elements.
87/// * Number of rounds: 7.
88/// * S-Box degree: 7.
89///
90/// The above parameters target 128-bit security level. The digest consists of four field elements
91/// and it can be serialized into 32 bytes (256 bits).
92///
93/// ## Hash output consistency
94/// Functions [hash_elements()](RpJive64_256::hash_elements), [merge()](RpJive64_256::merge), and
95/// [merge_with_int()](RpJive64_256::merge_with_int) are not consistent. This is because the former
96/// is instantiated with a sponge construction, while the latter use the Jive compression mode and
97/// hence do not rely on the sponge construction.
98///
99/// In addition, [hash()](RpJive64_256::hash) function is not consistent with the functions
100/// mentioned above. For example, if we take two field elements, serialize them to bytes and hash
101/// them using [hash()](RpJive64_256::hash), the result will differ from the result obtained by
102/// hashing these elements directly using [hash_elements()](RpJive64_256::hash_elements) function.
103/// The reason for this difference is that [hash()](RpJive64_256::hash) function needs to be able to
104/// handle arbitrary binary strings, which may or may not encode valid field elements - and thus,
105/// deserialization procedure used by this function is different from the procedure used to
106/// deserialize valid field elements.
107///
108/// Thus, if the underlying data consists of valid field elements, it might make more sense
109/// to deserialize them into field elements and then hash them using
110/// [hash_elements()](RpJive64_256::hash_elements) function rather then hashing the serialized bytes
111/// using [hash()](RpJive64_256::hash) function.
112pub struct RpJive64_256();
113
114impl Hasher for RpJive64_256 {
115    type Digest = ElementDigest;
116
117    const COLLISION_RESISTANCE: u32 = 128;
118
119    fn hash(bytes: &[u8]) -> Self::Digest {
120        // compute the number of elements required to represent the string; we will be processing
121        // the string in 7-byte chunks, thus the number of elements will be equal to the number
122        // of such chunks (including a potential partial chunk at the end).
123        let num_elements = if bytes.len() % 7 == 0 {
124            bytes.len() / 7
125        } else {
126            bytes.len() / 7 + 1
127        };
128
129        // initialize state to all zeros, except for the first element of the capacity part, which
130        // is set to 1 if the number of elements is not a multiple of RATE_WIDTH.
131        let mut state = [BaseElement::ZERO; STATE_WIDTH];
132        if num_elements % RATE_WIDTH != 0 {
133            state[CAPACITY_RANGE.start] = BaseElement::ONE;
134        }
135
136        // break the string into 7-byte chunks, convert each chunk into a field element, and
137        // absorb the element into the rate portion of the state. we use 7-byte chunks because
138        // every 7-byte chunk is guaranteed to map to some field element.
139        let mut i = 0;
140        let mut buf = [0_u8; 8];
141        for (index, chunk) in bytes.chunks(7).enumerate() {
142            if index < num_elements - 1 {
143                buf[..7].copy_from_slice(chunk);
144            } else {
145                // if we are dealing with the last chunk, it may be smaller than 7 bytes long, so
146                // we need to handle it slightly differently. we also append a byte with value 1
147                // to the end of the string; this pads the string in such a way that adding
148                // trailing zeros results in different hash
149                let chunk_len = chunk.len();
150                buf = [0_u8; 8];
151                buf[..chunk_len].copy_from_slice(chunk);
152                buf[chunk_len] = 1;
153            }
154
155            // convert the bytes into a field element and absorb it into the rate portion of the
156            // state; if the rate is filled up, apply the Rescue-Prime permutation and start
157            // absorbing again from zero index.
158            state[RATE_RANGE.start + i] += BaseElement::new(u64::from_le_bytes(buf));
159            i += 1;
160            if i % RATE_WIDTH == 0 {
161                Self::apply_permutation(&mut state);
162                i = 0;
163            }
164        }
165
166        // if we absorbed some elements but didn't apply a permutation to them (would happen when
167        // the number of elements is not a multiple of RATE_WIDTH), apply a final permutation after
168        // padding by appending a 1 followed by as many 0 as necessary to make the input length a
169        // multiple of the RATE_WIDTH.
170        if i > 0 {
171            state[RATE_RANGE.start + i] = BaseElement::ONE;
172            i += 1;
173            while i != RATE_WIDTH {
174                state[RATE_RANGE.start + i] = BaseElement::ZERO;
175                i += 1;
176            }
177            Self::apply_permutation(&mut state);
178        }
179
180        // return the first 4 elements of the state as hash result
181        ElementDigest::new(state[DIGEST_RANGE].try_into().unwrap())
182    }
183
184    // We do not rely on the sponge construction to build our compression function. Instead, we use
185    // the Jive compression mode designed in https://eprint.iacr.org/2022/840.pdf.
186    fn merge(values: &[Self::Digest; 2]) -> Self::Digest {
187        // initialize the state by copying the digest elements into the state
188        let initial_state: [BaseElement; STATE_WIDTH] =
189            Self::Digest::digests_as_elements(values).try_into().unwrap();
190        let mut state = initial_state;
191
192        // apply the Rescue permutation and apply the final Jive summation
193        Self::apply_permutation(&mut state);
194
195        Self::apply_jive_summation(&initial_state, &state)
196    }
197
198    fn merge_many(values: &[Self::Digest]) -> Self::Digest {
199        Self::hash_elements(ElementDigest::digests_as_elements(values))
200    }
201
202    // We do not rely on the sponge construction to build our compression function. Instead, we use
203    // the Jive compression mode designed in https://eprint.iacr.org/2022/840.pdf.
204    fn merge_with_int(seed: Self::Digest, value: u64) -> Self::Digest {
205        // initialize the state as follows:
206        // - seed is copied into the first 4 elements of the state.
207        // - if the value fits into a single field element, copy it into the fifth rate element and
208        //   set the last state element to 5 (the number of elements to be hashed).
209        // - if the value doesn't fit into a single field element, split it into two field elements,
210        //   copy them into state elements 5 and 6, and set the last state element to 6.
211        let mut state = [BaseElement::ZERO; STATE_WIDTH];
212        state[INPUT1_RANGE].copy_from_slice(seed.as_elements());
213        state[INPUT2_RANGE.start] = BaseElement::new(value);
214        if value < BaseElement::MODULUS {
215            state[INPUT2_RANGE.end - 1] = BaseElement::new(DIGEST_SIZE as u64 + 1);
216        } else {
217            state[INPUT2_RANGE.start + 1] = BaseElement::new(value / BaseElement::MODULUS);
218            state[INPUT2_RANGE.end - 1] = BaseElement::new(DIGEST_SIZE as u64 + 2);
219        }
220
221        let initial_state = state;
222        // apply the Rescue permutation and apply the final Jive summation
223        Self::apply_permutation(&mut state);
224
225        Self::apply_jive_summation(&initial_state, &state)
226    }
227}
228
229impl ElementHasher for RpJive64_256 {
230    type BaseField = BaseElement;
231
232    fn hash_elements<E: FieldElement<BaseField = Self::BaseField>>(elements: &[E]) -> Self::Digest {
233        // convert the elements into a list of base field elements
234        let elements = E::slice_as_base_elements(elements);
235
236        // initialize state to all zeros, except for the first element of the capacity part, which
237        // is set to 1 if the number of elements is not a multiple of RATE_WIDTH.
238        let mut state = [BaseElement::ZERO; STATE_WIDTH];
239        if elements.len() % RATE_WIDTH != 0 {
240            state[CAPACITY_RANGE.start] = BaseElement::ONE;
241        }
242
243        // absorb elements into the state one by one until the rate portion of the state is filled
244        // up; then apply the Rescue-Prime permutation and start absorbing again; repeat until all
245        // elements have been absorbed
246        let mut i = 0;
247        for &element in elements.iter() {
248            state[RATE_RANGE.start + i] += element;
249            i += 1;
250            if i % RATE_WIDTH == 0 {
251                Self::apply_permutation(&mut state);
252                i = 0;
253            }
254        }
255
256        // if we absorbed some elements but didn't apply a permutation to them (would happen when
257        // the number of elements is not a multiple of RATE_WIDTH), apply a final permutation after
258        // padding by appending a 1 followed by as many 0 as necessary to make the input length a
259        // multiple of the RATE_WIDTH.
260        if i > 0 {
261            state[RATE_RANGE.start + i] = BaseElement::ONE;
262            i += 1;
263            while i != RATE_WIDTH {
264                state[RATE_RANGE.start + i] = BaseElement::ZERO;
265                i += 1;
266            }
267            Self::apply_permutation(&mut state);
268        }
269
270        // return the first 4 elements of the state as hash result
271        ElementDigest::new(state[DIGEST_RANGE].try_into().unwrap())
272    }
273}
274
275// HASH FUNCTION IMPLEMENTATION
276// ================================================================================================
277
278impl RpJive64_256 {
279    // CONSTANTS
280    // --------------------------------------------------------------------------------------------
281
282    /// The number of rounds is set to 7 to target 128-bit security level with 40% security margin.
283    pub const NUM_ROUNDS: usize = NUM_ROUNDS;
284
285    /// Sponge state is set to 8 field elements or 64 bytes; 4 elements are reserved for rate and
286    /// the remaining 4 elements are reserved for capacity.
287    pub const STATE_WIDTH: usize = STATE_WIDTH;
288
289    /// The rate portion of the state is located in elements 4 through 7 (inclusive).
290    pub const RATE_RANGE: Range<usize> = RATE_RANGE;
291
292    /// The capacity portion of the state is located in elements 0, 1, 2, and 3.
293    pub const CAPACITY_RANGE: Range<usize> = CAPACITY_RANGE;
294
295    /// The output of the hash function can be read from state elements 4, 5, 6, and 7.
296    pub const DIGEST_RANGE: Range<usize> = DIGEST_RANGE;
297
298    /// MDS matrix used for computing the linear layer in a Rescue Prime round.
299    pub const MDS: [[BaseElement; STATE_WIDTH]; STATE_WIDTH] = MDS;
300
301    /// Inverse of the MDS matrix.
302    pub const INV_MDS: [[BaseElement; STATE_WIDTH]; STATE_WIDTH] = INV_MDS;
303
304    /// Round constants added to the hasher state in the first half of the Rescue Prime round.
305    pub const ARK1: [[BaseElement; STATE_WIDTH]; NUM_ROUNDS] = ARK1;
306
307    /// Round constants added to the hasher state in the second half of the Rescue Prime round.
308    pub const ARK2: [[BaseElement; STATE_WIDTH]; NUM_ROUNDS] = ARK2;
309
310    // RESCUE PERMUTATION
311    // --------------------------------------------------------------------------------------------
312
313    /// Applies Rescue-XLIX permutation to the provided state.
314    pub fn apply_permutation(state: &mut [BaseElement; STATE_WIDTH]) {
315        // implementation is based on algorithm 3 from <https://eprint.iacr.org/2020/1143.pdf>
316        // apply round function 7 times; this provides 128-bit security with 40% security margin
317        for i in 0..NUM_ROUNDS {
318            Self::apply_round(state, i);
319        }
320    }
321
322    /// Rescue-XLIX round function.
323    #[inline(always)]
324    pub fn apply_round(state: &mut [BaseElement; STATE_WIDTH], round: usize) {
325        // apply first half of Rescue round
326        Self::apply_sbox(state);
327        Self::apply_mds(state);
328        Self::add_constants(state, &ARK1[round]);
329
330        // apply second half of Rescue round
331        Self::apply_inv_sbox(state);
332        Self::apply_mds(state);
333        Self::add_constants(state, &ARK2[round]);
334    }
335
336    #[inline(always)]
337    pub fn apply_jive_summation(
338        initial_state: &[BaseElement; STATE_WIDTH],
339        final_state: &[BaseElement; STATE_WIDTH],
340    ) -> ElementDigest {
341        let mut result = [BaseElement::ZERO; DIGEST_SIZE];
342        for (i, r) in result.iter_mut().enumerate() {
343            *r = initial_state[i]
344                + initial_state[DIGEST_SIZE + i]
345                + final_state[i]
346                + final_state[DIGEST_SIZE + i];
347        }
348
349        ElementDigest::new(result)
350    }
351
352    // HELPER FUNCTIONS
353    // --------------------------------------------------------------------------------------------
354
355    #[inline(always)]
356    fn apply_mds(state: &mut [BaseElement; STATE_WIDTH]) {
357        mds_multiply(state)
358    }
359
360    #[inline(always)]
361    fn add_constants(state: &mut [BaseElement; STATE_WIDTH], ark: &[BaseElement; STATE_WIDTH]) {
362        state.iter_mut().zip(ark).for_each(|(s, &k)| *s += k);
363    }
364
365    #[inline(always)]
366    fn apply_sbox(state: &mut [BaseElement; STATE_WIDTH]) {
367        state[0] = state[0].exp7();
368        state[1] = state[1].exp7();
369        state[2] = state[2].exp7();
370        state[3] = state[3].exp7();
371        state[4] = state[4].exp7();
372        state[5] = state[5].exp7();
373        state[6] = state[6].exp7();
374        state[7] = state[7].exp7();
375    }
376
377    #[inline(always)]
378    fn apply_inv_sbox(state: &mut [BaseElement; STATE_WIDTH]) {
379        // compute base^10540996611094048183 using 72 multiplications per array element
380        // 10540996611094048183 = b1001001001001001001001001001000110110110110110110110110110110111
381
382        // compute base^10
383        let mut t1 = *state;
384        t1.iter_mut().for_each(|t| *t = t.square());
385
386        // compute base^100
387        let mut t2 = t1;
388        t2.iter_mut().for_each(|t| *t = t.square());
389
390        // compute base^100100
391        let t3 = exp_acc::<BaseElement, STATE_WIDTH, 3>(t2, t2);
392
393        // compute base^100100100100
394        let t4 = exp_acc::<BaseElement, STATE_WIDTH, 6>(t3, t3);
395
396        // compute base^100100100100100100100100
397        let t5 = exp_acc::<BaseElement, STATE_WIDTH, 12>(t4, t4);
398
399        // compute base^100100100100100100100100100100
400        let t6 = exp_acc::<BaseElement, STATE_WIDTH, 6>(t5, t3);
401
402        // compute base^1001001001001001001001001001000100100100100100100100100100100
403        let t7 = exp_acc::<BaseElement, STATE_WIDTH, 31>(t6, t6);
404
405        // compute base^1001001001001001001001001001000110110110110110110110110110110111
406        for (i, s) in state.iter_mut().enumerate() {
407            let a = (t7[i].square() * t6[i]).square().square();
408            let b = t1[i] * t2[i] * *s;
409            *s = a * b;
410        }
411    }
412}
413
414// MDS
415// ================================================================================================
416/// Rescue MDS matrix
417const MDS: [[BaseElement; STATE_WIDTH]; STATE_WIDTH] = [
418    [
419        BaseElement::new(23),
420        BaseElement::new(8),
421        BaseElement::new(13),
422        BaseElement::new(10),
423        BaseElement::new(7),
424        BaseElement::new(6),
425        BaseElement::new(21),
426        BaseElement::new(8),
427    ],
428    [
429        BaseElement::new(8),
430        BaseElement::new(23),
431        BaseElement::new(8),
432        BaseElement::new(13),
433        BaseElement::new(10),
434        BaseElement::new(7),
435        BaseElement::new(6),
436        BaseElement::new(21),
437    ],
438    [
439        BaseElement::new(21),
440        BaseElement::new(8),
441        BaseElement::new(23),
442        BaseElement::new(8),
443        BaseElement::new(13),
444        BaseElement::new(10),
445        BaseElement::new(7),
446        BaseElement::new(6),
447    ],
448    [
449        BaseElement::new(6),
450        BaseElement::new(21),
451        BaseElement::new(8),
452        BaseElement::new(23),
453        BaseElement::new(8),
454        BaseElement::new(13),
455        BaseElement::new(10),
456        BaseElement::new(7),
457    ],
458    [
459        BaseElement::new(7),
460        BaseElement::new(6),
461        BaseElement::new(21),
462        BaseElement::new(8),
463        BaseElement::new(23),
464        BaseElement::new(8),
465        BaseElement::new(13),
466        BaseElement::new(10),
467    ],
468    [
469        BaseElement::new(10),
470        BaseElement::new(7),
471        BaseElement::new(6),
472        BaseElement::new(21),
473        BaseElement::new(8),
474        BaseElement::new(23),
475        BaseElement::new(8),
476        BaseElement::new(13),
477    ],
478    [
479        BaseElement::new(13),
480        BaseElement::new(10),
481        BaseElement::new(7),
482        BaseElement::new(6),
483        BaseElement::new(21),
484        BaseElement::new(8),
485        BaseElement::new(23),
486        BaseElement::new(8),
487    ],
488    [
489        BaseElement::new(8),
490        BaseElement::new(13),
491        BaseElement::new(10),
492        BaseElement::new(7),
493        BaseElement::new(6),
494        BaseElement::new(21),
495        BaseElement::new(8),
496        BaseElement::new(23),
497    ],
498];
499
500/// Rescue Inverse MDS matrix
501const INV_MDS: [[BaseElement; STATE_WIDTH]; STATE_WIDTH] = [
502    [
503        BaseElement::new(10671399028204489528),
504        BaseElement::new(15436289366139187412),
505        BaseElement::new(4624329233769728317),
506        BaseElement::new(18200084821960740316),
507        BaseElement::new(8736112961492104393),
508        BaseElement::new(1953609990965186349),
509        BaseElement::new(12477339747250042564),
510        BaseElement::new(1495657543820456485),
511    ],
512    [
513        BaseElement::new(1495657543820456485),
514        BaseElement::new(10671399028204489528),
515        BaseElement::new(15436289366139187412),
516        BaseElement::new(4624329233769728317),
517        BaseElement::new(18200084821960740316),
518        BaseElement::new(8736112961492104393),
519        BaseElement::new(1953609990965186349),
520        BaseElement::new(12477339747250042564),
521    ],
522    [
523        BaseElement::new(12477339747250042564),
524        BaseElement::new(1495657543820456485),
525        BaseElement::new(10671399028204489528),
526        BaseElement::new(15436289366139187412),
527        BaseElement::new(4624329233769728317),
528        BaseElement::new(18200084821960740316),
529        BaseElement::new(8736112961492104393),
530        BaseElement::new(1953609990965186349),
531    ],
532    [
533        BaseElement::new(1953609990965186349),
534        BaseElement::new(12477339747250042564),
535        BaseElement::new(1495657543820456485),
536        BaseElement::new(10671399028204489528),
537        BaseElement::new(15436289366139187412),
538        BaseElement::new(4624329233769728317),
539        BaseElement::new(18200084821960740316),
540        BaseElement::new(8736112961492104393),
541    ],
542    [
543        BaseElement::new(8736112961492104393),
544        BaseElement::new(1953609990965186349),
545        BaseElement::new(12477339747250042564),
546        BaseElement::new(1495657543820456485),
547        BaseElement::new(10671399028204489528),
548        BaseElement::new(15436289366139187412),
549        BaseElement::new(4624329233769728317),
550        BaseElement::new(18200084821960740316),
551    ],
552    [
553        BaseElement::new(18200084821960740316),
554        BaseElement::new(8736112961492104393),
555        BaseElement::new(1953609990965186349),
556        BaseElement::new(12477339747250042564),
557        BaseElement::new(1495657543820456485),
558        BaseElement::new(10671399028204489528),
559        BaseElement::new(15436289366139187412),
560        BaseElement::new(4624329233769728317),
561    ],
562    [
563        BaseElement::new(4624329233769728317),
564        BaseElement::new(18200084821960740316),
565        BaseElement::new(8736112961492104393),
566        BaseElement::new(1953609990965186349),
567        BaseElement::new(12477339747250042564),
568        BaseElement::new(1495657543820456485),
569        BaseElement::new(10671399028204489528),
570        BaseElement::new(15436289366139187412),
571    ],
572    [
573        BaseElement::new(15436289366139187412),
574        BaseElement::new(4624329233769728317),
575        BaseElement::new(18200084821960740316),
576        BaseElement::new(8736112961492104393),
577        BaseElement::new(1953609990965186349),
578        BaseElement::new(12477339747250042564),
579        BaseElement::new(1495657543820456485),
580        BaseElement::new(10671399028204489528),
581    ],
582];
583
584// ROUND CONSTANTS
585// ================================================================================================
586
587/// Rescue round constants;
588/// computed using algorithm 5 from <https://eprint.iacr.org/2020/1143.pdf>
589///
590/// The constants are broken up into two arrays ARK1 and ARK2; ARK1 contains the constants for the
591/// first half of Rescue round, and ARK2 contains constants for the second half of Rescue round.
592const ARK1: [[BaseElement; STATE_WIDTH]; NUM_ROUNDS] = [
593    [
594        BaseElement::new(5250156239823432273),
595        BaseElement::new(17991370199276831394),
596        BaseElement::new(15363758995121189373),
597        BaseElement::new(7550390719632034712),
598        BaseElement::new(705744964663370588),
599        BaseElement::new(14718080047998507086),
600        BaseElement::new(15612952641514293932),
601        BaseElement::new(8827614218997241655),
602    ],
603    [
604        BaseElement::new(3820104553051581938),
605        BaseElement::new(3385456123263281593),
606        BaseElement::new(16094709323995557719),
607        BaseElement::new(16303336019291352506),
608        BaseElement::new(8678496957982514796),
609        BaseElement::new(498270172890916765),
610        BaseElement::new(17676155962043649331),
611        BaseElement::new(14993644560894569061),
612    ],
613    [
614        BaseElement::new(14258773164148374760),
615        BaseElement::new(1655972393090532756),
616        BaseElement::new(7105012644980738960),
617        BaseElement::new(11852376844296856307),
618        BaseElement::new(17816158174482938174),
619        BaseElement::new(3981864273667206359),
620        BaseElement::new(2807469273751819673),
621        BaseElement::new(14974221859211617968),
622    ],
623    [
624        BaseElement::new(15947271309323471269),
625        BaseElement::new(14698197888879866148),
626        BaseElement::new(14077077040726269118),
627        BaseElement::new(2859805440338816615),
628        BaseElement::new(4945184696648790387),
629        BaseElement::new(15183288803792940883),
630        BaseElement::new(7601775560447886378),
631        BaseElement::new(6477224812816853098),
632    ],
633    [
634        BaseElement::new(18213733347601447845),
635        BaseElement::new(10031679943792626621),
636        BaseElement::new(5971928707867502549),
637        BaseElement::new(4916840084933933812),
638        BaseElement::new(3613815642787339926),
639        BaseElement::new(16715066477165606893),
640        BaseElement::new(14603075385258290966),
641        BaseElement::new(6037771699330759024),
642    ],
643    [
644        BaseElement::new(11092469678405138663),
645        BaseElement::new(14512788091784891767),
646        BaseElement::new(12690682422447262976),
647        BaseElement::new(4807355108863118656),
648        BaseElement::new(5207405791308193025),
649        BaseElement::new(5970889292753030887),
650        BaseElement::new(17691092604759176390),
651        BaseElement::new(2731892623388788619),
652    ],
653    [
654        BaseElement::new(9320990164295747317),
655        BaseElement::new(8313044787501051613),
656        BaseElement::new(15388579942433649113),
657        BaseElement::new(16827303822369113172),
658        BaseElement::new(7362247368635881413),
659        BaseElement::new(5501558211335089067),
660        BaseElement::new(16959364163466644433),
661        BaseElement::new(15127897185888596873),
662    ],
663];
664
665const ARK2: [[BaseElement; STATE_WIDTH]; NUM_ROUNDS] = [
666    [
667        BaseElement::new(9860068499471230379),
668        BaseElement::new(10391494434594667033),
669        BaseElement::new(4986587677027284267),
670        BaseElement::new(17781977240739864050),
671        BaseElement::new(6888921375142581299),
672        BaseElement::new(8950831725295674725),
673        BaseElement::new(17048848277806802259),
674        BaseElement::new(14146306451370933851),
675    ],
676    [
677        BaseElement::new(707569561928852298),
678        BaseElement::new(6724851263229096394),
679        BaseElement::new(16052786826295129381),
680        BaseElement::new(1966016718617096590),
681        BaseElement::new(9416027981257317341),
682        BaseElement::new(650995073054283087),
683        BaseElement::new(10013853213448688130),
684        BaseElement::new(14400137552134409897),
685    ],
686    [
687        BaseElement::new(7149263702162640230),
688        BaseElement::new(7096225564191267298),
689        BaseElement::new(12197502430442379401),
690        BaseElement::new(12804378092281676880),
691        BaseElement::new(17409570408925731570),
692        BaseElement::new(2819914464281065415),
693        BaseElement::new(15831648359524824910),
694        BaseElement::new(15629743966484525526),
695    ],
696    [
697        BaseElement::new(17953398529773387863),
698        BaseElement::new(6198711330432012203),
699        BaseElement::new(9157726872360640492),
700        BaseElement::new(9493333679697066249),
701        BaseElement::new(16030612341681265024),
702        BaseElement::new(4739709630031417239),
703        BaseElement::new(18287301685877696586),
704        BaseElement::new(8798230489526342293),
705    ],
706    [
707        BaseElement::new(11624786627634502148),
708        BaseElement::new(12924370583547723043),
709        BaseElement::new(11192385058160295505),
710        BaseElement::new(14350900531623057057),
711        BaseElement::new(6649040255431543914),
712        BaseElement::new(2106567763792008889),
713        BaseElement::new(12434281915569617273),
714        BaseElement::new(8101377239551798417),
715    ],
716    [
717        BaseElement::new(13925815041351874730),
718        BaseElement::new(15981136477777934021),
719        BaseElement::new(17398194123970783302),
720        BaseElement::new(17377636820017036987),
721        BaseElement::new(5173992930377549692),
722        BaseElement::new(3688194845376511083),
723        BaseElement::new(16177005022792194790),
724        BaseElement::new(6482787365501773067),
725    ],
726    [
727        BaseElement::new(9197066592623932055),
728        BaseElement::new(1777435748159421921),
729        BaseElement::new(5079482957444239813),
730        BaseElement::new(15080163201683705054),
731        BaseElement::new(4278835591662809119),
732        BaseElement::new(6609842793229774583),
733        BaseElement::new(651644751771720476),
734        BaseElement::new(14434199410773467460),
735    ],
736];