Skip to content

Commit 4fb152b

Browse files
authored
Merge pull request #5 from QEDProtocol/feat/add-keccak256
feat: add keccak256
2 parents 0d18f33 + 8ed5898 commit 4fb152b

File tree

5 files changed

+390
-1
lines changed

5 files changed

+390
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/target
22
.vscode
3+
.idea

hash/src/keccak/constants.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#[allow(clippy::unreadable_literal)]
2+
pub const ROUND_CONSTANTS: [u64; 24] = [
3+
1u64,
4+
0x8082u64,
5+
0x800000000000808au64,
6+
0x8000000080008000u64,
7+
0x808bu64,
8+
0x80000001u64,
9+
0x8000000080008081u64,
10+
0x8000000000008009u64,
11+
0x8au64,
12+
0x88u64,
13+
0x80008009u64,
14+
0x8000000au64,
15+
0x8000808bu64,
16+
0x800000000000008bu64,
17+
0x8000000000008089u64,
18+
0x8000000000008003u64,
19+
0x8000000000008002u64,
20+
0x8000000000000080u64,
21+
0x800au64,
22+
0x800000008000000au64,
23+
0x8000000080008081u64,
24+
0x8000000000008080u64,
25+
0x80000001u64,
26+
0x8000000080008008u64,
27+
];
28+
29+
pub const ROTR: [usize; 25] = [
30+
0, 1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43, 25, 39, 41, 45, 15, 21, 8, 18, 2, 61, 56, 14,
31+
];

hash/src/keccak/mod.rs

Lines changed: 262 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,210 @@
1+
mod constants;
2+
mod periodic;
3+
mod utils;
4+
use ark_bls12_381::{Bls12_381, Fr};
5+
use constants::{ROUND_CONSTANTS, ROTR};
6+
use ark_ff::UniformRand;
7+
use ark_r1cs_std::boolean::Boolean;
8+
use ark_r1cs_std::eq::EqGadget;
9+
use ark_r1cs_std::{R1CSVar, ToBitsGadget};
10+
use ark_r1cs_std::uint64::UInt64;
11+
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
12+
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
13+
use ark_std::rand::{thread_rng, Rng};
14+
use ark_std::ops::{BitAnd, BitXor, Not};
15+
116
use ark_ff::PrimeField;
17+
use ark_r1cs_std::alloc::AllocVar;
218
use ark_r1cs_std::fields::fp::FpVar;
19+
use ark_r1cs_std::uint8::UInt8;
320
use swiftness_field::Fp;
421
use swiftness_field::SimpleField;
522
use sha3::Keccak256;
623
use sha3::Digest;
24+
use crate::keccak::utils::{bitand, not, rotate_left};
25+
26+
fn xor_2<F: PrimeField>(a: &UInt64<F>, b: &UInt64<F>) -> Result<UInt64<F>, SynthesisError> {
27+
a.xor(b)
28+
}
29+
30+
fn xor_5<F: PrimeField>(
31+
a: &UInt64<F>,
32+
b: &UInt64<F>,
33+
c: &UInt64<F>,
34+
d: &UInt64<F>,
35+
e: &UInt64<F>,
36+
) -> Result<UInt64<F>, SynthesisError> {
37+
// a ^ b ^ c ^ d ^ e
38+
//note: in aff-std 0.5, use .bitxor
39+
let ab = a.xor(b)?;
40+
let abc = ab.xor(c)?;
41+
let abcd = abc.xor(d)?;
42+
abcd.xor(e)
43+
}
44+
45+
fn xor_not_and<F: PrimeField>(
46+
a: &UInt64<F>,
47+
b: &UInt64<F>,
48+
c: &UInt64<F>,
49+
) -> Result<UInt64<F>, SynthesisError> {
50+
// let nb = b.not();
51+
let nb = not(b);
52+
53+
// let nbc = nb.bitand(c);
54+
let nbc = bitand(&nb, c);
55+
a.xor(&nbc)
56+
}
57+
58+
fn round_1600<F: PrimeField>(
59+
// cs: &ConstraintSystemRef<F>,
60+
a: Vec<UInt64<F>>,
61+
rc: u64,
62+
) -> Result<Vec<UInt64<F>>, SynthesisError> {
63+
64+
assert_eq!(a.len(), 25);
65+
66+
// # θ step
67+
// C[x] = A[x,0] xor A[x,1] xor A[x,2] xor A[x,3] xor A[x,4], for x in 0…4
68+
let mut c = Vec::new();
69+
for x in 0..5 {
70+
c.push(xor_5(
71+
&a[x + 0usize],
72+
&a[x + 5usize],
73+
&a[x + 10usize],
74+
&a[x + 15usize],
75+
&a[x + 20usize],
76+
)?);
77+
}
78+
79+
// D[x] = C[x-1] xor rot(C[x+1],1), for x in 0…4
80+
let mut d = Vec::new();
81+
for x in 0..5 {
82+
d.push(xor_2(
83+
&c[(x + 4usize) % 5usize],
84+
// &c[(x + 1usize) % 5usize].rotate_left(1),
85+
&rotate_left(&c[(x + 1usize) % 5usize], 1),
86+
87+
)?);
88+
}
89+
90+
91+
// A[x,y] = A[x,y] xor D[x], for (x,y) in (0…4,0…4)
92+
let mut a_new1 = Vec::new();
93+
for y in 0..5 {
94+
for x in 0..5 {
95+
a_new1.push(xor_2(&a[x + (y * 5usize)], &d[x])?);
96+
}
97+
}
98+
99+
// # ρ and π steps
100+
// B[y,2*x+3*y] = rot(A[x,y], r[x,y]), for (x,y) in (0…4,0…4)
101+
let mut b = a_new1.clone();
102+
for y in 0..5 {
103+
for x in 0..5 {
104+
b[y + ((((2 * x) + (3 * y)) % 5) * 5usize)] =
105+
// a_new1[x + (y * 5usize)].rotate_left(ROTR[x + (y * 5usize)]);
106+
rotate_left(&a_new1[x + (y * 5usize)], ROTR[x + (y * 5usize)]);
107+
}
108+
}
109+
110+
let mut a_new2 = Vec::new();
111+
112+
// # χ step
113+
// A[x,y] = B[x,y] xor ((not B[x+1,y]) and B[x+2,y]), for (x,y) in (0…4,0…4)
114+
for y in 0..5 {
115+
for x in 0..5 {
116+
a_new2.push(xor_not_and(
117+
&b[x + (y * 5usize)],
118+
&b[((x + 1usize) % 5usize) + (y * 5usize)],
119+
&b[((x + 2usize) % 5usize) + (y * 5usize)],
120+
)?);
121+
}
122+
}
123+
124+
// // # ι step
125+
// // A[0,0] = A[0,0] xor RC
126+
let rc = UInt64::constant(rc);
127+
a_new2[0] = a_new2[0].clone().xor(&rc)?;
128+
129+
Ok(a_new2)
130+
}
131+
132+
fn keccak_f_1600<F: PrimeField>(
133+
// cs: &ConstraintSystemRef<F>,
134+
input: Vec<Boolean<F>>,
135+
) -> Result<Vec<Boolean<F>>, SynthesisError> {
136+
137+
assert_eq!(input.len(), 1600);
138+
139+
let mut a = input
140+
.chunks(64)
141+
.map(|e| UInt64::from_bits_le(e))
142+
.collect::<Vec<_>>();
143+
144+
for i in 0..24 {
145+
// a = round_1600(cs, a, ROUND_CONSTANTS[i])?;
146+
a = round_1600(a, ROUND_CONSTANTS[i])?;
147+
}
148+
149+
let a_new = a.into_iter().flat_map(|e| e.to_bits_le()).collect();
150+
151+
Ok(a_new)
152+
}
153+
154+
pub fn keccak256<F: PrimeField>(
155+
// cs: &ConstraintSystemRef<F>,
156+
input: &[Boolean<F>],
157+
) -> Result<Vec<Boolean<F>>, SynthesisError> {
158+
159+
let block_size = 136 * 8; //136 bytes * 8 = 1088 bit
160+
let input_len = input.len();
161+
let num_blocks = input_len / block_size + 1;
162+
let padded_len = num_blocks * block_size;
163+
let mut padded = vec![Boolean::constant(false); padded_len];
164+
for i in 0..input.len() {
165+
padded[i] = input[i].clone();
166+
}
167+
168+
169+
// # Padding
170+
// d = 2^|Mbits| + sum for i=0..|Mbits|-1 of 2^i*Mbits[i]
171+
// P = Mbytes || d || 0x00 || … || 0x00
172+
// P = P xor (0x00 || … || 0x00 || 0x80)
173+
//0x0100 ... 0080
174+
padded[input_len] = Boolean::constant(true);
175+
padded[padded_len - 1] = Boolean::constant(true);
176+
177+
// # Initialization
178+
// S[x,y] = 0, for (x,y) in (0…4,0…4)
179+
180+
// # Absorbing phase
181+
// for each block Pi in P
182+
// S[x,y] = S[x,y] xor Pi[x+5*y], for (x,y) such that x+5*y < r/w
183+
// S = Keccak-f[r+c](S)
184+
let mut m = vec![Boolean::constant(false); 1600];
185+
186+
for i in 0..num_blocks {
187+
for j in 0..block_size {
188+
m[j] = m[j].clone().xor(&padded[i * block_size + j])?;
189+
}
190+
//m = keccak_f_1600(cs, m)?;
191+
m = keccak_f_1600(m)?;
192+
}
193+
194+
// # Squeezing phase
195+
// Z = empty string
196+
let mut z = Vec::new();
197+
198+
// while output is requested
199+
// Z = Z || S[x,y], for (x,y) such that x+5*y < r/w
200+
// S = Keccak-f[r+c](S)
201+
for i in 0..256 {
202+
z.push(m[i].clone());
203+
}
204+
205+
return Ok(z);
206+
}
207+
7208

8209
pub trait KeccakHash: SimpleField {
9210
fn hash(data: &[<Self as SimpleField>::ByteType]) -> Vec<<Self as SimpleField>::ByteType>;
@@ -19,6 +220,66 @@ impl KeccakHash for Fp {
19220

20221
impl<F: PrimeField + SimpleField> KeccakHash for FpVar<F> {
21222
fn hash(data: &[<Self as SimpleField>::ByteType]) -> Vec<<Self as SimpleField>::ByteType> {
22-
unimplemented!()
223+
// convert input to boolean
224+
let input= data.iter()
225+
.flat_map(|byte| byte.to_bits_le().unwrap())
226+
.collect::<Vec<_>>();
227+
228+
let res = match keccak256(&input){
229+
Ok(res) => res,
230+
Err(e) => {
231+
panic!("keccak256 hash err:{}", e);
232+
}
233+
};
234+
235+
//convert output to bytes
236+
let output = res
237+
.chunks(8)
238+
.map(UInt8::from_bits_le)
239+
.collect::<Vec<_>>();
240+
output
23241
}
24242
}
243+
244+
245+
#[cfg(test)]
246+
mod tests {
247+
use super::*;
248+
use swiftness_field::StarkArkConvert;
249+
use KeccakHash;
250+
use ark_ff::Field;
251+
use ark_ff::MontFp as Fp;
252+
use swiftness_field::Fp;
253+
fn keccak_hash(data: &[u8]) -> Vec<u8> {
254+
let mut hasher = Keccak256::new();
255+
hasher.update(data);
256+
hasher.finalize().to_vec()
257+
}
258+
259+
#[test]
260+
fn test_keccak_hash_fp() {
261+
let input_len = 1024;
262+
let mut rng = thread_rng();
263+
let input: Vec<u8> = (0..input_len).map(|_| rng.gen()).collect();
264+
let real_bytes: Vec<u8> = <Fp as KeccakHash>::hash(&input);
265+
let expected = keccak_hash(&input);
266+
assert_eq!(real_bytes, expected);
267+
268+
}
269+
#[test]
270+
fn test_keccak_hash_fp_var() {
271+
let input_len = 1024;
272+
let mut rng = thread_rng();
273+
let input: Vec<u8> = (0..input_len).map(|_| rng.gen()).collect();
274+
//let input_fp = input.iter().map(|x| Fp::from(*x)).collect::<Vec<_>>();
275+
let cs = ConstraintSystem::<Fp>::new_ref();
276+
let input_fp = UInt8::new_input_vec(ark_relations::ns!(cs, "input"), &input).unwrap();
277+
let real_bytes: Vec<UInt8<Fp>> = <FpVar<Fp> as KeccakHash>::hash(&input_fp);
278+
let expected = keccak_hash(&input);
279+
assert_eq!(real_bytes.len(), expected.len());
280+
for i in 0..real_bytes.len() {
281+
assert_eq!(real_bytes[i].value().unwrap(), expected[i]);
282+
}
283+
}
284+
285+
}

hash/src/keccak/periodic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//todo: add some example

0 commit comments

Comments
 (0)