From b9341856b19f3cb4bf79febc25077a364cc36fc7 Mon Sep 17 00:00:00 2001 From: Russell Greene Date: Wed, 5 Jun 2019 21:03:34 -0600 Subject: [PATCH 1/2] Add AES_wrap_key and AES_unwrap_key functionality --- openssl-sys/src/aes.rs | 16 ++++++ openssl/src/aes.rs | 118 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 131 insertions(+), 3 deletions(-) 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[..]); + } + } From 1c3f5b5f57dec81d169d3c349ffae6243d147e0d Mon Sep 17 00:00:00 2001 From: Russell Greene Date: Fri, 7 Jun 2019 19:32:36 -0600 Subject: [PATCH 2/2] Address comments --- openssl/src/aes.rs | 49 +++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/openssl/src/aes.rs b/openssl/src/aes.rs index 549b3219..879002ab 100644 --- a/openssl/src/aes.rs +++ b/openssl/src/aes.rs @@ -57,10 +57,8 @@ //! use ffi; use libc::{c_int, c_uint}; -use std::mem; +use std::{mem, ptr}; -use cvt; -use error::ErrorStack; use symm::Mode; /// Provides Error handling for parsing keys. @@ -165,28 +163,33 @@ pub fn aes_ige(in_: &[u8], out: &mut [u8], key: &AesKey, iv: &mut [u8], mode: Mo /// * `out`: The output buffer to store the ciphertext /// * `in_`: The input buffer, storing the key to be wrapped /// +/// Returns the number of bytes written into `out` +/// /// # 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_`. +/// `out` is not 8 bytes longer than `in_` pub fn wrap_key( key: &AesKey, iv: Option<[u8; 8]>, out: &mut [u8], in_: &[u8], -) -> Result { +) -> 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( + assert!(out.len() >= in_.len() + 8); // Ciphertext is 64 bits longer (see 2.2.1) + + let written = 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 _), + iv.as_ref().map_or(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 + ); + if written <= 0 { + Err(KeyError(())) + } else { + Ok(written as usize) + } } } @@ -197,28 +200,34 @@ pub fn wrap_key( /// * `out`: The buffer to write the unwrapped key to /// * `in_`: The input ciphertext /// +/// Returns the number of bytes written into `out` +/// /// # 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_`. +/// 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 { +) -> Result { unsafe { - assert!(in_.len() == out.len() + 8); - assert!(in_.len() % 8 == 0); + assert!(out.len() + 8 <= in_.len()); - cvt(ffi::AES_unwrap_key( + let written = 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 _), + iv.as_ref().map_or(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) + ); + + if written <= 0 { + Err(KeyError(())) + } else { + Ok(written as usize) + } } }