arnaucube
arnaucube/plonky2-crypto-gadgetsPublic Self-hosted repository
plonky2-crypto-gadgets/aes-gcm/src/native_aes.rs
aes-gcm/src/native_aes.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, nor memory-efficient, but simulating
  7//! the behavior that we will do later inside the circuit.
  8
  9use std::array;
 10
 11use crate::constants::{RCON, SBOX};
 12
 13pub(crate) type State = [[u8; 4]; 4];
 14
 15pub(crate) fn flatten_state(s: State) -> [u8; 16] {
 16    let mut r = [0u8; 16];
 17    for i in 0..4 {
 18        for j in 0..4 {
 19            r[i * 4 + j] = s[j][i];
 20        }
 21    }
 22    r
 23}
 24
 25/// encrypts an AES block (16 bytes). NR determines the number of rounds,
 26/// where AES-128: NR=10, AES-196: NR=12, AES-256: NR=14.
 27pub(crate) fn encrypt_block<const NR: usize>(
 28    input: &[u8; 16],
 29    w: &[[u8; 4]; 4 * (NR + 1)],
 30) -> State {
 31    assert_eq!(4 * (NR + 1), w.len());
 32
 33    let mut s: State = [[0; 4]; 4];
 34    for i in 0..4 {
 35        for j in 0..4 {
 36            // TODO
 37            s[i][j] = input[i + 4 * j];
 38        }
 39    }
 40
 41    s = add_round_key(s, &w[0..4]);
 42
 43    for round in 1..NR {
 44        s = sub_bytes(s);
 45        s = shift_rows(s);
 46        s = mix_columns(s);
 47        s = add_round_key(s, &w[4 * round..4 * round + 4]);
 48    }
 49
 50    s = sub_bytes(s);
 51    s = shift_rows(s);
 52    s = add_round_key(s, &w[4 * NR..4 * NR + 4]);
 53
 54    s
 55}
 56
 57pub(crate) fn sub_bytes(s: State) -> State {
 58    let mut r = [[0u8; 4]; 4];
 59    for i in 0..4 {
 60        for j in 0..4 {
 61            r[i][j] = SBOX[s[i][j] as usize];
 62        }
 63    }
 64    r
 65}
 66
 67pub(crate) fn shift_rows<T: Copy>(s: [[T; 4]; 4]) -> [[T; 4]; 4] {
 68    array::from_fn(|i| array::from_fn(|j| s[i][(i + j) % 4]))
 69}
 70
 71pub(crate) fn 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
 83pub(crate) fn 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
112pub(crate) fn key_expansion<const NK: usize, const NB: usize, const NR: usize>(
113    key: &[u8; NK * NB],
114) -> [[u8; 4]; 4 * (NR + 1)] {
115    let mut w = [[0u8; 4]; 4 * (NR + 1)]; // expanded key
116
117    for i in 0..NK {
118        w[i].clone_from_slice(&key[i * 4..i * 4 + 4]);
119    }
120
121    for i in NK..4 * (NR + 1) {
122        let mut temp = w[i - 1];
123        if i % NK == 0 {
124            let rcon: [u8; 4] = [RCON[i / NK], 0, 0, 0];
125            temp = xor_words(sub_word(rot_word(temp)), rcon);
126        } else if NK > 6 && i % NK == 4 {
127            temp = sub_word(temp);
128        }
129        w[i] = xor_words(w[i - NK], temp);
130    }
131    w
132}
133
134pub(crate) fn rot_word<T: Copy>(w: [T; 4]) -> [T; 4] {
135    array::from_fn(|i| w[(i + 1) % 4])
136}
137fn sub_word(w: [u8; 4]) -> [u8; 4] {
138    let mut r = [0u8; 4];
139    for i in 0..4 {
140        r[i] = SBOX[w[i] as usize];
141    }
142    r
143}
144fn xor_words(w1: [u8; 4], w2: [u8; 4]) -> [u8; 4] {
145    let mut r = [0u8; 4];
146    for i in 0..4 {
147        r[i] = w1[i] ^ w2[i];
148    }
149    r
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    /// test against test vectors from Appendix A
157    #[test]
158    fn test_key_expansion() {
159        // AES-128
160        let key = [
161            0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
162            0x4f, 0x3c,
163        ];
164        let expected: [u32; 4] = [0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c];
165        let w = key_expansion::</*Nk,Nb,Nr*/ 4, 4, 10>(&key);
166        for i in 0..4 {
167            assert_eq!(expected[i], u32::from_be_bytes(w[i]));
168        }
169
170        // AES-196
171        let key = [
172            0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90,
173            0x79, 0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b,
174        ];
175        let expected: [u32; 6] = [
176            0x8e73b0f7, 0xda0e6452, 0xc810f32b, 0x809079e5, 0x62f8ead2, 0x522c6b7b,
177        ];
178        let w = key_expansion::</*Nk,Nb,Nr*/ 6, 4, 12>(&key);
179        for i in 0..6 {
180            assert_eq!(expected[i], u32::from_be_bytes(w[i]));
181        }
182
183        // AES-256
184        let key = [
185            0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d,
186            0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3,
187            0x09, 0x14, 0xdf, 0xf4,
188        ];
189        let expected: [u32; 8] = [
190            0x603deb10, 0x15ca71be, 0x2b73aef0, 0x857d7781, 0x1f352c07, 0x3b6108d7, 0x2d9810a3,
191            0x0914dff4,
192        ];
193        let w = key_expansion::</*Nk,Nb,Nr*/ 8, 4, 14>(&key);
194        for i in 0..8 {
195            assert_eq!(expected[i], u32::from_be_bytes(w[i]));
196        }
197    }
198
199    /// test against paper test vector (appendix B)
200    #[test]
201    fn test_encrypt_block_test_vector() {
202        let input: [u8; 16] = [
203            0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37,
204            0x07, 0x34,
205        ];
206        let key: [u8; 16] = [
207            0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
208            0x4f, 0x3c,
209        ];
210        let expected: [u8; 16] = [
211            0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a,
212            0x0b, 0x32,
213        ];
214        let expanded_key = key_expansion::<4, 4, 10>(&key);
215        let c = encrypt_block::<10>(&input, &expanded_key);
216        assert_eq!(flatten_state(c), expected);
217    }
218
219    use aes::{
220        Aes128, Aes192, Aes256,
221        cipher::{BlockDecrypt, BlockEncrypt, BlockSizeUser, KeyInit, generic_array::GenericArray},
222    };
223    use rand::RngExt;
224    /// test with loop of random-values against a rust well-tested library
225    #[test]
226    fn test_encrypt_block_external_lib() {
227        let mut rng = rand::rng();
228
229        // AES-128
230        {
231            const NK: usize = 4;
232            const NB: usize = 4;
233            const NR: usize = 10;
234
235            for _ in 0..10 {
236                let key: [u8; NK * NB] = array::from_fn(|_| rng.random());
237                let block: [u8; 16] = array::from_fn(|_| rng.random());
238                let block = GenericArray::from(block);
239                test_encrypt_block_external_lib_op::<NK, NB, NR, Aes128>(key, block);
240            }
241        }
242        // AES-192
243        {
244            const NK: usize = 6;
245            const NB: usize = 4;
246            const NR: usize = 12;
247
248            for _ in 0..10 {
249                let key: [u8; NK * NB] = array::from_fn(|_| rng.random());
250                let block: [u8; 16] = array::from_fn(|_| rng.random());
251                let block = GenericArray::from(block);
252                test_encrypt_block_external_lib_op::<NK, NB, NR, Aes192>(key, block);
253            }
254        }
255        // AES-256
256        {
257            const NK: usize = 8;
258            const NB: usize = 4;
259            const NR: usize = 14;
260
261            for _ in 0..10 {
262                let key: [u8; NK * NB] = array::from_fn(|_| rng.random());
263                let block: [u8; 16] = array::from_fn(|_| rng.random());
264                let block = GenericArray::from(block);
265                test_encrypt_block_external_lib_op::<NK, NB, NR, Aes256>(key, block);
266            }
267        }
268    }
269    fn test_encrypt_block_external_lib_op<const NK: usize, const NB: usize, const NR: usize, C>(
270        key_arr: [u8; NK * NB],
271        block: GenericArray<u8, C::BlockSize>,
272    ) where
273        C: BlockEncrypt + BlockDecrypt + BlockSizeUser + KeyInit,
274        [(); 4 * (NR + 1)]:,
275    {
276        let cipher = C::new_from_slice(&key_arr).unwrap();
277
278        let mut block_arr: [u8; 16] = [0; 16];
279        block_arr.clone_from_slice(block.as_slice());
280        let mut block = block.clone();
281        let block_copy = block.clone();
282
283        // encrypt block in-place
284        cipher.encrypt_block(&mut block);
285        let binding = block.clone();
286        let expected_cipher = binding.as_slice();
287
288        // and decrypt it back
289        cipher.decrypt_block(&mut block);
290        assert_eq!(block, block_copy);
291
292        // now use our native implementation
293        let expanded_key = key_expansion::<NK, NB, NR>(&key_arr);
294        let c = encrypt_block::<NR>(&block_arr, &expanded_key);
295        // check native implementation vs external lib
296        assert_eq!(flatten_state(c), expected_cipher);
297    }
298}