#![allow(missing_docs)]
#![allow(
unsafe_code,
clippy::expect_used,
clippy::panic,
clippy::print_stdout,
clippy::unwrap_used
)]
#![cfg(all(feature = "native", feature = "aes", not(target_arch = "wasm32")))]
use aes_gcm::aead::consts::U12;
use aes_gcm::aead::{Aead, KeyInit, Payload};
use aes_gcm::{Aes256Gcm, Nonce};
use crypto_core::AeadAlgorithm;
use crypto_dispatch::error::AlgorithmError;
use crypto_dispatch::{aead_decrypt, aead_encrypt, AeadParams};
fn vector_key() -> [u8; 32] {
[
0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83,
0x08, 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30,
0x83, 0x08,
]
}
fn vector_nonce() -> [u8; 12] {
[
0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88,
]
}
fn vector_aad() -> Vec<u8> {
hex::decode("feedfacedeadbeeffeedfacedeadbeefabaddad2").expect("vector aad must decode")
}
fn vector_plaintext() -> Vec<u8> {
hex::decode(
"d9313225f88406e5a55909c5aff5269a\
86a7a9531534f7da2e4c303d8a318a72\
1c3c0c95956809532fcf0e2449a6b525\
b16aedf5aa0de657ba637b39",
)
.expect("vector plaintext must decode")
}
fn vector_ciphertext_and_tag() -> Vec<u8> {
hex::decode(
"522dc1f099567d07f47f37a32a84427d\
643a8cdcbfe5c0c97598a2bd2555d1aa\
8cb08e48590dbb3da7b08b1056828838\
c5f61e6393ba7a0abcc9f662\
76fc6ece0f4e1768cddf8853bb2d551b",
)
.expect("vector ciphertext must decode")
}
#[no_mangle]
pub unsafe extern "C" fn aes256gcm_encrypt(
key: *const u8,
key_len: usize,
nonce: *const u8,
nonce_len: usize,
aad: *const u8,
aad_len: usize,
plaintext: *const u8,
plaintext_len: usize,
ciphertext_out: *mut u8,
ciphertext_out_len: usize,
ciphertext_len_out: *mut usize,
) -> i32 {
if key_len != 32 || nonce_len != 12 {
return -1;
}
let key = unsafe { core::slice::from_raw_parts(key, key_len) };
let nonce = unsafe { core::slice::from_raw_parts(nonce, nonce_len) };
let aad = unsafe { core::slice::from_raw_parts(aad, aad_len) };
let plaintext = unsafe { core::slice::from_raw_parts(plaintext, plaintext_len) };
let cipher = match Aes256Gcm::new_from_slice(key) {
Ok(cipher) => cipher,
Err(_) => return -1,
};
let nonce_array: Nonce<U12> = match <[u8; 12]>::try_from(nonce) {
Ok(value) => value.into(),
Err(_) => return -2,
};
let payload = Payload {
msg: plaintext,
aad,
};
let encrypted = match cipher.encrypt(&nonce_array, payload) {
Ok(bytes) => bytes,
Err(_) => return -3,
};
if encrypted.len() > ciphertext_out_len {
return -3;
}
let out = unsafe { core::slice::from_raw_parts_mut(ciphertext_out, ciphertext_out_len) };
out[..encrypted.len()].copy_from_slice(&encrypted);
unsafe {
*ciphertext_len_out = encrypted.len();
}
0
}
#[no_mangle]
pub unsafe extern "C" fn aes256gcm_decrypt(
key: *const u8,
key_len: usize,
nonce: *const u8,
nonce_len: usize,
aad: *const u8,
aad_len: usize,
ciphertext: *const u8,
ciphertext_len: usize,
plaintext_out: *mut u8,
plaintext_out_len: usize,
plaintext_len_out: *mut usize,
) -> i32 {
if key_len != 32 || nonce_len != 12 {
return -1;
}
let key = unsafe { core::slice::from_raw_parts(key, key_len) };
let nonce = unsafe { core::slice::from_raw_parts(nonce, nonce_len) };
let aad = unsafe { core::slice::from_raw_parts(aad, aad_len) };
let ciphertext = unsafe { core::slice::from_raw_parts(ciphertext, ciphertext_len) };
let cipher = match Aes256Gcm::new_from_slice(key) {
Ok(cipher) => cipher,
Err(_) => return -1,
};
let nonce_array: Nonce<U12> = match <[u8; 12]>::try_from(nonce) {
Ok(value) => value.into(),
Err(_) => return -1,
};
let payload = Payload {
msg: ciphertext,
aad,
};
let plaintext = match cipher.decrypt(&nonce_array, payload) {
Ok(bytes) => bytes,
Err(_) => return -1,
};
if plaintext.len() > plaintext_out_len {
return -3;
}
let out = unsafe { core::slice::from_raw_parts_mut(plaintext_out, plaintext_out_len) };
out[..plaintext.len()].copy_from_slice(&plaintext);
unsafe {
*plaintext_len_out = plaintext.len();
}
0
}
#[test]
fn lane_runtime_aes_dispatch_matches_vector() {
let key = vector_key();
let nonce = vector_nonce();
let aad = vector_aad();
let plaintext = vector_plaintext();
let expected = vector_ciphertext_and_tag();
let params = AeadParams {
key: &key,
nonce: &nonce,
aad: &aad,
};
let encrypted = aead_encrypt(AeadAlgorithm::Aes256Gcm, ¶ms, &plaintext)
.expect("dispatch encryption must succeed");
assert_eq!(encrypted.as_slice(), expected.as_slice());
let decrypted = aead_decrypt(AeadAlgorithm::Aes256Gcm, ¶ms, &encrypted)
.expect("dispatch decryption must succeed");
assert_eq!(decrypted.as_slice(), plaintext.as_slice());
}
#[test]
fn lane_runtime_aes_dispatch_rejects_tampered_ciphertext() {
let key = vector_key();
let nonce = vector_nonce();
let aad = vector_aad();
let mut tampered = vector_ciphertext_and_tag();
tampered[0] ^= 0x80;
let params = AeadParams {
key: &key,
nonce: &nonce,
aad: &aad,
};
let err = aead_decrypt(AeadAlgorithm::Aes256Gcm, ¶ms, &tampered)
.expect_err("tampered ciphertext must fail");
match err {
AlgorithmError::Crypto(_) => {}
_ => panic!("unexpected error variant for tampered ciphertext"),
}
}