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