Merge pull request #864 from mlen/aes-ccm-bindings
Implement AES-{128,256}-CCM bindings
This commit is contained in:
commit
9452c01672
|
|
@ -1954,6 +1954,7 @@ extern "C" {
|
||||||
pub fn EVP_aes_128_xts() -> *const EVP_CIPHER;
|
pub fn EVP_aes_128_xts() -> *const EVP_CIPHER;
|
||||||
pub fn EVP_aes_128_ctr() -> *const EVP_CIPHER;
|
pub fn EVP_aes_128_ctr() -> *const EVP_CIPHER;
|
||||||
pub fn EVP_aes_128_gcm() -> *const EVP_CIPHER;
|
pub fn EVP_aes_128_gcm() -> *const EVP_CIPHER;
|
||||||
|
pub fn EVP_aes_128_ccm() -> *const EVP_CIPHER;
|
||||||
pub fn EVP_aes_128_cfb1() -> *const EVP_CIPHER;
|
pub fn EVP_aes_128_cfb1() -> *const EVP_CIPHER;
|
||||||
pub fn EVP_aes_128_cfb128() -> *const EVP_CIPHER;
|
pub fn EVP_aes_128_cfb128() -> *const EVP_CIPHER;
|
||||||
pub fn EVP_aes_128_cfb8() -> *const EVP_CIPHER;
|
pub fn EVP_aes_128_cfb8() -> *const EVP_CIPHER;
|
||||||
|
|
@ -1962,6 +1963,7 @@ extern "C" {
|
||||||
pub fn EVP_aes_256_xts() -> *const EVP_CIPHER;
|
pub fn EVP_aes_256_xts() -> *const EVP_CIPHER;
|
||||||
pub fn EVP_aes_256_ctr() -> *const EVP_CIPHER;
|
pub fn EVP_aes_256_ctr() -> *const EVP_CIPHER;
|
||||||
pub fn EVP_aes_256_gcm() -> *const EVP_CIPHER;
|
pub fn EVP_aes_256_gcm() -> *const EVP_CIPHER;
|
||||||
|
pub fn EVP_aes_256_ccm() -> *const EVP_CIPHER;
|
||||||
pub fn EVP_aes_256_cfb1() -> *const EVP_CIPHER;
|
pub fn EVP_aes_256_cfb1() -> *const EVP_CIPHER;
|
||||||
pub fn EVP_aes_256_cfb128() -> *const EVP_CIPHER;
|
pub fn EVP_aes_256_cfb128() -> *const EVP_CIPHER;
|
||||||
pub fn EVP_aes_256_cfb8() -> *const EVP_CIPHER;
|
pub fn EVP_aes_256_cfb8() -> *const EVP_CIPHER;
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ pub enum Mode {
|
||||||
/// See OpenSSL doc at [`EVP_EncryptInit`] for more information on each algorithms.
|
/// See OpenSSL doc at [`EVP_EncryptInit`] for more information on each algorithms.
|
||||||
///
|
///
|
||||||
/// [`EVP_EncryptInit`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_EncryptInit.html
|
/// [`EVP_EncryptInit`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_EncryptInit.html
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct Cipher(*const ffi::EVP_CIPHER);
|
pub struct Cipher(*const ffi::EVP_CIPHER);
|
||||||
|
|
||||||
impl Cipher {
|
impl Cipher {
|
||||||
|
|
@ -107,6 +107,10 @@ impl Cipher {
|
||||||
unsafe { Cipher(ffi::EVP_aes_128_gcm()) }
|
unsafe { Cipher(ffi::EVP_aes_128_gcm()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn aes_128_ccm() -> Cipher {
|
||||||
|
unsafe { Cipher(ffi::EVP_aes_128_ccm()) }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn aes_256_ecb() -> Cipher {
|
pub fn aes_256_ecb() -> Cipher {
|
||||||
unsafe { Cipher(ffi::EVP_aes_256_ecb()) }
|
unsafe { Cipher(ffi::EVP_aes_256_ecb()) }
|
||||||
}
|
}
|
||||||
|
|
@ -139,6 +143,10 @@ impl Cipher {
|
||||||
unsafe { Cipher(ffi::EVP_aes_256_gcm()) }
|
unsafe { Cipher(ffi::EVP_aes_256_gcm()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn aes_256_ccm() -> Cipher {
|
||||||
|
unsafe { Cipher(ffi::EVP_aes_256_ccm()) }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bf_cbc() -> Cipher {
|
pub fn bf_cbc() -> Cipher {
|
||||||
unsafe { Cipher(ffi::EVP_bf_cbc()) }
|
unsafe { Cipher(ffi::EVP_bf_cbc()) }
|
||||||
}
|
}
|
||||||
|
|
@ -221,6 +229,12 @@ impl Cipher {
|
||||||
pub fn block_size(&self) -> usize {
|
pub fn block_size(&self) -> usize {
|
||||||
unsafe { EVP_CIPHER_block_size(self.0) as usize }
|
unsafe { EVP_CIPHER_block_size(self.0) as usize }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines whether the cipher is using CCM mode
|
||||||
|
fn is_ccm(&self) -> bool {
|
||||||
|
// NOTE: OpenSSL returns pointers to static structs, which makes this work as expected
|
||||||
|
*self == Cipher::aes_128_ccm() || *self == Cipher::aes_256_ccm()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Sync for Cipher {}
|
unsafe impl Sync for Cipher {}
|
||||||
|
|
@ -393,6 +407,41 @@ impl Crypter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the length of the authentication tag to generate in AES CCM.
|
||||||
|
///
|
||||||
|
/// When encrypting with AES CCM, the tag length needs to be explicitly set in order
|
||||||
|
/// to use a value different than the default 12 bytes.
|
||||||
|
pub fn set_tag_len(&mut self, tag_len: usize) -> 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,
|
||||||
|
ptr::null_mut(),
|
||||||
|
)).map(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Feeds total plaintext length to the cipher.
|
||||||
|
///
|
||||||
|
/// The total plaintext or ciphertext length MUST be passed to the cipher when it operates in
|
||||||
|
/// CCM mode.
|
||||||
|
pub fn set_data_len(&mut self, data_len: usize)-> Result<(), ErrorStack> {
|
||||||
|
unsafe {
|
||||||
|
assert!(data_len <= c_int::max_value() as usize);
|
||||||
|
let mut len = 0;
|
||||||
|
cvt(ffi::EVP_CipherUpdate(
|
||||||
|
self.ctx,
|
||||||
|
ptr::null_mut(),
|
||||||
|
&mut len,
|
||||||
|
ptr::null_mut(),
|
||||||
|
data_len as c_int,
|
||||||
|
)).map(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Feeds Additional Authenticated Data (AAD) through the cipher.
|
/// 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
|
/// This can only be used with AEAD ciphers such as AES GCM. Data fed in is not encrypted, but
|
||||||
|
|
@ -607,6 +656,12 @@ pub fn encrypt_aead(
|
||||||
) -> Result<Vec<u8>, ErrorStack> {
|
) -> Result<Vec<u8>, ErrorStack> {
|
||||||
let mut c = Crypter::new(t, Mode::Encrypt, key, iv)?;
|
let mut c = Crypter::new(t, Mode::Encrypt, key, iv)?;
|
||||||
let mut out = vec![0; data.len() + t.block_size()];
|
let mut out = vec![0; data.len() + t.block_size()];
|
||||||
|
|
||||||
|
if t.is_ccm() {
|
||||||
|
c.set_tag_len(tag.len())?;
|
||||||
|
c.set_data_len(data.len())?;
|
||||||
|
}
|
||||||
|
|
||||||
c.aad_update(aad)?;
|
c.aad_update(aad)?;
|
||||||
let count = c.update(data, &mut out)?;
|
let count = c.update(data, &mut out)?;
|
||||||
let rest = c.finalize(&mut out[count..])?;
|
let rest = c.finalize(&mut out[count..])?;
|
||||||
|
|
@ -629,10 +684,21 @@ pub fn decrypt_aead(
|
||||||
) -> Result<Vec<u8>, ErrorStack> {
|
) -> Result<Vec<u8>, ErrorStack> {
|
||||||
let mut c = Crypter::new(t, Mode::Decrypt, key, iv)?;
|
let mut c = Crypter::new(t, Mode::Decrypt, key, iv)?;
|
||||||
let mut out = vec![0; data.len() + t.block_size()];
|
let mut out = vec![0; data.len() + t.block_size()];
|
||||||
|
|
||||||
|
if t.is_ccm() {
|
||||||
|
c.set_tag(tag)?;
|
||||||
|
c.set_data_len(data.len())?;
|
||||||
|
}
|
||||||
|
|
||||||
c.aad_update(aad)?;
|
c.aad_update(aad)?;
|
||||||
let count = c.update(data, &mut out)?;
|
let count = c.update(data, &mut out)?;
|
||||||
c.set_tag(tag)?;
|
let mut rest = 0;
|
||||||
let rest = c.finalize(&mut out[count..])?;
|
|
||||||
|
if !t.is_ccm() {
|
||||||
|
c.set_tag(tag)?;
|
||||||
|
rest = c.finalize(&mut out[count..])?;
|
||||||
|
}
|
||||||
|
|
||||||
out.truncate(count + rest);
|
out.truncate(count + rest);
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
@ -1017,6 +1083,114 @@ mod tests {
|
||||||
assert_eq!(pt, hex::encode(out));
|
assert_eq!(pt, hex::encode(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_aes128_ccm() {
|
||||||
|
let key = "3ee186594f110fb788a8bf8aa8be5d4a";
|
||||||
|
let nonce = "44f705d52acf27b7f17196aa9b";
|
||||||
|
let aad = "2c16724296ff85e079627be3053ea95adf35722c21886baba343bd6c79b5cb57";
|
||||||
|
|
||||||
|
let pt = "d71864877f2578db092daba2d6a1f9f4698a9c356c7830a1";
|
||||||
|
let ct = "b4dd74e7a0cc51aea45dfb401a41d5822c96901a83247ea0";
|
||||||
|
let tag = "d6965f5aa6e31302a9cc2b36";
|
||||||
|
|
||||||
|
let mut actual_tag = [0; 12];
|
||||||
|
let out = encrypt_aead(
|
||||||
|
Cipher::aes_128_ccm(),
|
||||||
|
&Vec::from_hex(key).unwrap(),
|
||||||
|
Some(&Vec::from_hex(nonce).unwrap()),
|
||||||
|
&Vec::from_hex(aad).unwrap(),
|
||||||
|
&Vec::from_hex(pt).unwrap(),
|
||||||
|
&mut actual_tag,
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(ct, hex::encode(out));
|
||||||
|
assert_eq!(tag, hex::encode(actual_tag));
|
||||||
|
|
||||||
|
let out = decrypt_aead(
|
||||||
|
Cipher::aes_128_ccm(),
|
||||||
|
&Vec::from_hex(key).unwrap(),
|
||||||
|
Some(&Vec::from_hex(nonce).unwrap()),
|
||||||
|
&Vec::from_hex(aad).unwrap(),
|
||||||
|
&Vec::from_hex(ct).unwrap(),
|
||||||
|
&Vec::from_hex(tag).unwrap(),
|
||||||
|
).unwrap();
|
||||||
|
assert_eq!(pt, hex::encode(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_aes128_ccm_verify_fail() {
|
||||||
|
let key = "3ee186594f110fb788a8bf8aa8be5d4a";
|
||||||
|
let nonce = "44f705d52acf27b7f17196aa9b";
|
||||||
|
let aad = "2c16724296ff85e079627be3053ea95adf35722c21886baba343bd6c79b5cb57";
|
||||||
|
|
||||||
|
let ct = "b4dd74e7a0cc51aea45dfb401a41d5822c96901a83247ea0";
|
||||||
|
let tag = "00005f5aa6e31302a9cc2b36";
|
||||||
|
|
||||||
|
let out = decrypt_aead(
|
||||||
|
Cipher::aes_128_ccm(),
|
||||||
|
&Vec::from_hex(key).unwrap(),
|
||||||
|
Some(&Vec::from_hex(nonce).unwrap()),
|
||||||
|
&Vec::from_hex(aad).unwrap(),
|
||||||
|
&Vec::from_hex(ct).unwrap(),
|
||||||
|
&Vec::from_hex(tag).unwrap(),
|
||||||
|
);
|
||||||
|
assert!(out.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_aes256_ccm() {
|
||||||
|
let key = "7f4af6765cad1d511db07e33aaafd57646ec279db629048aa6770af24849aa0d";
|
||||||
|
let nonce = "dde2a362ce81b2b6913abc3095";
|
||||||
|
let aad = "404f5df97ece7431987bc098cce994fc3c063b519ffa47b0365226a0015ef695";
|
||||||
|
|
||||||
|
let pt = "7ebef26bf4ecf6f0ebb2eb860edbf900f27b75b4a6340fdb";
|
||||||
|
let ct = "353022db9c568bd7183a13c40b1ba30fcc768c54264aa2cd";
|
||||||
|
let tag = "2927a053c9244d3217a7ad05";
|
||||||
|
|
||||||
|
let mut actual_tag = [0; 12];
|
||||||
|
let out = encrypt_aead(
|
||||||
|
Cipher::aes_256_ccm(),
|
||||||
|
&Vec::from_hex(key).unwrap(),
|
||||||
|
Some(&Vec::from_hex(nonce).unwrap()),
|
||||||
|
&Vec::from_hex(aad).unwrap(),
|
||||||
|
&Vec::from_hex(pt).unwrap(),
|
||||||
|
&mut actual_tag,
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(ct, hex::encode(out));
|
||||||
|
assert_eq!(tag, hex::encode(actual_tag));
|
||||||
|
|
||||||
|
let out = decrypt_aead(
|
||||||
|
Cipher::aes_256_ccm(),
|
||||||
|
&Vec::from_hex(key).unwrap(),
|
||||||
|
Some(&Vec::from_hex(nonce).unwrap()),
|
||||||
|
&Vec::from_hex(aad).unwrap(),
|
||||||
|
&Vec::from_hex(ct).unwrap(),
|
||||||
|
&Vec::from_hex(tag).unwrap(),
|
||||||
|
).unwrap();
|
||||||
|
assert_eq!(pt, hex::encode(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_aes256_ccm_verify_fail() {
|
||||||
|
let key = "7f4af6765cad1d511db07e33aaafd57646ec279db629048aa6770af24849aa0d";
|
||||||
|
let nonce = "dde2a362ce81b2b6913abc3095";
|
||||||
|
let aad = "404f5df97ece7431987bc098cce994fc3c063b519ffa47b0365226a0015ef695";
|
||||||
|
|
||||||
|
let ct = "353022db9c568bd7183a13c40b1ba30fcc768c54264aa2cd";
|
||||||
|
let tag = "0000a053c9244d3217a7ad05";
|
||||||
|
|
||||||
|
let out = decrypt_aead(
|
||||||
|
Cipher::aes_256_ccm(),
|
||||||
|
&Vec::from_hex(key).unwrap(),
|
||||||
|
Some(&Vec::from_hex(nonce).unwrap()),
|
||||||
|
&Vec::from_hex(aad).unwrap(),
|
||||||
|
&Vec::from_hex(ct).unwrap(),
|
||||||
|
&Vec::from_hex(tag).unwrap(),
|
||||||
|
);
|
||||||
|
assert!(out.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(all(ossl110, feature = "v110"), all(ossl111, feature = "v111")))]
|
#[cfg(any(all(ossl110, feature = "v110"), all(ossl111, feature = "v111")))]
|
||||||
fn test_chacha20() {
|
fn test_chacha20() {
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,8 @@ fn main() {
|
||||||
.header("openssl/pkcs12.h")
|
.header("openssl/pkcs12.h")
|
||||||
.header("openssl/bn.h")
|
.header("openssl/bn.h")
|
||||||
.header("openssl/aes.h")
|
.header("openssl/aes.h")
|
||||||
.header("openssl/ocsp.h");
|
.header("openssl/ocsp.h")
|
||||||
|
.header("openssl/evp.h");
|
||||||
|
|
||||||
if !is_libressl {
|
if !is_libressl {
|
||||||
cfg.header("openssl/cms.h");
|
cfg.header("openssl/cms.h");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue