Actually support AES GCM
This is an AEAD cipher, so we need some extra functionality. As another bonus, we no longer panic if provided an IV with a different length than the cipher's default.
This commit is contained in:
parent
1edb6f682e
commit
203a02c3e6
|
|
@ -114,6 +114,10 @@ pub const EVP_PKEY_RSA: c_int = NID_rsaEncryption;
|
||||||
pub const EVP_PKEY_HMAC: c_int = NID_hmac;
|
pub const EVP_PKEY_HMAC: c_int = NID_hmac;
|
||||||
pub const EVP_PKEY_DSA: c_int = NID_dsa;
|
pub const EVP_PKEY_DSA: c_int = NID_dsa;
|
||||||
|
|
||||||
|
pub const EVP_CTRL_GCM_SET_IVLEN: c_int = 0x9;
|
||||||
|
pub const EVP_CTRL_GCM_GET_TAG: c_int = 0x10;
|
||||||
|
pub const EVP_CTRL_GCM_SET_TAG: c_int = 0x11;
|
||||||
|
|
||||||
pub const MBSTRING_ASC: c_int = MBSTRING_FLAG | 1;
|
pub const MBSTRING_ASC: c_int = MBSTRING_FLAG | 1;
|
||||||
pub const MBSTRING_BMP: c_int = MBSTRING_FLAG | 2;
|
pub const MBSTRING_BMP: c_int = MBSTRING_FLAG | 2;
|
||||||
pub const MBSTRING_FLAG: c_int = 0x1000;
|
pub const MBSTRING_FLAG: c_int = 0x1000;
|
||||||
|
|
@ -1400,6 +1404,7 @@ extern {
|
||||||
pub fn EVP_CIPHER_CTX_new() -> *mut EVP_CIPHER_CTX;
|
pub fn EVP_CIPHER_CTX_new() -> *mut EVP_CIPHER_CTX;
|
||||||
pub fn EVP_CIPHER_CTX_set_padding(ctx: *mut EVP_CIPHER_CTX, padding: c_int) -> c_int;
|
pub fn EVP_CIPHER_CTX_set_padding(ctx: *mut EVP_CIPHER_CTX, padding: c_int) -> c_int;
|
||||||
pub fn EVP_CIPHER_CTX_set_key_length(ctx: *mut EVP_CIPHER_CTX, keylen: c_int) -> c_int;
|
pub fn EVP_CIPHER_CTX_set_key_length(ctx: *mut EVP_CIPHER_CTX, keylen: c_int) -> c_int;
|
||||||
|
pub fn EVP_CIPHER_CTX_ctrl(ctx: *mut EVP_CIPHER_CTX, type_: c_int, arg: c_int, ptr: *mut c_void) -> c_int;
|
||||||
pub fn EVP_CIPHER_CTX_free(ctx: *mut EVP_CIPHER_CTX);
|
pub fn EVP_CIPHER_CTX_free(ctx: *mut EVP_CIPHER_CTX);
|
||||||
|
|
||||||
pub fn EVP_CipherInit(ctx: *mut EVP_CIPHER_CTX, evp: *const EVP_CIPHER,
|
pub fn EVP_CipherInit(ctx: *mut EVP_CIPHER_CTX, evp: *const EVP_CIPHER,
|
||||||
|
|
|
||||||
|
|
@ -135,8 +135,7 @@ impl Crypter {
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if an IV is required by the cipher but not provided, or if the
|
/// Panics if an IV is required by the cipher but not provided.
|
||||||
/// IV's length does not match the expected length (see `Cipher::iv_len`).
|
|
||||||
pub fn new(t: Cipher,
|
pub fn new(t: Cipher,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
key: &[u8],
|
key: &[u8],
|
||||||
|
|
@ -169,7 +168,13 @@ impl Crypter {
|
||||||
let key = key.as_ptr() as *mut _;
|
let key = key.as_ptr() as *mut _;
|
||||||
let iv = match (iv, t.iv_len()) {
|
let iv = match (iv, t.iv_len()) {
|
||||||
(Some(iv), Some(len)) => {
|
(Some(iv), Some(len)) => {
|
||||||
assert!(iv.len() == len);
|
if iv.len() != len {
|
||||||
|
assert!(iv.len() <= c_int::max_value() as usize);
|
||||||
|
try!(cvt(ffi::EVP_CIPHER_CTX_ctrl(crypter.ctx,
|
||||||
|
ffi::EVP_CTRL_GCM_SET_IVLEN,
|
||||||
|
iv.len() as c_int,
|
||||||
|
ptr::null_mut())));
|
||||||
|
}
|
||||||
iv.as_ptr() as *mut _
|
iv.as_ptr() as *mut _
|
||||||
}
|
}
|
||||||
(Some(_), None) | (None, None) => ptr::null_mut(),
|
(Some(_), None) | (None, None) => ptr::null_mut(),
|
||||||
|
|
@ -196,6 +201,39 @@ impl Crypter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the tag used to authenticate ciphertext in AEAD ciphers such as AES GCM.
|
||||||
|
///
|
||||||
|
/// When decrypting cipher text using an AEAD cipher, this must be called before `finalize`.
|
||||||
|
pub fn set_tag(&mut self, tag: &[u8]) -> Result<(), ErrorStack> {
|
||||||
|
unsafe {
|
||||||
|
assert!(tag.len() <= c_int::max_value() as usize);
|
||||||
|
// NB: this constant is actually more general than just GCM.
|
||||||
|
cvt(ffi::EVP_CIPHER_CTX_ctrl(self.ctx,
|
||||||
|
ffi::EVP_CTRL_GCM_SET_TAG,
|
||||||
|
tag.len() as c_int,
|
||||||
|
tag.as_ptr() as *mut _))
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Feeds Additional Authenticated Data (AAD) through the cipher.
|
||||||
|
///
|
||||||
|
/// This can only be used with AEAD ciphers such as AES GCM. Data fed in is not encrypted, but
|
||||||
|
/// is factored into the authentication tag. It must be called before the first call to
|
||||||
|
/// `update`.
|
||||||
|
pub fn aad_update(&mut self, input: &[u8]) -> Result<(), ErrorStack> {
|
||||||
|
unsafe {
|
||||||
|
assert!(input.len() <= c_int::max_value() as usize);
|
||||||
|
let mut len = 0;
|
||||||
|
cvt(ffi::EVP_CipherUpdate(self.ctx,
|
||||||
|
ptr::null_mut(),
|
||||||
|
&mut len,
|
||||||
|
input.as_ptr(),
|
||||||
|
input.len() as c_int))
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Feeds data from `input` through the cipher, writing encrypted/decrypted
|
/// Feeds data from `input` through the cipher, writing encrypted/decrypted
|
||||||
/// bytes into `output`.
|
/// bytes into `output`.
|
||||||
///
|
///
|
||||||
|
|
@ -244,6 +282,21 @@ impl Crypter {
|
||||||
Ok(outl as usize)
|
Ok(outl as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the authentication tag used to authenticate ciphertext in AEAD ciphers such
|
||||||
|
/// as AES GCM.
|
||||||
|
///
|
||||||
|
/// When encrypting data with an AEAD cipher, this must be called after `finalize`.
|
||||||
|
pub fn get_tag(&self, tag: &mut [u8]) -> Result<(), ErrorStack> {
|
||||||
|
unsafe {
|
||||||
|
assert!(tag.len() <= c_int::max_value() as usize);
|
||||||
|
cvt(ffi::EVP_CIPHER_CTX_ctrl(self.ctx,
|
||||||
|
ffi::EVP_CTRL_GCM_GET_TAG,
|
||||||
|
tag.len() as c_int,
|
||||||
|
tag.as_mut_ptr() as *mut _))
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Crypter {
|
impl Drop for Crypter {
|
||||||
|
|
@ -319,6 +372,7 @@ use self::compat::*;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use serialize::hex::{FromHex, ToHex};
|
use serialize::hex::{FromHex, ToHex};
|
||||||
|
use super::*;
|
||||||
|
|
||||||
// Test vectors from FIPS-197:
|
// Test vectors from FIPS-197:
|
||||||
// http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
|
// http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
|
||||||
|
|
@ -534,4 +588,42 @@ mod tests {
|
||||||
|
|
||||||
cipher_test(super::Cipher::des_ecb(), pt, ct, key, iv);
|
cipher_test(super::Cipher::des_ecb(), pt, ct, key, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_aes128_gcm() {
|
||||||
|
let key = "0e00c76561d2bd9b40c3c15427e2b08f";
|
||||||
|
let iv =
|
||||||
|
"492cadaccd3ca3fbc9cf9f06eb3325c4e159850b0dbe98199b89b7af528806610b6f63998e1eae80c348e7\
|
||||||
|
4cbb921d8326631631fc6a5d304f39166daf7ea15fa1977f101819adb510b50fe9932e12c5a85aa3fd1e73\
|
||||||
|
d8d760af218be829903a77c63359d75edd91b4f6ed5465a72662f5055999e059e7654a8edc921aa0d496";
|
||||||
|
let pt =
|
||||||
|
"fef03c2d7fb15bf0d2df18007d99f967c878ad59359034f7bb2c19af120685d78e32f6b8b83b032019956c\
|
||||||
|
a9c0195721476b85";
|
||||||
|
let aad =
|
||||||
|
"d8f1163d8c840292a2b2dacf4ac7c36aff8733f18fabb4fa5594544125e03d1e6e5d6d0fd61656c8d8f327\
|
||||||
|
c92839ae5539bb469c9257f109ebff85aad7bd220fdaa95c022dbd0c7bb2d878ad504122c943045d3c5eba\
|
||||||
|
8f1f56c0";
|
||||||
|
let ct =
|
||||||
|
"4f6cf471be7cbd2575cd5a1747aea8fe9dea83e51936beac3e68f66206922060c697ffa7af80ad6bb68f2c\
|
||||||
|
f4fc97416ee52abe";
|
||||||
|
let tag = "e20b6655";
|
||||||
|
|
||||||
|
let mut crypter = Crypter::new(Cipher::aes_128_gcm(), Mode::Encrypt, &key.from_hex().unwrap(), Some(&iv.from_hex().unwrap())).unwrap();
|
||||||
|
let mut out = [0; 1024];
|
||||||
|
crypter.aad_update(&aad.from_hex().unwrap()).unwrap();
|
||||||
|
let mut nwritten = crypter.update(&pt.from_hex().unwrap(), &mut out).unwrap();
|
||||||
|
nwritten += crypter.finalize(&mut out[nwritten..]).unwrap();
|
||||||
|
assert_eq!(ct, out[..nwritten].to_hex());
|
||||||
|
let mut actual_tag = [0; 4];
|
||||||
|
crypter.get_tag(&mut actual_tag).unwrap();
|
||||||
|
assert_eq!(tag, actual_tag.to_hex());
|
||||||
|
|
||||||
|
let mut crypter = Crypter::new(Cipher::aes_128_gcm(), Mode::Decrypt, &key.from_hex().unwrap(), Some(&iv.from_hex().unwrap())).unwrap();
|
||||||
|
let mut out = [0; 1024];
|
||||||
|
crypter.aad_update(&aad.from_hex().unwrap()).unwrap();
|
||||||
|
let mut nwritten = crypter.update(&ct.from_hex().unwrap(), &mut out).unwrap();
|
||||||
|
crypter.set_tag(&tag.from_hex().unwrap()).unwrap();
|
||||||
|
nwritten += crypter.finalize(&mut out[nwritten..]).unwrap();
|
||||||
|
assert_eq!(pt, out[..nwritten].to_hex());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue