Skip to content

Commit

Permalink
Remove griffin hash due to security concerns (#254)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nashtare authored Feb 29, 2024
1 parent 2c1d6e1 commit 069e517
Show file tree
Hide file tree
Showing 11 changed files with 13 additions and 1,082 deletions.
34 changes: 8 additions & 26 deletions crypto/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ This crate contains modules with cryptographic operations needed in STARK proof
* BLAKE3 with either 256-bit or 192-bit output. The smaller output version can be used to reduce STARK proof size, however, it also limits proof security level to at most 96 bits.
* Rescue Prime over a 64-bit field with 256-bit output and over a 62-bit field with 248-bit output. Rescue is an arithmetization-friendly hash function and can be used in the STARK protocol when recursive proof composition is desired. However, using this function is not yet supported by the Winterfell STARK prover and verifier.
* Rescue Prime over the same 64-bit field as above, with 256-bit output, but using the novel [Jive compression mode](https://eprint.iacr.org/2022/840.pdf) to obtain a smaller state and faster 2-to-1 compression.
* Griffin over the same 64-bit field as above, with 256-bit output, also using the novel [Jive compression mode](https://eprint.iacr.org/2022/840.pdf) to obtain a smaller state and faster 2-to-1 compression.

### Rescue hash function implementation
Rescue hash function is implemented according to the Rescue Prime [specifications](https://eprint.iacr.org/2020/1143.pdf) with the following exception:
Expand Down Expand Up @@ -45,36 +44,19 @@ The parameters used to instantiate the functions are:
- S-Box degree: 3.
- Target security level: 124-bits.

### Griffin hash function implementation
Griffin hash function is implemented according to the Griffin [specifications](https://eprint.iacr.org/2022/403.pdf) with the following differences:
* We set the number of rounds to 7, which implies a 15% security margin instead of the 20% margin used in the specifications (a 20% margin rounds up to 8 rounds), similarly to the motivation on Rescue above.
* When hashing a sequence of elements, we follow the [Hirose padding](https://www.researchgate.net/publication/325706626_Sequential_Hashing_with_Minimum_Padding) specification, similarly to RPJive64_256 instantiation. However, this means that our instantiation of Griffin cannot be used in a stream mode as the number of elements to be hashed must be known upfront.
* For instantiation `GriffinJive64_256`, we also make the following modifications:
- Instead of using the matrix suggested by the Griffin specification paper, we use a methodology developed by Polygon Zero to find an MDS matrix with coefficients which are small powers of two in frequency domain. This allows us to dramatically reduce MDS matrix multiplication time. We claim without proof that using a different MDS matrix does not affect security of the hash function.
- We use Jive as compression mode for 2-to-1 hashing. Similarly to `RpJive64_256` above, the output of the hash function is not the same when we hash 8 field elements as a sequence of elements using `hash_elements()` function and when we compress 8 field elements into 4 (e.g., for building a Merkle tree) using the 2-to-1 Jive compression mode.

The parameters used to instantiate the function are:
* For `GriffinJive64_256`:
- Field: 64-bit prime field with modulus 2<sup>64</sup> - 2<sup>32</sup> + 1.
- State width: 8 field elements.
- Capacity size: 4 field elements.
- Digest size: 4 field elements (can be serialized into 32 bytes).
- Number of founds: 7.
- S-Box degree: 7.
- Target security level: 128-bits.

### Hash function performance
One of the core operations performed during STARK proof generation is construction of Merkle trees. We care greatly about building these trees as quickly as possible, and thus, for the purposes of STARK protocol, 2-to-1 hash operation (e.g., computing a hash of two 32-byte values) is especially important. The table below contains rough benchmarks for computing a 2-to-1 hash for all currently implemented hash functions.

| CPU | BLAKE3_256 | SHA3_256 | RP64_256 | RPJ64_256 | RP62_248 | GriffinJ64_256 |
| --------------------------- | :--------: | :------: | :------: | :-------: | :------: | :------------: |
| Apple M1 Pro | 76 ns | 227 ns | 5.1 us | 3.8 us | 7.1 us | 2.2 us |
| AMD Ryzen 9 5950X @ 3.4 GHz | 62 ns | 310 ns | 5.2 us | 3.9 us | 6.9 us | 1.5 us |
| Core i9-9980KH @ 2.4 GHz | 66 ns | 400 ns | - | - | 6.6 us | - |
| Core i5-7300U @ 2.6 GHz | 81 ns | 540 ns | - | - | 9.5 us | - |
| Core i5-4300U @ 1.9 GHz | 106 ns | 675 ns | - | - | 13.9 us | - |
| CPU | BLAKE3_256 | SHA3_256 | RP64_256 | RPJ64_256 | RP62_248 |
| --------------------------- | :--------: | :------: | :------: | :-------: | :------: |
| Apple M1 Pro | 76 ns | 227 ns | 5.1 us | 3.8 us | 7.1 us |
| AMD Ryzen 9 5950X @ 3.4 GHz | 62 ns | 310 ns | 5.2 us | 3.9 us | 6.9 us |
| Core i9-9980KH @ 2.4 GHz | 66 ns | 400 ns | - | - | 6.6 us |
| Core i5-7300U @ 2.6 GHz | 81 ns | 540 ns | - | - | 9.5 us |
| Core i5-4300U @ 1.9 GHz | 106 ns | 675 ns | - | - | 13.9 us |

As can be seen from the table, BLAKE3 is by far the fastest hash function, while our implementations of algebraic hashes are between 30x (Griffin) and 70x (Rescue-Prime) slower than BLAKE3 and between 10x (Griffin) and 20x (Rescue-Prime) slower than SHA3.
As can be seen from the table, BLAKE3 is by far the fastest hash function, while our implementations of algebraic hashes are 70x slower than BLAKE3 and 20x slower than SHA3.

## Merkle
[Merkle](src/merkle) module contains an implementation of a Merkle tree which supports batch proof generation and verification. Batch proofs are based on the Octopus algorithm described [here](https://eprint.iacr.org/2017/933).
Expand Down
26 changes: 2 additions & 24 deletions crypto/benches/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion
use math::fields::f128;
use rand_utils::rand_value;
use winter_crypto::{
hashers::{Blake3_256, GriffinJive64_256, Rp62_248, Rp64_256, RpJive64_256, Sha3_256},
hashers::{Blake3_256, Rp62_248, Rp64_256, RpJive64_256, Sha3_256},
Hasher,
};

Expand All @@ -20,7 +20,6 @@ type Sha3Digest = <Sha3 as Hasher>::Digest;
type Rp62_248Digest = <Rp62_248 as Hasher>::Digest;
type Rp64_256Digest = <Rp64_256 as Hasher>::Digest;
type RpJive64_256Digest = <RpJive64_256 as Hasher>::Digest;
type GriffinJive64_256Digest = <GriffinJive64_256 as Hasher>::Digest;

fn blake3(c: &mut Criterion) {
let v: [Blake3Digest; 2] = [Blake3::hash(&[1u8]), Blake3::hash(&[2u8])];
Expand Down Expand Up @@ -118,26 +117,5 @@ fn rescue_jive256(c: &mut Criterion) {
});
}

fn griffin_jive256(c: &mut Criterion) {
let v: [GriffinJive64_256Digest; 2] =
[GriffinJive64_256::hash(&[1u8]), GriffinJive64_256::hash(&[2u8])];
c.bench_function("hash_griffin_jive64_256 (cached)", |bench| {
bench.iter(|| GriffinJive64_256::merge(black_box(&v)))
});

c.bench_function("hash_griffin_jive64_256 (random)", |b| {
b.iter_batched(
|| {
[
GriffinJive64_256::hash(&rand_value::<u64>().to_le_bytes()),
GriffinJive64_256::hash(&rand_value::<u64>().to_le_bytes()),
]
},
|state| GriffinJive64_256::merge(&state),
BatchSize::SmallInput,
)
});
}

criterion_group!(hash_group, blake3, sha3, rescue248, rescue256, rescue_jive256, griffin_jive256,);
criterion_group!(hash_group, blake3, sha3, rescue248, rescue256, rescue_jive256);
criterion_main!(hash_group);
111 changes: 0 additions & 111 deletions crypto/src/hash/griffin/griffin64_256_jive/digest.rs

This file was deleted.

Loading

0 comments on commit 069e517

Please sign in to comment.