diff --git a/openssl-sys/src/aes.rs b/openssl-sys/src/aes.rs index cbb289c6..a03a9e78 100644 --- a/openssl-sys/src/aes.rs +++ b/openssl-sys/src/aes.rs @@ -25,4 +25,20 @@ extern "C" { ivec: *mut c_uchar, enc: c_int, ); + + pub fn AES_wrap_key( + key: *mut AES_KEY, + iv: *const c_uchar, + out: *mut c_uchar, + in_: *const c_uchar, + inlen: c_uint, + ) -> c_int; + + pub fn AES_unwrap_key( + key: *mut AES_KEY, + iv: *const c_uchar, + out: *mut c_uchar, + in_: *const c_uchar, + inlen: c_uint, + ) -> c_int; } diff --git a/openssl/src/aes.rs b/openssl/src/aes.rs index de45d2ca..549b3219 100644 --- a/openssl/src/aes.rs +++ b/openssl/src/aes.rs @@ -1,7 +1,7 @@ -//! Low level AES IGE functionality +//! Low level AES IGE and key wrapping functionality //! //! AES ECB, CBC, XTS, CTR, CFB, GCM and other conventional symmetric encryption -//! modes are found in [`symm`]. This is the implementation of AES IGE. +//! modes are found in [`symm`]. This is the implementation of AES IGE and key wrapping //! //! Advanced Encryption Standard (AES) provides symmetric key cipher that //! the same key is used to encrypt and decrypt data. This implementation @@ -22,6 +22,7 @@ //! //! # Examples //! +//! ## AES IGE //! ```rust //! use openssl::aes::{AesKey, aes_ige}; //! use openssl::symm::Mode; @@ -35,10 +36,31 @@ //! let mut output = [0u8; 16]; //! aes_ige(plaintext, &mut output, &key, &mut iv, Mode::Encrypt); //! assert_eq!(output, *b"\xa6\xad\x97\x4d\x5c\xea\x1d\x36\xd2\xf3\x67\x98\x09\x07\xed\x32"); +//! ``` +//! +//! ## Key wrapping +//! ```rust +//! use openssl::aes::{AesKey, unwrap_key, wrap_key}; +//! +//! let kek = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +//! let key_to_wrap = b"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"; +//! +//! let enc_key = AesKey::new_encrypt(kek).unwrap(); +//! let mut ciphertext = [0u8; 24]; +//! wrap_key(&enc_key, None, &mut ciphertext, &key_to_wrap[..]).unwrap(); +//! let dec_key = AesKey::new_decrypt(kek).unwrap(); +//! let mut orig_key = [0u8; 16]; +//! unwrap_key(&dec_key, None, &mut orig_key, &ciphertext[..]).unwrap(); +//! +//! assert_eq!(&orig_key[..], &key_to_wrap[..]); +//! ``` +//! use ffi; -use libc::c_int; +use libc::{c_int, c_uint}; use std::mem; +use cvt; +use error::ErrorStack; use symm::Mode; /// Provides Error handling for parsing keys. @@ -136,6 +158,70 @@ pub fn aes_ige(in_: &[u8], out: &mut [u8], key: &AesKey, iv: &mut [u8], mode: Mo } } +/// Wrap a key, according to [RFC 3394](https://tools.ietf.org/html/rfc3394) +/// +/// * `key`: The key-encrypting-key to use. Must be a encrypting key +/// * `iv`: The IV to use. You must use the same IV for both wrapping and unwrapping +/// * `out`: The output buffer to store the ciphertext +/// * `in_`: The input buffer, storing the key to be wrapped +/// +/// # Panics +/// +/// Panics if either `out` or `in_` do not have sizes that are a multiple of 8, or if +/// `out` is not 8 bytes longer than `in_`. +pub fn wrap_key( + key: &AesKey, + iv: Option<[u8; 8]>, + out: &mut [u8], + in_: &[u8], +) -> Result { + unsafe { + assert!(out.len() == in_.len() + 8); // Ciphertext is 64 bits longer (see 2.2.1) + assert!(in_.len() % 8 == 0); // Input (and hence output, given above) are integer multiples of 64 bit values + + cvt(ffi::AES_wrap_key( + &key.0 as *const _ as *mut _, // this is safe, the implementation only uses the key as a const pointer. + iv.map_or(std::ptr::null(), |iv| iv.as_ptr() as *const _), + out.as_ptr() as *mut _, + in_.as_ptr() as *const _, + in_.len() as c_uint, + )) + .map(|written| written as usize) // convert is safe, cvt only return ok with positive numbers + } +} + +/// Unwrap a key, according to [RFC 3394](https://tools.ietf.org/html/rfc3394) +/// +/// * `key`: The key-encrypting-key to decrypt the wrapped key. Must be a decrypting key +/// * `iv`: The same IV used for wrapping the key +/// * `out`: The buffer to write the unwrapped key to +/// * `in_`: The input ciphertext +/// +/// # Panics +/// +/// Panics if either `out` or `in_` do not have sizes that are a multiple of 8, or +/// if `in` is not 8 bytes longer than `in_`. +pub fn unwrap_key( + key: &AesKey, + iv: Option<[u8; 8]>, + out: &mut [u8], + in_: &[u8], +) -> Result { + unsafe { + assert!(in_.len() == out.len() + 8); + assert!(in_.len() % 8 == 0); + + cvt(ffi::AES_unwrap_key( + &key.0 as *const _ as *mut _, // this is safe, the implementation only uses the key as a const pointer. + iv.map_or(std::ptr::null(), |iv| iv.as_ptr() as *const _), + out.as_ptr() as *mut _, + in_.as_ptr() as *const _, + in_.len() as c_uint, + )) + .map(|written| written as usize) + } +} + #[cfg(test)] mod test { use hex::FromHex; @@ -166,4 +252,30 @@ mod test { aes_ige(&ct, &mut pt_actual, &key, &mut iv, Mode::Decrypt); assert_eq!(pt_actual, pt); } + + // from the RFC https://tools.ietf.org/html/rfc3394#section-2.2.3 + #[test] + fn test_wrap_unwrap() { + let raw_key = Vec::from_hex("000102030405060708090A0B0C0D0E0F").unwrap(); + let key_data = Vec::from_hex("00112233445566778899AABBCCDDEEFF").unwrap(); + let expected_ciphertext = + Vec::from_hex("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5").unwrap(); + + let enc_key = AesKey::new_encrypt(&raw_key).unwrap(); + let mut wrapped = [0; 24]; + assert_eq!( + wrap_key(&enc_key, None, &mut wrapped, &key_data).unwrap(), + 24 + ); + assert_eq!(&wrapped[..], &expected_ciphertext[..]); + + let dec_key = AesKey::new_decrypt(&raw_key).unwrap(); + let mut unwrapped = [0; 16]; + assert_eq!( + unwrap_key(&dec_key, None, &mut unwrapped, &wrapped).unwrap(), + 16 + ); + assert_eq!(&unwrapped[..], &key_data[..]); + } + }