1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // // Copyright (c) DUSK NETWORK. All rights reserved. //! This module contains an implementation of the `Hades252` //! strategy algorithm specifically designed to work outside of //! Rank 1 Constraint Systems (R1CS) or other custom Constraint //! Systems such as Add/Mul/Custom plonk gate-circuits. //! //! The inputs of the permutation function have to be explicitly //! over the BlsScalar Field of the bls12_381 curve so working over //! `Fq = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001`. use crate::{round_constants::ROUND_CONSTANTS, PARTIAL_ROUNDS, TOTAL_FULL_ROUNDS}; use dusk_bls12_381::BlsScalar; /// Strategy for zero-knowledge plonk circuits #[cfg(feature = "std")] pub mod gadget; /// Strategy for scalars pub mod scalar; #[cfg(feature = "std")] pub use gadget::GadgetStrategy; pub use scalar::ScalarStrategy; /// Defines the Hades252 strategy algorithm. pub trait Strategy<T: Clone + Copy> { /// Fetch the next round constant from an iterator fn next_c<'b, I>(constants: &mut I) -> BlsScalar where I: Iterator<Item = &'b BlsScalar>, { constants .next() .copied() .expect("Hades252 out of ARK constants") } /// Add round keys to a set of `StrategyInput`. /// /// This round key addition also known as `ARK` is used to /// reach `Confusion and Diffusion` properties for the algorithm. /// /// Basically it allows to destroy any connection between the /// inputs and the outputs of the function. fn add_round_key<'b, I>(&mut self, constants: &mut I, words: &mut [T]) where I: Iterator<Item = &'b BlsScalar>; /// Computes `input ^ 5 (mod Fp)` /// /// The modulo depends on the input you use. In our case /// the modulo is done in respect of the `bls12_381 scalar field` /// == `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001`. fn quintic_s_box(&mut self, value: &mut T); /// Multiply the values for MDS matrix during the /// full rounds application. fn mul_matrix<'b, I>(&mut self, constants: &mut I, values: &mut [T]) where I: Iterator<Item = &'b BlsScalar>; /// Applies a `Partial Round` also known as a /// `Partial S-Box layer` to a set of inputs. /// /// ### A partial round has 3 steps on every iteration: /// /// - Add round keys to each word. Also known as `ARK`. /// - Apply `quintic S-Box` **just to the last element of /// the words generated from the first step.** This is also known /// as a `Sub Words` operation. /// - Multiplies the output words from the second step by /// the `MDS_MATRIX`. /// This is known as the `Mix Layer`. fn apply_partial_round<'b, I>(&mut self, constants: &mut I, words: &mut [T]) where I: Iterator<Item = &'b BlsScalar>, { let last = words.len() - 1; // Add round keys to each word self.add_round_key(constants, words); // Then apply quintic s-box self.quintic_s_box(&mut words[last]); // Multiply this result by the MDS matrix self.mul_matrix(constants, words); } /// Applies a `Full Round` also known as a /// `Full S-Box layer` to a set of inputs. /// /// A full round has 3 steps on every iteration: /// /// - Add round keys to each word. Also known as `ARK`. /// - Apply `quintic S-Box` **to all of the words generated /// from the first step.** /// This is also known as a `Sub Words` operation. /// - Multiplies the output words from the second step by /// the `MDS_MATRIX`. /// This is known as the `Mix Layer`. fn apply_full_round<'a, I>(&mut self, constants: &mut I, words: &mut [T]) where I: Iterator<Item = &'a BlsScalar>, { // Add round keys to each word self.add_round_key(constants, words); // Then apply quintic s-box words.iter_mut().for_each(|w| self.quintic_s_box(w)); // Multiply this result by the MDS matrix self.mul_matrix(constants, words); } /// Applies a `permutation-round` of the `Hades252` strategy. /// /// It returns a vec of `WIDTH` outputs as a result which should be /// a randomly permuted version of the input. /// /// In general, the same round function is iterated enough times /// to make sure that any symmetries and structural properties that /// might exist in the round function vanish. /// /// This `permutation` is a 3-step process that: /// /// - Applies twice the half of the `FULL_ROUNDS` /// (which can be understood as linear ops). /// /// - In the middle step it applies the `PARTIAL_ROUDS` /// (which can be understood as non-linear ops). /// /// This structure allows to minimize the number of non-linear /// ops while mantaining the security. fn perm(&mut self, data: &mut [T]) { let mut constants = ROUND_CONSTANTS.iter(); // Apply R_f full rounds for _ in 0..TOTAL_FULL_ROUNDS / 2 { self.apply_full_round(&mut constants, data); } // Apply R_P partial rounds for _ in 0..PARTIAL_ROUNDS { self.apply_partial_round(&mut constants, data); } // Apply R_f full rounds for _ in 0..TOTAL_FULL_ROUNDS / 2 { self.apply_full_round(&mut constants, data); } } /// Return the total rounds count fn rounds() -> usize { TOTAL_FULL_ROUNDS + PARTIAL_ROUNDS } }