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