Skip to main content

provekit_skyscraper/
reference.rs

1//! Reference implementation of the Skyscraper hash function using ark-ff.
2
3use {
4    ark_bn254::Fr,
5    ark_ff::{BigInt, Field, PrimeField},
6    seq_macro::seq,
7    std::{mem::swap, sync::LazyLock},
8    zerocopy::transmute,
9};
10
11// Compile time convert round constants to Fr
12seq!(I in 0..18 {
13    const ROUND_CONSTANTS: [Fr; 18] = [
14        #(Fr::new(BigInt(crate::constants::ROUND_CONSTANTS[I])), )*
15    ];
16});
17
18/// ```python
19/// load('skyscraper.sage')
20/// Sky_BN254_1.sigma_inv
21/// ```
22static SIGMA_INV: LazyLock<Fr> = LazyLock::new(|| {
23    "9915499612839321149637521777990102151350674507940716049588462388200839649614"
24        .parse()
25        .expect("valid field element")
26});
27
28pub fn compress_many(messages: &[u8], hashes: &mut [u8]) {
29    assert_eq!(messages.len() % 64, 0);
30    assert_eq!(hashes.len() % 32, 0);
31    assert_eq!(messages.len(), hashes.len() * 2);
32    for (message, hash) in messages.chunks_exact(64).zip(hashes.chunks_exact_mut(32)) {
33        let message: [u8; 64] = message.try_into().unwrap();
34        let [l, r] = transmute!(message);
35        let h = compress(l, r);
36        let h: [u8; 32] = transmute!(h);
37        hash.copy_from_slice(h.as_slice());
38    }
39}
40
41pub fn compress(l: [u64; 4], r: [u64; 4]) -> [u64; 4] {
42    let (l, r) = (Fr::new(BigInt(l)), Fr::new(BigInt(r)));
43    let t = l;
44    let (l, _) = permute(l, r);
45    (l + t).into_bigint().0
46}
47
48/// See Figure 2.a
49pub fn permute(l: Fr, r: Fr) -> (Fr, Fr) {
50    let (l, r) = ss(0, l, r);
51    let (l, r) = ss(2, l, r);
52    let (l, r) = ss(4, l, r);
53    let (l, r) = bb(6, l, r);
54    let (l, r) = ss(8, l, r);
55    let (l, r) = bb(10, l, r);
56    let (l, r) = ss(12, l, r);
57    let (l, r) = ss(14, l, r);
58    let (l, r) = ss(16, l, r);
59    (l, r)
60}
61
62/// See Figure 2.b
63fn ss(round: usize, mut l: Fr, mut r: Fr) -> (Fr, Fr) {
64    r += l.square() * *SIGMA_INV + ROUND_CONSTANTS[round];
65    swap(&mut l, &mut r);
66    r += l.square() * *SIGMA_INV + ROUND_CONSTANTS[round + 1];
67    swap(&mut l, &mut r);
68    (l, r)
69}
70
71/// See Figure 2.c
72fn bb(round: usize, mut l: Fr, mut r: Fr) -> (Fr, Fr) {
73    r += bar(l) + ROUND_CONSTANTS[round];
74    swap(&mut l, &mut r);
75    r += bar(l) + ROUND_CONSTANTS[round + 1];
76    swap(&mut l, &mut r);
77    (l, r)
78}
79
80fn bar(x: Fr) -> Fr {
81    // Convert to little-endian bytes
82    let limbs: [u64; 4] = x.into_bigint().0;
83    let mut bytes: [u8; 32] = transmute!(limbs);
84
85    // Cyclic rotate bytes by 16
86    let (l, r) = bytes.split_at_mut(16);
87    l.swap_with_slice(r);
88
89    // Apply sbox to each byte
90    let bytes = bytes.map(sbox);
91
92    // Interpret as field element
93    Fr::from_le_bytes_mod_order(bytes.as_slice())
94}
95
96pub fn sbox(v: u8) -> u8 {
97    (v ^ ((!v).rotate_left(1) & v.rotate_left(2) & v.rotate_left(3))).rotate_left(1)
98}
99
100#[cfg(test)]
101mod tests {
102    use {super::*, ark_ff::AdditiveGroup};
103
104    #[test]
105    fn test_ss_2() {
106        // Example from sage reference
107        let l = "11818428481613126259506041491792444971306025298632020312923851211664140080269"
108            .parse()
109            .unwrap();
110        let r = "16089984100220651117533376273482359701319211672522891227502963383930673183481"
111            .parse()
112            .unwrap();
113        let el = "2897520731550929941842826131888578795995028656093850302425034320680216166225"
114            .parse()
115            .unwrap();
116        let er = "10274752619072178425540318899508997829349102488123199431506343228471746115261"
117            .parse()
118            .unwrap();
119        let (l, r) = ss(2, l, r);
120        assert_eq!(l, el);
121        assert_eq!(r, er);
122    }
123
124    #[test]
125    fn test_sbox() {
126        // Table 3.
127        assert_eq!(sbox(0xcd), 0xd3);
128        assert_eq!(sbox(0x17), 0x0e);
129        assert_eq!(sbox(0x83), 0x17);
130        assert_eq!(sbox(0x14), 0x28);
131        assert_eq!(sbox(0x2b), 0x46);
132        assert_eq!(sbox(0x1e), 0xbc);
133    }
134
135    #[test]
136    fn test_bb_6() {
137        let l = "13251711941470795978907268022756015766767985221093713388330058285942871890923"
138            .parse()
139            .unwrap();
140        let r = "1017722258958995329580328739423576514309327442471989504101393158056883989572"
141            .parse()
142            .unwrap();
143        let el = "3193610555912363022088172260048956988022957239290210718020144819371540058981"
144            .parse()
145            .unwrap();
146        let er = "17363210535454321713488811303876243393424286347736908007836172565366081010820"
147            .parse()
148            .unwrap();
149        let (l, r) = bb(6, l, r);
150        assert_eq!(l, el);
151        assert_eq!(r, er);
152    }
153
154    #[test]
155    fn test_zero() {
156        // From Sage notebook example
157        let l = Fr::ZERO;
158        let r = Fr::ZERO;
159        let el = "5793276905781313965269111743763131906666794041798623267477617572701829069290"
160            .parse()
161            .unwrap();
162        let er = "12296274483727574983376829575121280934973829438414198530604912453551798647077"
163            .parse()
164            .unwrap();
165        let (l, r) = permute(l, r);
166        assert_eq!(l, el);
167        assert_eq!(r, er);
168    }
169
170    #[test]
171    fn test_random() {
172        // From Sage notebook example
173        let l = "50417215636675310123686652273432694184389644587803328798109154235492038730484"
174            .parse()
175            .unwrap();
176        let r = "14620920779025509970947930308416120371903474543120179490887326852503500806990"
177            .parse()
178            .unwrap();
179        let el = "8412949970293910117511617126618515787729842528183672400383899220234743146062"
180            .parse()
181            .unwrap();
182        let er = "11868175801025513844525564200589229804433722826344843184417708742749423276015"
183            .parse()
184            .unwrap();
185        let (l, r) = permute(l, r);
186        assert_eq!(l, el);
187        assert_eq!(r, er);
188    }
189}