arnaucube
arnaucube/plonky2-crypto-gadgetsPublic Self-hosted repository
plonky2-crypto-gadgets/src/native.rs
src/native.rsRaw
  1//! For LICENSE check out https://github.com/0xPARC/plonky2-aes/blob/main/LICENSE
  2//!
  3//! Rust native implementation of
  4//! [AES](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197-upd1.pdf) having
  5//! in mind the approach that will be done in-circuit; this means that it is not
  6//! written in the most rust idiomatic way, but simulating the behavior that we
  7//! will do later inside the circuit.
  8
  9use crate::constants::{RCON, SBOX};
 10
 11type State = [[u8; 4]; 4];
 12
 13fn flatten_state(s: State) -> [u8; 16] {
 14    let mut r = [0u8; 16];
 15    for i in 0..4 {
 16        for j in 0..4 {
 17            r[i * 4 + j] = s[i][j];
 18        }
 19    }
 20    r
 21}
 22
 23/// encrypts an AES block (16 bytes). NR determines the number of rounds,
 24/// where AES-128: NR=10, AES-196: NR=12, AES-256: NR=14.
 25fn encrypt_block<const NR: usize>(input: &[u8; 16], w: &[[u8; 4]; 4 * (NR + 1)]) -> State {
 26    assert_eq!(4 * (NR + 1), w.len());
 27
 28    let mut s: State = [[0; 4]; 4];
 29    for i in 0..4 {
 30        for j in 0..4 {
 31            s[i][j] = input[i + 4 * j];
 32        }
 33    }
 34
 35    s = add_round_key(s, &w[0..4]);
 36
 37    for round in 1..NR {
 38        s = sub_bytes(s);
 39        s = shift_rows(s);
 40        s = mix_columns(s);
 41        s = add_round_key(s, &w[4 * round..4 * round + 4]);
 42    }
 43
 44    s = sub_bytes(s);
 45    s = shift_rows(s);
 46    s = add_round_key(s, &w[4 * NR..4 * NR + 4]);
 47
 48    s
 49}
 50
 51fn sub_bytes(s: State) -> State {
 52    let mut r = [[0u8; 4]; 4];
 53    for i in 0..4 {
 54        for j in 0..4 {
 55            r[i][j] = SBOX[s[i][j] as usize];
 56        }
 57    }
 58    r
 59}
 60
 61fn shift_rows(s: State) -> State {
 62    let mut r = [[0u8; 4]; 4];
 63    for i in 0..4 {
 64        for j in 0..4 {
 65            r[i][j] = s[i][(i + j) % 4];
 66        }
 67    }
 68    r
 69}
 70
 71fn mix_columns(s: State) -> State {
 72    let mut r = [[0u8; 4]; 4];
 73    for c in 0..4 {
 74        r[0][c] = gf_2_8_mul(0x02, s[0][c]) ^ gf_2_8_mul(0x03, s[1][c]) ^ s[2][c] ^ s[3][c];
 75        r[1][c] = s[0][c] ^ gf_2_8_mul(0x02, s[1][c]) ^ gf_2_8_mul(0x03, s[2][c]) ^ s[3][c];
 76        r[2][c] = s[0][c] ^ s[1][c] ^ gf_2_8_mul(0x02, s[2][c]) ^ gf_2_8_mul(0x03, s[3][c]);
 77        r[3][c] = gf_2_8_mul(0x03, s[0][c]) ^ s[1][c] ^ s[2][c] ^ gf_2_8_mul(0x02, s[3][c]);
 78    }
 79    r
 80}
 81
 82/// multiplication in GF(2^8), section 4.2
 83fn gf_2_8_mul(a_raw: u8, b_raw: u8) -> u8 {
 84    let mut r = 0u8;
 85    let mut a = a_raw;
 86    let mut b = b_raw;
 87    for _ in 0..8 {
 88        if b & 1 == 1 {
 89            r ^= a
 90        }
 91        let high_bit = a & 0x80;
 92        a <<= 1;
 93        if high_bit == 0x80 {
 94            a ^= 0x1b; // ^0x1b = reduce mod m(x) = x^8 + x^4 + x^3 + x + 1
 95        }
 96        b >>= 1;
 97    }
 98    r
 99}
100
101fn add_round_key(s: State, key: &[[u8; 4]]) -> State {
102    assert_eq!(key.len(), 4); // ensures that key: [[u8;4]; 4]
103    let mut r: [[u8; 4]; 4] = [[0; 4]; 4];
104    for i in 0..4 {
105        for j in 0..4 {
106            r[i][j] = s[i][j] ^ key[j][i];
107        }
108    }
109    r
110}
111
112fn key_expansion<const NK: usize, const NB: usize, const NR: usize>(
113    key: &[u8; NK * NB],
114) -> [[u8; 4]; 4 * (NR + 1)]
115where
116    [(); 4 * (NR + 1)]:,
117    [(); 4 * 4 * (NR + 1)]:,
118{
119    let mut w = [[0u8; 4]; 4 * (NR + 1)]; // expanded key
120
121    for i in 0..NK {
122        w[i].clone_from_slice(&key[i * 4..i * 4 + 4]);
123    }
124
125    for i in NK..4 * (NR + 1) {
126        let mut temp = w[i - 1];
127        if i % NK == 0 {
128            let rcon: [u8; 4] = [RCON[i / NK], 0, 0, 0];
129            temp = xor_words(sub_word(rot_word(temp)), rcon);
130        } else if NK > 6 && i % NK == 4 {
131            temp = sub_word(temp);
132        }
133        w[i] = xor_words(w[i - NK], temp);
134    }
135    w
136}
137
138fn rot_word(w: [u8; 4]) -> [u8; 4] {
139    let mut r = [0u8; 4];
140    (r[0], r[1], r[2], r[3]) = (w[1], w[2], w[3], w[0]);
141    r
142}
143fn sub_word(w: [u8; 4]) -> [u8; 4] {
144    let mut r = [0u8; 4];
145    for i in 0..4 {
146        r[i] = SBOX[w[i] as usize];
147    }
148    r
149}
150fn xor_words(w1: [u8; 4], w2: [u8; 4]) -> [u8; 4] {
151    let mut r = [0u8; 4];
152    for i in 0..4 {
153        r[i] = w1[i] ^ w2[i];
154    }
155    r
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    /// test against test vectors from Appendix A
163    #[test]
164    fn test_key_expansion() {
165        // AES-128
166        let key = [
167            0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
168            0x4f, 0x3c,
169        ];
170        let expected: [u32; 4] = [0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c];
171        let w = key_expansion::</*Nk,Nb,Nr*/ 4, 4, 10>(&key);
172        for i in 0..4 {
173            assert_eq!(expected[i], u32::from_be_bytes(w[i]));
174        }
175
176        // AES-196
177        let key = [
178            0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90,
179            0x79, 0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b,
180        ];
181        let expected: [u32; 6] = [
182            0x8e73b0f7, 0xda0e6452, 0xc810f32b, 0x809079e5, 0x62f8ead2, 0x522c6b7b,
183        ];
184        let w = key_expansion::</*Nk,Nb,Nr*/ 6, 4, 12>(&key);
185        for i in 0..6 {
186            assert_eq!(expected[i], u32::from_be_bytes(w[i]));
187        }
188
189        // AES-256
190        let key = [
191            0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d,
192            0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3,
193            0x09, 0x14, 0xdf, 0xf4,
194        ];
195        let expected: [u32; 8] = [
196            0x603deb10, 0x15ca71be, 0x2b73aef0, 0x857d7781, 0x1f352c07, 0x3b6108d7, 0x2d9810a3,
197            0x0914dff4,
198        ];
199        let w = key_expansion::</*Nk,Nb,Nr*/ 8, 4, 14>(&key);
200        for i in 0..8 {
201            assert_eq!(expected[i], u32::from_be_bytes(w[i]));
202        }
203    }
204
205    /// test against paper test vector (appendix B)
206    #[test]
207    fn test_encrypt_block_test_vector() {
208        let input: [u8; 16] = [
209            0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37,
210            0x07, 0x34,
211        ];
212        let key = [
213            0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
214            0x4f, 0x3c,
215        ];
216        let expanded_key = key_expansion::<4, 4, 10>(&key);
217
218        let c = encrypt_block::<10>(&input, &expanded_key);
219
220        let expected: [u8; 16] = [
221            0x39, 0x02, 0xdc, 0x19, 0x25, 0xdc, 0x11, 0x6a, 0x84, 0x09, 0x85, 0x0b, 0x1d, 0xfb,
222            0x97, 0x32,
223        ];
224
225        assert_eq!(flatten_state(c), expected);
226    }
227
228    /// test with loop of random-values against a rust well-tested library
229    #[test]
230    fn test_encrypt_block_external_lib() {
231        // TODO random-values loop against a rust well-tested library
232    }
233}