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)] {
116    let mut w = [[0u8; 4]; 4 * (NR + 1)]; // expanded key
117
118    for i in 0..NK {
119        w[i].clone_from_slice(&key[i * 4..i * 4 + 4]);
120    }
121
122    for i in NK..4 * (NR + 1) {
123        let mut temp = w[i - 1];
124        if i % NK == 0 {
125            let rcon: [u8; 4] = [RCON[i / NK], 0, 0, 0];
126            temp = xor_words(sub_word(rot_word(temp)), rcon);
127        } else if NK > 6 && i % NK == 4 {
128            temp = sub_word(temp);
129        }
130        w[i] = xor_words(w[i - NK], temp);
131    }
132    w
133}
134
135pub(crate) fn rot_word<T: Copy>(w: [T; 4]) -> [T; 4] {
136    array::from_fn(|i| w[(i + 1) % 4])
137}
138fn sub_word(w: [u8; 4]) -> [u8; 4] {
139    let mut r = [0u8; 4];
140    for i in 0..4 {
141        r[i] = SBOX[w[i] as usize];
142    }
143    r
144}
145fn xor_words(w1: [u8; 4], w2: [u8; 4]) -> [u8; 4] {
146    let mut r = [0u8; 4];
147    for i in 0..4 {
148        r[i] = w1[i] ^ w2[i];
149    }
150    r
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    /// test against test vectors from Appendix A
158    #[test]
159    fn test_key_expansion() {
160        // AES-128
161        let key = [
162            0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
163            0x4f, 0x3c,
164        ];
165        let expected: [u32; 4] = [0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c];
166        let w = key_expansion::</*Nk,Nb,Nr*/ 4, 4, 10>(&key);
167        for i in 0..4 {
168            assert_eq!(expected[i], u32::from_be_bytes(w[i]));
169        }
170
171        // AES-196
172        let key = [
173            0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90,
174            0x79, 0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b,
175        ];
176        let expected: [u32; 6] = [
177            0x8e73b0f7, 0xda0e6452, 0xc810f32b, 0x809079e5, 0x62f8ead2, 0x522c6b7b,
178        ];
179        let w = key_expansion::</*Nk,Nb,Nr*/ 6, 4, 12>(&key);
180        for i in 0..6 {
181            assert_eq!(expected[i], u32::from_be_bytes(w[i]));
182        }
183
184        // AES-256
185        let key = [
186            0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d,
187            0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3,
188            0x09, 0x14, 0xdf, 0xf4,
189        ];
190        let expected: [u32; 8] = [
191            0x603deb10, 0x15ca71be, 0x2b73aef0, 0x857d7781, 0x1f352c07, 0x3b6108d7, 0x2d9810a3,
192            0x0914dff4,
193        ];
194        let w = key_expansion::</*Nk,Nb,Nr*/ 8, 4, 14>(&key);
195        for i in 0..8 {
196            assert_eq!(expected[i], u32::from_be_bytes(w[i]));
197        }
198    }
199
200    /// test against paper test vector (appendix B)
201    #[test]
202    fn test_encrypt_block_test_vector() {
203        let input: [u8; 16] = [
204            0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37,
205            0x07, 0x34,
206        ];
207        let key: [u8; 16] = [
208            0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
209            0x4f, 0x3c,
210        ];
211        let expected: [u8; 16] = [
212            0x39, 0x02, 0xdc, 0x19, 0x25, 0xdc, 0x11, 0x6a, 0x84, 0x09, 0x85, 0x0b, 0x1d, 0xfb,
213            0x97, 0x32,
214        ];
215        let expanded_key = key_expansion::<4, 4, 10>(&key);
216        let c = encrypt_block::<10>(&input, &expanded_key);
217        assert_eq!(flatten_state(c), expected);
218    }
219
220    /// test with loop of random-values against a rust well-tested library
221    #[test]
222    fn test_encrypt_block_external_lib() {
223        // TODO random-values loop against a rust well-tested library
224    }
225}