From 0c3c89755e4fbe6846984b2e975ee1bc059215b6 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 13 Apr 2020 19:08:20 +0200 Subject: openpgp: Keep aes_key_(un)wrap same across different backends --- openpgp/src/crypto/backend/nettle/ecdh.rs | 307 +------------------------ openpgp/src/crypto/backend/nettle/symmetric.rs | 74 ++++++ openpgp/src/crypto/ecdh.rs | 293 ++++++++++++++++++++++- 3 files changed, 367 insertions(+), 307 deletions(-) (limited to 'openpgp') diff --git a/openpgp/src/crypto/backend/nettle/ecdh.rs b/openpgp/src/crypto/backend/nettle/ecdh.rs index 400c66d1..c6bd5552 100644 --- a/openpgp/src/crypto/backend/nettle/ecdh.rs +++ b/openpgp/src/crypto/backend/nettle/ecdh.rs @@ -1,14 +1,13 @@ //! Elliptic Curve Diffie-Hellman. -use nettle::{cipher, curve25519, mode, mode::Mode, ecc, ecdh, random::Yarrow}; +use nettle::{curve25519, ecc, ecdh, random::Yarrow}; use crate::{Error, Result}; use crate::crypto::SessionKey; -use crate::crypto::ecdh::{kdf, pkcs5_pad, pkcs5_unpad}; +use crate::crypto::ecdh::{aes_key_wrap, aes_key_unwrap, kdf, pkcs5_pad, pkcs5_unpad}; use crate::crypto::mem::Protected; use crate::crypto::mpi::{MPI, PublicKey, SecretKeyMaterial, Ciphertext}; use crate::packet::{key, Key}; -use crate::utils::{write_be_u64, read_be_u64}; use crate::types::{Curve, HashAlgorithm, SymmetricAlgorithm, PublicKeyAlgorithm}; /// Wraps a session key using Elliptic Curve Diffie-Hellman. @@ -352,305 +351,3 @@ fn make_param(recipient: &Key, param } - -/// Wraps a key using the AES Key Wrap Algorithm. -/// -/// See [RFC 3394]. -/// -/// [RFC 3394]: https://tools.ietf.org/html/rfc3394 -pub fn aes_key_wrap(algo: SymmetricAlgorithm, key: &Protected, - plaintext: &Protected) - -> Result> { - use crate::SymmetricAlgorithm::*; - - if plaintext.len() % 8 != 0 { - return Err(Error::InvalidArgument( - "Plaintext must be a multiple of 8".into()).into()); - } - - if key.len() != algo.key_size()? { - return Err(Error::InvalidArgument("Bad key size".into()).into()); - } - - // We need ECB for the algorithm. However, there is no - // nettle::Mode:ECB, and we need nettle::Mode for polymorphism (we - // cannot have Box). To work around this, we use - // CBC, and always use an all-zero IV. - let mut cipher: Box = match algo { - AES128 => Box::new( - mode::Cbc::::with_encrypt_key(key)?), - AES192 => Box::new( - mode::Cbc::::with_encrypt_key(key)?), - AES256 => Box::new( - mode::Cbc::::with_encrypt_key(key)?), - _ => return Err(Error::UnsupportedSymmetricAlgorithm(algo).into()), - }; - - // Inputs: Plaintext, n 64-bit values {P1, P2, ..., Pn}, and - // Key, K (the KEK). - // Outputs: Ciphertext, (n+1) 64-bit values {C0, C1, ..., Cn}. - let n = plaintext.len() / 8; - let mut ciphertext = vec![0; 8 + plaintext.len()]; - - // 1) Initialize variables. - // - // Set A = IV, an initial value (see 2.2.3) - let mut a = AES_KEY_WRAP_IV; - - { - // For i = 1 to n - // R[i] = P[i] - let r = &mut ciphertext[8..]; - r.copy_from_slice(plaintext); - - let mut b = [0; 16]; - let mut tmp = [0; 16]; - let mut iv: Protected = vec![0; cipher.block_size()].into(); - - // 2) Calculate intermediate values. - - // For j = 0 to 5 - for j in 0..6 { - // For i=1 to n - for i in 0..n { - // B = AES(K, A | R[i]) - write_be_u64(&mut tmp[..8], a); - &mut tmp[8..].copy_from_slice(&r[8 * i..8 * (i + 1)]); - iv.iter_mut().for_each(|p| *p = 0); // Turn CBC into ECB. - cipher.encrypt(&mut iv, &mut b, &tmp)?; - - // A = MSB(64, B) ^ t where t = (n*j)+i - a = read_be_u64(&b[..8]) ^ ((n * j) + i + 1) as u64; - // (Note that our i runs from 0 to n-1 instead of 1 to - // n, hence the index shift. - - // R[i] = LSB(64, B) - &mut r[8 * i..8 * (i + 1)].copy_from_slice(&b[8..]); - } - } - } - - // 3) Output the results. - // - // Set C[0] = A - // For i = 1 to n - // C[i] = R[i] - write_be_u64(&mut ciphertext[..8], a); - Ok(ciphertext) -} - -/// Unwraps an encrypted key using the AES Key Wrap Algorithm. -/// -/// See [RFC 3394]. -/// -/// [RFC 3394]: https://tools.ietf.org/html/rfc3394 -pub fn aes_key_unwrap(algo: SymmetricAlgorithm, key: &Protected, - ciphertext: &[u8]) - -> Result { - use crate::SymmetricAlgorithm::*; - - if ciphertext.len() % 8 != 0 { - return Err(Error::InvalidArgument( - "Ciphertext must be a multiple of 8".into()).into()); - } - - if key.len() != algo.key_size()? { - return Err(Error::InvalidArgument("Bad key size".into()).into()); - } - - // We need ECB for the algorithm. However, there is no - // nettle::Mode:ECB, and we need nettle::Mode for polymorphism (we - // cannot have Box). To work around this, we use - // CBC, and always use an all-zero IV. - let mut cipher: Box = match algo { - AES128 => Box::new( - mode::Cbc::::with_decrypt_key(key)?), - AES192 => Box::new( - mode::Cbc::::with_decrypt_key(key)?), - AES256 => Box::new( - mode::Cbc::::with_decrypt_key(key)?), - _ => return Err(Error::UnsupportedSymmetricAlgorithm(algo).into()), - }; - - // Inputs: Ciphertext, (n+1) 64-bit values {C0, C1, ..., Cn}, and - // Key, K (the KEK). - // Outputs: Plaintext, n 64-bit values {P1, P2, ..., Pn}. - let n = ciphertext.len() / 8 - 1; - let mut plaintext = Vec::with_capacity(ciphertext.len() - 8); - - // 1) Initialize variables. - // - // Set A = C[0] - // For i = 1 to n - // R[i] = C[i] - let mut a = read_be_u64(&ciphertext[..8]); - plaintext.extend_from_slice(&ciphertext[8..]); - let mut plaintext: Protected = plaintext.into(); - - // 2) Calculate intermediate values. - { - let r = &mut plaintext; - - let mut b = [0; 16]; - let mut tmp = [0; 16]; - let mut iv: Protected = vec![0; cipher.block_size()].into(); - - // For j = 5 to 0 - for j in (0..=5).rev() { - // For i = n to 1 - for i in (0..=n-1).rev() { - // B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i - write_be_u64(&mut tmp[..8], a ^ ((n * j) + i + 1) as u64); - &mut tmp[8..].copy_from_slice(&r[8 * i..8 * (i + 1)]); - // (Note that our i runs from n-1 to 0 instead of n to - // 1, hence the index shift. - iv.iter_mut().for_each(|p| *p = 0); // Turn CBC into ECB. - cipher.decrypt(&mut iv, &mut b, &tmp)?; - - // A = MSB(64, B) - a = read_be_u64(&b[..8]); - - // R[i] = LSB(64, B) - &mut r[8 * i..8 * (i + 1)].copy_from_slice(&b[8..]); - } - } - } - - // 3) Output results. - // - // If A is an appropriate initial value (see 2.2.3), - // Then - // For i = 1 to n - // P[i] = R[i] - // Else - // Return an error - if a == AES_KEY_WRAP_IV { - Ok(plaintext) - } else { - Err(Error::InvalidArgument("Bad key".into()).into()) - } -} - -const AES_KEY_WRAP_IV: u64 = 0xa6a6a6a6a6a6a6a6; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn pkcs5_padding() { - let v = pkcs5_pad(vec![0, 0, 0].into(), 8); - assert_eq!(&v, &Protected::from(&[0, 0, 0, 5, 5, 5, 5, 5][..])); - let v = pkcs5_unpad(v, 3).unwrap(); - assert_eq!(&v, &Protected::from(&[0, 0, 0][..])); - - let v = pkcs5_pad(vec![].into(), 8); - assert_eq!(&v, &Protected::from(&[8, 8, 8, 8, 8, 8, 8, 8][..])); - let v = pkcs5_unpad(v, 0).unwrap(); - assert_eq!(&v, &Protected::from(&[][..])); - } - - #[test] - fn aes_wrapping() { - struct Test { - algo: SymmetricAlgorithm, - kek: &'static [u8], - key_data: &'static [u8], - ciphertext: &'static [u8], - } - - // These are the test vectors from RFC3394. - const TESTS: &[Test] = &[ - Test { - algo: SymmetricAlgorithm::AES128, - kek: &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F], - key_data: &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF], - ciphertext: &[0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47, - 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, - 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5], - }, - Test { - algo: SymmetricAlgorithm::AES192, - kek: &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17], - key_data: &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF], - ciphertext: &[0x96, 0x77, 0x8B, 0x25, 0xAE, 0x6C, 0xA4, 0x35, - 0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2, - 0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D], - }, - Test { - algo: SymmetricAlgorithm::AES256, - kek: &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F], - key_data: &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF], - ciphertext: &[0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2, - 0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A, - 0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7], - }, - Test { - algo: SymmetricAlgorithm::AES192, - kek: &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17], - key_data: &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07], - ciphertext: &[0x03, 0x1D, 0x33, 0x26, 0x4E, 0x15, 0xD3, 0x32, - 0x68, 0xF2, 0x4E, 0xC2, 0x60, 0x74, 0x3E, 0xDC, - 0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93, - 0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2], - }, - Test { - algo: SymmetricAlgorithm::AES256, - kek: &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F], - key_data: &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07], - ciphertext: &[0xA8, 0xF9, 0xBC, 0x16, 0x12, 0xC6, 0x8B, 0x3F, - 0xF6, 0xE6, 0xF4, 0xFB, 0xE3, 0x0E, 0x71, 0xE4, - 0x76, 0x9C, 0x8B, 0x80, 0xA3, 0x2C, 0xB8, 0x95, - 0x8C, 0xD5, 0xD1, 0x7D, 0x6B, 0x25, 0x4D, 0xA1], - }, - Test { - algo: SymmetricAlgorithm::AES256, - kek: &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F], - key_data: &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F], - ciphertext: &[0x28, 0xC9, 0xF4, 0x04, 0xC4, 0xB8, 0x10, 0xF4, - 0xCB, 0xCC, 0xB3, 0x5C, 0xFB, 0x87, 0xF8, 0x26, - 0x3F, 0x57, 0x86, 0xE2, 0xD8, 0x0E, 0xD3, 0x26, - 0xCB, 0xC7, 0xF0, 0xE7, 0x1A, 0x99, 0xF4, 0x3B, - 0xFB, 0x98, 0x8B, 0x9B, 0x7A, 0x02, 0xDD, 0x21], - }, - ]; - - for test in TESTS { - let ciphertext = aes_key_wrap(test.algo, - &test.kek.into(), - &test.key_data.into()) - .unwrap(); - assert_eq!(test.ciphertext, &ciphertext[..]); - - let key_data = aes_key_unwrap(test.algo, - &test.kek.into(), - &ciphertext[..]) - .unwrap(); - assert_eq!(&Protected::from(test.key_data), &key_data); - } - } -} diff --git a/openpgp/src/crypto/backend/nettle/symmetric.rs b/openpgp/src/crypto/backend/nettle/symmetric.rs index 2faaaa03..9611701d 100644 --- a/openpgp/src/crypto/backend/nettle/symmetric.rs +++ b/openpgp/src/crypto/backend/nettle/symmetric.rs @@ -143,4 +143,78 @@ impl SymmetricAlgorithm { _ => Err(Error::UnsupportedSymmetricAlgorithm(self).into()) } } + + /// Creates a Nettle context for encrypting in CBC mode. + pub(crate) fn make_encrypt_cbc(self, key: &[u8]) -> Result> { + match self { + SymmetricAlgorithm::TripleDES => + Ok(Box::new( + mode::Cbc::::with_encrypt_key(&key[..])?)), + SymmetricAlgorithm::CAST5 => + Ok(Box::new( + mode::Cbc::::with_encrypt_key(&key[..])?)), + SymmetricAlgorithm::Blowfish => + Ok(Box::new( + mode::Cbc::::with_encrypt_key(&key[..])?)), + SymmetricAlgorithm::AES128 => + Ok(Box::new( + mode::Cbc::::with_encrypt_key(&key[..])?)), + SymmetricAlgorithm::AES192 => + Ok(Box::new( + mode::Cbc::::with_encrypt_key(&key[..])?)), + SymmetricAlgorithm::AES256 => + Ok(Box::new( + mode::Cbc::::with_encrypt_key(&key[..])?)), + SymmetricAlgorithm::Twofish => + Ok(Box::new( + mode::Cbc::::with_encrypt_key(&key[..])?)), + SymmetricAlgorithm::Camellia128 => + Ok(Box::new( + mode::Cbc::::with_encrypt_key(&key[..])?)), + SymmetricAlgorithm::Camellia192 => + Ok(Box::new( + mode::Cbc::::with_encrypt_key(&key[..])?)), + SymmetricAlgorithm::Camellia256 => + Ok(Box::new( + mode::Cbc::::with_encrypt_key(&key[..])?)), + _ => Err(Error::UnsupportedSymmetricAlgorithm(self).into()), + } + } + + /// Creates a Nettle context for decrypting in CBC mode. + pub(crate) fn make_decrypt_cbc(self, key: &[u8]) -> Result> { + match self { + SymmetricAlgorithm::TripleDES => + Ok(Box::new( + mode::Cbc::::with_decrypt_key(&key[..])?)), + SymmetricAlgorithm::CAST5 => + Ok(Box::new( + mode::Cbc::::with_decrypt_key(&key[..])?)), + SymmetricAlgorithm::Blowfish => + Ok(Box::new( + mode::Cbc::::with_decrypt_key(&key[..])?)), + SymmetricAlgorithm::AES128 => + Ok(Box::new( + mode::Cbc::::with_decrypt_key(&key[..])?)), + SymmetricAlgorithm::AES192 => + Ok(Box::new( + mode::Cbc::::with_decrypt_key(&key[..])?)), + SymmetricAlgorithm::AES256 => + Ok(Box::new( + mode::Cbc::::with_decrypt_key(&key[..])?)), + SymmetricAlgorithm::Twofish => + Ok(Box::new( + mode::Cbc::::with_decrypt_key(&key[..])?)), + SymmetricAlgorithm::Camellia128 => + Ok(Box::new( + mode::Cbc::::with_decrypt_key(&key[..])?)), + SymmetricAlgorithm::Camellia192 => + Ok(Box::new( + mode::Cbc::::with_decrypt_key(&key[..])?)), + SymmetricAlgorithm::Camellia256 => + Ok(Box::new( + mode::Cbc::::with_decrypt_key(&key[..])?)), + _ => Err(Error::UnsupportedSymmetricAlgorithm(self).into()), + } + } } \ No newline at end of file diff --git a/openpgp/src/crypto/ecdh.rs b/openpgp/src/crypto/ecdh.rs index d88510a3..399a670d 100644 --- a/openpgp/src/crypto/ecdh.rs +++ b/openpgp/src/crypto/ecdh.rs @@ -4,11 +4,11 @@ use crate::vec_truncate; use crate::{Error, Result}; use crate::crypto::mem::Protected; -use crate::types::HashAlgorithm; +use crate::types::{SymmetricAlgorithm, HashAlgorithm}; +use crate::utils::{read_be_u64, write_be_u64}; pub use crate::crypto::backend::ecdh::{encrypt, decrypt}; pub use crate::crypto::backend::ecdh::{encrypt_shared, decrypt_shared}; -pub use crate::crypto::backend::ecdh::{aes_key_wrap, aes_key_unwrap}; /// Derives a secret key for session key wrapping. /// @@ -83,3 +83,292 @@ pub fn pkcs5_unpad(sk: Protected, target_len: usize) -> Result { Err(Error::InvalidArgument("bad padding".into()).into()) } } + + +/// Wraps a key using the AES Key Wrap Algorithm. +/// +/// See [RFC 3394]. +/// +/// [RFC 3394]: https://tools.ietf.org/html/rfc3394 +pub fn aes_key_wrap(algo: SymmetricAlgorithm, key: &Protected, + plaintext: &Protected) + -> Result> { + use crate::SymmetricAlgorithm::*; + + if plaintext.len() % 8 != 0 { + return Err(Error::InvalidArgument( + "Plaintext must be a multiple of 8".into()).into()); + } + + if key.len() != algo.key_size()? { + return Err(Error::InvalidArgument("Bad key size".into()).into()); + } + + // We need ECB for the algorithm. However, there is no nettle::Mode:ECB, + // so to work around this, we use CBC, and always use an all-zero IV. + let mut cipher = match algo { + AES128 | AES192 | AES256 => algo.make_encrypt_cbc(key)?, + _ => return Err(Error::UnsupportedSymmetricAlgorithm(algo).into()), + }; + + // Inputs: Plaintext, n 64-bit values {P1, P2, ..., Pn}, and + // Key, K (the KEK). + // Outputs: Ciphertext, (n+1) 64-bit values {C0, C1, ..., Cn}. + let n = plaintext.len() / 8; + let mut ciphertext = vec![0; 8 + plaintext.len()]; + + // 1) Initialize variables. + // + // Set A = IV, an initial value (see 2.2.3) + let mut a = AES_KEY_WRAP_IV; + + { + // For i = 1 to n + // R[i] = P[i] + let r = &mut ciphertext[8..]; + r.copy_from_slice(plaintext); + + let mut b = [0; 16]; + let mut tmp = [0; 16]; + let mut iv: Protected = vec![0; cipher.block_size()].into(); + + // 2) Calculate intermediate values. + + // For j = 0 to 5 + for j in 0..6 { + // For i=1 to n + for i in 0..n { + // B = AES(K, A | R[i]) + write_be_u64(&mut tmp[..8], a); + &mut tmp[8..].copy_from_slice(&r[8 * i..8 * (i + 1)]); + iv.iter_mut().for_each(|p| *p = 0); // Turn CBC into ECB. + cipher.encrypt(&mut iv, &mut b, &tmp)?; + + // A = MSB(64, B) ^ t where t = (n*j)+i + a = read_be_u64(&b[..8]) ^ ((n * j) + i + 1) as u64; + // (Note that our i runs from 0 to n-1 instead of 1 to + // n, hence the index shift. + + // R[i] = LSB(64, B) + &mut r[8 * i..8 * (i + 1)].copy_from_slice(&b[8..]); + } + } + } + + // 3) Output the results. + // + // Set C[0] = A + // For i = 1 to n + // C[i] = R[i] + write_be_u64(&mut ciphertext[..8], a); + Ok(ciphertext) +} + +/// Unwraps an encrypted key using the AES Key Wrap Algorithm. +/// +/// See [RFC 3394]. +/// +/// [RFC 3394]: https://tools.ietf.org/html/rfc3394 +pub fn aes_key_unwrap(algo: SymmetricAlgorithm, key: &Protected, + ciphertext: &[u8]) + -> Result { + use crate::SymmetricAlgorithm::*; + + if ciphertext.len() % 8 != 0 { + return Err(Error::InvalidArgument( + "Ciphertext must be a multiple of 8".into()).into()); + } + + if key.len() != algo.key_size()? { + return Err(Error::InvalidArgument("Bad key size".into()).into()); + } + + // We need ECB for the algorithm. However, there is no nettle::Mode:ECB, + // so to work around this, we use CBC, and always use an all-zero IV. + let mut cipher = match algo { + AES128 | AES192 | AES256 => algo.make_decrypt_cbc(key)?, + _ => return Err(Error::UnsupportedSymmetricAlgorithm(algo).into()), + }; + + // Inputs: Ciphertext, (n+1) 64-bit values {C0, C1, ..., Cn}, and + // Key, K (the KEK). + // Outputs: Plaintext, n 64-bit values {P1, P2, ..., Pn}. + let n = ciphertext.len() / 8 - 1; + let mut plaintext = Vec::with_capacity(ciphertext.len() - 8); + + // 1) Initialize variables. + // + // Set A = C[0] + // For i = 1 to n + // R[i] = C[i] + let mut a = read_be_u64(&ciphertext[..8]); + plaintext.extend_from_slice(&ciphertext[8..]); + let mut plaintext: Protected = plaintext.into(); + + // 2) Calculate intermediate values. + { + let r = &mut plaintext; + + let mut b = [0; 16]; + let mut tmp = [0; 16]; + let mut iv: Protected = vec![0; cipher.block_size()].into(); + + // For j = 5 to 0 + for j in (0..=5).rev() { + // For i = n to 1 + for i in (0..=n-1).rev() { + // B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i + write_be_u64(&mut tmp[..8], a ^ ((n * j) + i + 1) as u64); + &mut tmp[8..].copy_from_slice(&r[8 * i..8 * (i + 1)]); + // (Note that our i runs from n-1 to 0 instead of n to + // 1, hence the index shift. + iv.iter_mut().for_each(|p| *p = 0); // Turn CBC into ECB. + cipher.decrypt(&mut iv, &mut b, &tmp)?; + + // A = MSB(64, B) + a = read_be_u64(&b[..8]); + + // R[i] = LSB(64, B) + &mut r[8 * i..8 * (i + 1)].copy_from_slice(&b[8..]); + } + } + } + + // 3) Output results. + // + // If A is an appropriate initial value (see 2.2.3), + // Then + // For i = 1 to n + // P[i] = R[i] + // Else + // Return an error + if a == AES_KEY_WRAP_IV { + Ok(plaintext) + } else { + Err(Error::InvalidArgument("Bad key".into()).into()) + } +} + +const AES_KEY_WRAP_IV: u64 = 0xa6a6a6a6a6a6a6a6; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn pkcs5_padding() { + let v = pkcs5_pad(vec![0, 0, 0].into(), 8); + assert_eq!(&v, &Protected::from(&[0, 0, 0, 5, 5, 5, 5, 5][..])); + let v = pkcs5_unpad(v, 3).unwrap(); + assert_eq!(&v, &Protected::from(&[0, 0, 0][..])); + + let v = pkcs5_pad(vec![].into(), 8); + assert_eq!(&v, &Protected::from(&[8, 8, 8, 8, 8, 8, 8, 8][..])); + let v = pkcs5_unpad(v, 0).unwrap(); + assert_eq!(&v, &Protected::from(&[][..])); + } + + #[test] + fn aes_wrapping() { + struct Test { + algo: SymmetricAlgorithm, + kek: &'static [u8], + key_data: &'static [u8], + ciphertext: &'static [u8], + } + + // These are the test vectors from RFC3394. + const TESTS: &[Test] = &[ + Test { + algo: SymmetricAlgorithm::AES128, + kek: &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F], + key_data: &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF], + ciphertext: &[0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47, + 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, + 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5], + }, + Test { + algo: SymmetricAlgorithm::AES192, + kek: &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17], + key_data: &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF], + ciphertext: &[0x96, 0x77, 0x8B, 0x25, 0xAE, 0x6C, 0xA4, 0x35, + 0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2, + 0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D], + }, + Test { + algo: SymmetricAlgorithm::AES256, + kek: &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F], + key_data: &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF], + ciphertext: &[0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2, + 0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A, + 0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7], + }, + Test { + algo: SymmetricAlgorithm::AES192, + kek: &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17], + key_data: &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07], + ciphertext: &[0x03, 0x1D, 0x33, 0x26, 0x4E, 0x15, 0xD3, 0x32, + 0x68, 0xF2, 0x4E, 0xC2, 0x60, 0x74, 0x3E, 0xDC, + 0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93, + 0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2], + }, + Test { + algo: SymmetricAlgorithm::AES256, + kek: &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F], + key_data: &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07], + ciphertext: &[0xA8, 0xF9, 0xBC, 0x16, 0x12, 0xC6, 0x8B, 0x3F, + 0xF6, 0xE6, 0xF4, 0xFB, 0xE3, 0x0E, 0x71, 0xE4, + 0x76, 0x9C, 0x8B, 0x80, 0xA3, 0x2C, 0xB8, 0x95, + 0x8C, 0xD5, 0xD1, 0x7D, 0x6B, 0x25, 0x4D, 0xA1], + }, + Test { + algo: SymmetricAlgorithm::AES256, + kek: &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F], + key_data: &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F], + ciphertext: &[0x28, 0xC9, 0xF4, 0x04, 0xC4, 0xB8, 0x10, 0xF4, + 0xCB, 0xCC, 0xB3, 0x5C, 0xFB, 0x87, 0xF8, 0x26, + 0x3F, 0x57, 0x86, 0xE2, 0xD8, 0x0E, 0xD3, 0x26, + 0xCB, 0xC7, 0xF0, 0xE7, 0x1A, 0x99, 0xF4, 0x3B, + 0xFB, 0x98, 0x8B, 0x9B, 0x7A, 0x02, 0xDD, 0x21], + }, + ]; + + for test in TESTS { + let ciphertext = aes_key_wrap(test.algo, + &test.kek.into(), + &test.key_data.into()) + .unwrap(); + assert_eq!(test.ciphertext, &ciphertext[..]); + + let key_data = aes_key_unwrap(test.algo, + &test.kek.into(), + &ciphertext[..]) + .unwrap(); + assert_eq!(&Protected::from(test.key_data), &key_data); + } + } +} -- cgit v1.2.3