#[rustfmt::skip]
const SBOX: [u8; 256] = [
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16,
];
#[rustfmt::skip]
const INV_SBOX: [u8; 256] = [
0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,
0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,
0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,
0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,
0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,
0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,
0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,
0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,
0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,
0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,
0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,
0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,
0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,
0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,
0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,
0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d,
];
const RCON: [u8; 11] = [
0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
];
fn gmul(mut a: u8, mut b: u8) -> u8 {
let mut p = 0u8;
for _ in 0..8 {
if b & 1 != 0 {
p ^= a;
}
let hi = a & 0x80;
a <<= 1;
if hi != 0 {
a ^= 0x1b;
}
b >>= 1;
}
p
}
fn xtime(a: u8) -> u8 {
(a << 1) ^ (((a >> 7) & 1) * 0x1b)
}
pub(crate) struct Aes {
words: Vec<u32>,
rounds: usize,
}
impl Aes {
pub(crate) fn new(key: &[u8]) -> Aes {
let nk = key.len() / 4;
let rounds = nk + 6;
let total = 4 * (rounds + 1);
let mut w = Vec::with_capacity(total);
for chunk in key.chunks_exact(4) {
w.push(u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]));
}
for i in nk..total {
let mut temp = w[i - 1];
if i % nk == 0 {
temp = sub_word(temp.rotate_left(8)) ^ ((RCON[i / nk] as u32) << 24);
} else if nk > 6 && i % nk == 4 {
temp = sub_word(temp);
}
w.push(w[i - nk] ^ temp);
}
Aes { words: w, rounds }
}
fn round_key(&self, round: usize) -> [u8; 16] {
let mut rk = [0u8; 16];
for c in 0..4 {
rk[c * 4..c * 4 + 4].copy_from_slice(&self.words[round * 4 + c].to_be_bytes());
}
rk
}
pub(crate) fn encrypt_block(&self, block: &mut [u8; 16]) {
add_round_key(block, &self.round_key(0));
for round in 1..self.rounds {
sub_bytes(block);
shift_rows(block);
mix_columns(block);
add_round_key(block, &self.round_key(round));
}
sub_bytes(block);
shift_rows(block);
add_round_key(block, &self.round_key(self.rounds));
}
pub(crate) fn decrypt_block(&self, block: &mut [u8; 16]) {
add_round_key(block, &self.round_key(self.rounds));
for round in (1..self.rounds).rev() {
inv_shift_rows(block);
inv_sub_bytes(block);
add_round_key(block, &self.round_key(round));
inv_mix_columns(block);
}
inv_shift_rows(block);
inv_sub_bytes(block);
add_round_key(block, &self.round_key(0));
}
pub(crate) fn ctr_xor(&self, counter: &[u8; 16], data: &mut [u8]) {
let mut ctr = *counter;
for block in data.chunks_mut(16) {
let mut ks = ctr;
self.encrypt_block(&mut ks);
for (b, k) in block.iter_mut().zip(ks.iter()) {
*b ^= *k;
}
increment_be(&mut ctr);
}
}
}
fn sub_word(w: u32) -> u32 {
let b = w.to_be_bytes();
u32::from_be_bytes([
SBOX[b[0] as usize],
SBOX[b[1] as usize],
SBOX[b[2] as usize],
SBOX[b[3] as usize],
])
}
fn add_round_key(s: &mut [u8; 16], rk: &[u8; 16]) {
for i in 0..16 {
s[i] ^= rk[i];
}
}
fn sub_bytes(s: &mut [u8; 16]) {
for b in s.iter_mut() {
*b = SBOX[*b as usize];
}
}
fn inv_sub_bytes(s: &mut [u8; 16]) {
for b in s.iter_mut() {
*b = INV_SBOX[*b as usize];
}
}
fn shift_rows(s: &mut [u8; 16]) {
let o = *s;
for r in 1..4 {
for c in 0..4 {
s[r + 4 * c] = o[r + 4 * ((c + r) % 4)];
}
}
}
fn inv_shift_rows(s: &mut [u8; 16]) {
let o = *s;
for r in 1..4 {
for c in 0..4 {
s[r + 4 * c] = o[r + 4 * ((c + 4 - r) % 4)];
}
}
}
fn mix_columns(s: &mut [u8; 16]) {
for c in 0..4 {
let i = c * 4;
let a = [s[i], s[i + 1], s[i + 2], s[i + 3]];
let t = a[0] ^ a[1] ^ a[2] ^ a[3];
s[i] ^= t ^ xtime(a[0] ^ a[1]);
s[i + 1] ^= t ^ xtime(a[1] ^ a[2]);
s[i + 2] ^= t ^ xtime(a[2] ^ a[3]);
s[i + 3] ^= t ^ xtime(a[3] ^ a[0]);
}
}
fn inv_mix_columns(s: &mut [u8; 16]) {
for c in 0..4 {
let i = c * 4;
let a = [s[i], s[i + 1], s[i + 2], s[i + 3]];
s[i] = gmul(a[0], 14) ^ gmul(a[1], 11) ^ gmul(a[2], 13) ^ gmul(a[3], 9);
s[i + 1] = gmul(a[0], 9) ^ gmul(a[1], 14) ^ gmul(a[2], 11) ^ gmul(a[3], 13);
s[i + 2] = gmul(a[0], 13) ^ gmul(a[1], 9) ^ gmul(a[2], 14) ^ gmul(a[3], 11);
s[i + 3] = gmul(a[0], 11) ^ gmul(a[1], 13) ^ gmul(a[2], 9) ^ gmul(a[3], 14);
}
}
fn increment_be(ctr: &mut [u8; 16]) {
for byte in ctr.iter_mut().rev() {
*byte = byte.wrapping_add(1);
if *byte != 0 {
break;
}
}
}
const KW_IV: [u8; 8] = [0xA6; 8];
pub(crate) fn aes_key_wrap(kek: &[u8], plaintext: &[u8]) -> Vec<u8> {
let aes = Aes::new(kek);
let n = plaintext.len() / 8;
let mut a = KW_IV;
let mut r: Vec<[u8; 8]> = plaintext
.chunks_exact(8)
.map(|c| c.try_into().unwrap())
.collect();
for j in 0..6 {
for (i, ri) in r.iter_mut().enumerate() {
let mut block = [0u8; 16];
block[..8].copy_from_slice(&a);
block[8..].copy_from_slice(ri);
aes.encrypt_block(&mut block);
a.copy_from_slice(&block[..8]);
let t = (n * j + i + 1) as u64;
for (k, tb) in t.to_be_bytes().iter().enumerate() {
a[k] ^= tb;
}
ri.copy_from_slice(&block[8..]);
}
}
let mut out = Vec::with_capacity(8 + plaintext.len());
out.extend_from_slice(&a);
for ri in &r {
out.extend_from_slice(ri);
}
out
}
pub(crate) fn aes_key_unwrap(kek: &[u8], ciphertext: &[u8]) -> Option<Vec<u8>> {
if ciphertext.len() < 16 || ciphertext.len() % 8 != 0 {
return None;
}
let aes = Aes::new(kek);
let n = ciphertext.len() / 8 - 1;
let mut a = [0u8; 8];
a.copy_from_slice(&ciphertext[..8]);
let mut r: Vec<[u8; 8]> = ciphertext[8..]
.chunks_exact(8)
.map(|c| c.try_into().unwrap())
.collect();
for j in (0..6).rev() {
for i in (0..n).rev() {
let t = (n * j + i + 1) as u64;
let mut av = a;
for (k, tb) in t.to_be_bytes().iter().enumerate() {
av[k] ^= tb;
}
let mut block = [0u8; 16];
block[..8].copy_from_slice(&av);
block[8..].copy_from_slice(&r[i]);
aes.decrypt_block(&mut block);
a.copy_from_slice(&block[..8]);
r[i].copy_from_slice(&block[8..]);
}
}
if a != KW_IV {
return None; }
let mut out = Vec::with_capacity(n * 8);
for ri in &r {
out.extend_from_slice(ri);
}
Some(out)
}
const SHA1_BLOCK: usize = 64;
pub(crate) fn sha1(msg: &[u8]) -> [u8; 20] {
let mut h: [u32; 5] = [
0x6745_2301,
0xEFCD_AB89,
0x98BA_DCFE,
0x1032_5476,
0xC3D2_E1F0,
];
let bit_len = (msg.len() as u64).wrapping_mul(8);
let mut padded = Vec::with_capacity(msg.len() + 9 + 63);
padded.extend_from_slice(msg);
padded.push(0x80);
while padded.len() % SHA1_BLOCK != 56 {
padded.push(0);
}
padded.extend_from_slice(&bit_len.to_be_bytes());
let mut w = [0u32; 80];
for block in padded.chunks_exact(SHA1_BLOCK) {
for (i, word) in w.iter_mut().take(16).enumerate() {
*word = u32::from_be_bytes([
block[i * 4],
block[i * 4 + 1],
block[i * 4 + 2],
block[i * 4 + 3],
]);
}
for i in 16..80 {
w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
}
let (mut a, mut b, mut c, mut d, mut e) = (h[0], h[1], h[2], h[3], h[4]);
for (i, &wi) in w.iter().enumerate() {
let (f, k) = match i {
0..=19 => ((b & c) | ((!b) & d), 0x5A82_7999u32),
20..=39 => (b ^ c ^ d, 0x6ED9_EBA1),
40..=59 => ((b & c) | (b & d) | (c & d), 0x8F1B_BCDC),
_ => (b ^ c ^ d, 0xCA62_C1D6),
};
let temp = a
.rotate_left(5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(k)
.wrapping_add(wi);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = temp;
}
h[0] = h[0].wrapping_add(a);
h[1] = h[1].wrapping_add(b);
h[2] = h[2].wrapping_add(c);
h[3] = h[3].wrapping_add(d);
h[4] = h[4].wrapping_add(e);
}
let mut out = [0u8; 20];
for (i, word) in h.iter().enumerate() {
out[i * 4..i * 4 + 4].copy_from_slice(&word.to_be_bytes());
}
out
}
pub(crate) fn hmac_sha1(key: &[u8], msg: &[u8]) -> [u8; 20] {
let mut k = [0u8; SHA1_BLOCK];
if key.len() > SHA1_BLOCK {
k[..20].copy_from_slice(&sha1(key));
} else {
k[..key.len()].copy_from_slice(key);
}
let mut ipad = [0x36u8; SHA1_BLOCK];
let mut opad = [0x5cu8; SHA1_BLOCK];
for i in 0..SHA1_BLOCK {
ipad[i] ^= k[i];
opad[i] ^= k[i];
}
let mut inner = Vec::with_capacity(SHA1_BLOCK + msg.len());
inner.extend_from_slice(&ipad);
inner.extend_from_slice(msg);
let inner_hash = sha1(&inner);
let mut outer = Vec::with_capacity(SHA1_BLOCK + 20);
outer.extend_from_slice(&opad);
outer.extend_from_slice(&inner_hash);
sha1(&outer)
}
pub(crate) fn pbkdf2_hmac_sha1(
password: &[u8],
salt: &[u8],
iterations: u32,
dk_len: usize,
) -> Vec<u8> {
let mut dk = Vec::with_capacity(dk_len);
let mut block_index: u32 = 1;
while dk.len() < dk_len {
let mut msg = Vec::with_capacity(salt.len() + 4);
msg.extend_from_slice(salt);
msg.extend_from_slice(&block_index.to_be_bytes());
let mut u = hmac_sha1(password, &msg);
let mut t = u;
for _ in 1..iterations {
u = hmac_sha1(password, &u);
for (tb, ub) in t.iter_mut().zip(u.iter()) {
*tb ^= *ub;
}
}
dk.extend_from_slice(&t);
block_index += 1;
}
dk.truncate(dk_len);
dk
}
#[cfg(test)]
mod tests {
use super::*;
fn hex(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{b:02x}")).collect()
}
fn unhex(s: &str) -> Vec<u8> {
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
.collect()
}
#[test]
fn aes128_fips197() {
let key = unhex("000102030405060708090a0b0c0d0e0f");
let mut blk: [u8; 16] = unhex("00112233445566778899aabbccddeeff")
.try_into()
.unwrap();
let aes = Aes::new(&key);
aes.encrypt_block(&mut blk);
assert_eq!(hex(&blk), "69c4e0d86a7b0430d8cdb78070b4c55a");
aes.decrypt_block(&mut blk);
assert_eq!(hex(&blk), "00112233445566778899aabbccddeeff");
}
#[test]
fn aes192_fips197() {
let key = unhex("000102030405060708090a0b0c0d0e0f1011121314151617");
let mut blk: [u8; 16] = unhex("00112233445566778899aabbccddeeff")
.try_into()
.unwrap();
let aes = Aes::new(&key);
aes.encrypt_block(&mut blk);
assert_eq!(hex(&blk), "dda97ca4864cdfe06eaf70a0ec0d7191");
}
#[test]
fn aes256_fips197() {
let key = unhex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
let mut blk: [u8; 16] = unhex("00112233445566778899aabbccddeeff")
.try_into()
.unwrap();
let aes = Aes::new(&key);
aes.encrypt_block(&mut blk);
assert_eq!(hex(&blk), "8ea2b7ca516745bfeafc49904b496089");
aes.decrypt_block(&mut blk);
assert_eq!(hex(&blk), "00112233445566778899aabbccddeeff");
}
#[test]
fn key_wrap_rfc3394() {
let kek = unhex("000102030405060708090a0b0c0d0e0f");
let data = unhex("00112233445566778899aabbccddeeff");
let wrapped = aes_key_wrap(&kek, &data);
assert_eq!(
hex(&wrapped),
"1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5"
);
let unwrapped = aes_key_unwrap(&kek, &wrapped).unwrap();
assert_eq!(unwrapped, data);
}
#[test]
fn key_unwrap_rejects_tampered() {
let kek = unhex("000102030405060708090a0b0c0d0e0f");
let mut wrapped = aes_key_wrap(&kek, &unhex("00112233445566778899aabbccddeeff"));
wrapped[0] ^= 0xFF;
assert!(aes_key_unwrap(&kek, &wrapped).is_none());
}
#[test]
fn ctr_is_symmetric() {
let aes = Aes::new(&unhex("2b7e151628aed2a6abf7158809cf4f3c"));
let ctr = [0u8; 16];
let mut data = b"the quick brown fox jumps over a lazy SRT packet".to_vec();
let orig = data.clone();
aes.ctr_xor(&ctr, &mut data);
assert_ne!(data, orig);
aes.ctr_xor(&ctr, &mut data);
assert_eq!(data, orig);
}
#[test]
fn ctr_nist_sp800_38a() {
let aes = Aes::new(&unhex("2b7e151628aed2a6abf7158809cf4f3c"));
let ctr: [u8; 16] = unhex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")
.try_into()
.unwrap();
let mut data = unhex("6bc1bee22e409f96e93d7e117393172a");
aes.ctr_xor(&ctr, &mut data);
assert_eq!(hex(&data), "874d6191b620e3261bef6864990db6ce");
}
#[test]
fn sha1_fips180() {
assert_eq!(
hex(&sha1(b"abc")),
"a9993e364706816aba3e25717850c26c9cd0d89d"
);
assert_eq!(hex(&sha1(b"")), "da39a3ee5e6b4b0d3255bfef95601890afd80709");
}
#[test]
fn hmac_sha1_rfc2202() {
let mac = hmac_sha1(&[0x0b; 20], b"Hi There");
assert_eq!(hex(&mac), "b617318655057264e28bc0b6fb378c8ef146be00");
}
#[test]
fn pbkdf2_rfc6070() {
assert_eq!(
hex(&pbkdf2_hmac_sha1(b"password", b"salt", 1, 20)),
"0c60c80f961f0e71f3a9b524af6012062fe037a6"
);
assert_eq!(
hex(&pbkdf2_hmac_sha1(b"password", b"salt", 2, 20)),
"ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"
);
}
}