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}