p3_poseidon2/
internal.rs

1//! Inside the Poseidon2 paper, they describe that the internal layers of the hash
2//! function do not require the full properties of MDS matrices.
3//!
4//! > For the partial rounds, the MDS property is not required anymore, and
5//! > we can set up the matrix MI focusing only on providing full diffusion, breaking
6//! > arbitrarily long subspace trails, and ensuring that the polynomial representation
7//! > of the scheme is dense. (Section 5.2)
8//!
9//! This file implements a trait for linear layers that satisfy these three properties.
10
11// The requirements translate to the following 3 properties:
12// 1: All entries are non 0.
13// 2: No Subspace Trails.
14// 3: For a matrix of the form 1 + D, the diagonal D should also be non 0.
15//
16// Properties 1 and 3 are essentially immediate to check and a sufficient condition for property 2
17// is that the minimal polynomial of the matrix M and all its powers M^2, ..., M^{2WIDTH} are maximal and irreducible.
18// This is equivalent to all the characteristic polynomials being irreducible.
19//
20// These can be verified by the following sage code (Changing field/vector/length as desired):
21//
22// field = GF(2^31 - 1);
23// length = 16;
24// vector = [-2,  1,   2,   4,   8,  16,  32,  64, 128, 256, 1024, 4096, 8192, 16384, 32768, 65536];
25// const_mat = matrix.ones(field, length);
26// diag_mat  = diagonal_matrix(field, vector);
27// for i in range(1, 2 * length + 1)
28//      assert ((const_mat + diag_mat)^i).characteristic_polynomial().is_irreducible()
29
30use alloc::vec::Vec;
31
32use p3_field::{Field, FieldAlgebra};
33
34use crate::add_rc_and_sbox_generic;
35
36/// Initialize an internal layer from a set of constants.
37pub trait InternalLayerConstructor<FA>
38where
39    FA: FieldAlgebra,
40{
41    /// A constructor which internally will convert the supplied
42    /// constants into the appropriate form for the implementation.
43    fn new_from_constants(internal_constants: Vec<FA::F>) -> Self;
44}
45
46/// Given a vector v compute the matrix vector product (1 + diag(v))state with 1 denoting the constant matrix of ones.
47pub fn matmul_internal<F: Field, FA: FieldAlgebra<F = F>, const WIDTH: usize>(
48    state: &mut [FA; WIDTH],
49    mat_internal_diag_m_1: [F; WIDTH],
50) {
51    let sum: FA = state.iter().cloned().sum();
52    for i in 0..WIDTH {
53        state[i] *= FA::from_f(mat_internal_diag_m_1[i]);
54        state[i] += sum.clone();
55    }
56}
57
58/// A trait containing all data needed to implement the internal layers of Poseidon2.
59pub trait InternalLayer<FA, const WIDTH: usize, const D: u64>: Sync + Clone
60where
61    FA: FieldAlgebra,
62{
63    /// Perform the internal layers of the Poseidon2 permutation on the given state.
64    fn permute_state(&self, state: &mut [FA; WIDTH]);
65}
66
67/// A helper method which allows any field to easily implement Internal Layer.
68/// This should only be used in places where performance is not critical.
69#[inline]
70pub fn internal_permute_state<FA: FieldAlgebra, const WIDTH: usize, const D: u64>(
71    state: &mut [FA; WIDTH],
72    diffusion_mat: fn(&mut [FA; WIDTH]),
73    internal_constants: &[FA::F],
74) {
75    for elem in internal_constants.iter() {
76        add_rc_and_sbox_generic::<FA, D>(&mut state[0], *elem);
77        diffusion_mat(state);
78    }
79}
80
81/// The compiler doesn't realize that add is associative
82/// so we help it out and minimize the dependency chains by hand.
83#[inline(always)]
84fn sum_7<FA: FieldAlgebra + Copy>(state: &[FA]) -> FA {
85    assert_eq!(state.len(), 7);
86
87    let s01 = state[0] + state[1];
88    let s23 = state[2] + state[3];
89    let s45 = state[4] + state[5];
90
91    let s0123 = s01 + s23;
92    let s456 = s45 + state[6];
93    s0123 + s456
94}
95
96/// The compiler doesn't realize that add is associative
97/// so we help it out and minimize the dependency chains by hand.
98#[inline(always)]
99fn sum_8<FA: FieldAlgebra + Copy>(state: &[FA]) -> FA {
100    assert_eq!(state.len(), 8);
101
102    let s01 = state[0] + state[1];
103    let s23 = state[2] + state[3];
104    let s45 = state[4] + state[5];
105    let s67 = state[6] + state[7];
106
107    let s0123 = s01 + s23;
108    let s4567 = s45 + s67;
109    s0123 + s4567
110}
111
112/// The compiler doesn't realize that add is associative
113/// so we help it out and minimize the dependency chains by hand.
114#[inline(always)]
115pub fn sum_15<FA: FieldAlgebra + Copy>(state: &[FA]) -> FA {
116    assert_eq!(state.len(), 15);
117
118    let bot_sum = sum_8(&state[..8]);
119    let top_sum = sum_7(&state[8..]);
120
121    bot_sum + top_sum
122}
123
124/// The compiler doesn't realize that add is associative
125/// so we help it out and minimize the dependency chains by hand.
126#[inline(always)]
127pub fn sum_23<FA: FieldAlgebra + Copy>(state: &[FA]) -> FA {
128    assert_eq!(state.len(), 23);
129
130    let bot_sum = sum_8(&state[..8]);
131    let mid_sum = sum_8(&state[8..16]);
132    let top_sum = sum_7(&state[16..]);
133
134    bot_sum + mid_sum + top_sum
135}