From 4ca8b30044b99f71056ac67d25a34c36bcafdd51 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 5 Aug 2021 16:52:22 -0400 Subject: [PATCH] Implement AES-GMAC-SIV in terms of OpenSSL too. --- aes-gmac-siv/Cargo.lock | 61 +++++++++++ aes-gmac-siv/Cargo.toml | 9 +- aes-gmac-siv/src/impl_openssl.rs | 168 +++++++++++++++++++++++++++++++ aes-gmac-siv/src/lib.rs | 12 ++- network-hypervisor/Cargo.lock | 61 +++++++++++ 5 files changed, 303 insertions(+), 8 deletions(-) create mode 100644 aes-gmac-siv/src/impl_openssl.rs diff --git a/aes-gmac-siv/Cargo.lock b/aes-gmac-siv/Cargo.lock index 4401d32e5..efd14360e 100644 --- a/aes-gmac-siv/Cargo.lock +++ b/aes-gmac-siv/Cargo.lock @@ -7,9 +7,16 @@ name = "aes-gmac-siv" version = "0.1.0" dependencies = [ "gcrypt", + "openssl", "sha2", ] +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "bitflags" version = "1.2.1" @@ -71,6 +78,21 @@ dependencies = [ "generic-array", ] +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "gcrypt" version = "0.7.0" @@ -150,6 +172,39 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549430950c79ae24e6d02e0b7404534ecf311d94cc9f861e9e4020187d13d885" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + +[[package]] +name = "openssl-sys" +version = "0.9.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a7907e3bfa08bb85105209cdfcb6c63d109f8f6c1ed6ca318fff5c1853fbc1d" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + [[package]] name = "sha2" version = "0.9.5" @@ -169,6 +224,12 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.3" diff --git a/aes-gmac-siv/Cargo.toml b/aes-gmac-siv/Cargo.toml index 64430ce1d..0ab6689f2 100644 --- a/aes-gmac-siv/Cargo.toml +++ b/aes-gmac-siv/Cargo.toml @@ -3,18 +3,15 @@ name = "aes-gmac-siv" version = "0.1.0" edition = "2018" -[profile.test] -opt-level = 3 -lto = true -codegen-units = 1 -panic = 'abort' - [profile.release] opt-level = 3 lto = true codegen-units = 1 panic = 'abort' +[dependencies] +openssl = "^0" + [target."cfg(not(any(target_os = \"macos\", target_os = \"ios\")))".dependencies] gcrypt = "^0" diff --git a/aes-gmac-siv/src/impl_openssl.rs b/aes-gmac-siv/src/impl_openssl.rs new file mode 100644 index 000000000..f1eac956b --- /dev/null +++ b/aes-gmac-siv/src/impl_openssl.rs @@ -0,0 +1,168 @@ +// AES-GMAC-SIV implemented using OpenSSL. + +use std::convert::TryInto; +use openssl::symm::{Crypter, Cipher, Mode}; + +/// AES-GMAC-SIV encryptor/decryptor. +#[repr(align(8))] // allow tag and tmp to be accessed as u64 arrays as well +pub struct AesGmacSiv { + tag: [u8; 16], + tmp: [u8; 16], + k0: [u8; 32], + k1: [u8; 32], + ctr: Option, + gmac: Option, +} + +impl AesGmacSiv { + /// Create a new keyed instance of AES-GMAC-SIV + /// The key may be of size 16, 24, or 32 bytes (128, 192, or 256 bits). Any other size will panic. + #[inline(always)] + pub fn new(k0: &[u8], k1: &[u8]) -> Self { + if k0.len() != 32 && k0.len() != 24 && k0.len() != 16 { + panic!("AES supports 128, 192, or 256 bits keys"); + } + if k1.len() != k0.len() { + panic!("k0 and k1 must be of the same size"); + } + AesGmacSiv { + tag: [0_u8; 16], + tmp: [0_u8; 16], + k0: k0.try_into().unwrap(), + k1: k1.try_into().unwrap(), + ctr: None, + gmac: None, + } + } + + /// Reset to prepare for another encrypt or decrypt operation. + #[inline(always)] + pub fn reset(&mut self) { + let _ = self.ctr.take(); + let _ = self.gmac.take(); + } + + /// Initialize for encryption. + #[inline(always)] + pub fn encrypt_init(&mut self, iv: &[u8]) { + self.tag[0..8].copy_from_slice(iv); + self.tag[8..16].fill(0); + let _ = self.gmac.replace(Crypter::new(Cipher::aes_256_gcm(), Mode::Encrypt, &self.k0, Some(&self.tag)).unwrap()); + } + + /// Set additional authenticated data (data to be authenticated but not encrypted). + /// This can currently only be called once. Multiple calls will result in corrupt data. + #[inline(always)] + pub fn encrypt_set_aad(&mut self, data: &[u8]) { + let _ = self.gmac.as_mut().unwrap().aad_update(data); + let pad = data.len() & 0xf; + if pad != 0 { + let _ = self.gmac.as_mut().unwrap().aad_update(&crate::ZEROES[0..(16 - pad)]); + } + } + + /// Feed plaintext in for the first encryption pass. + /// This may be called more than once. + #[inline(always)] + pub fn encrypt_first_pass(&mut self, plaintext: &[u8]) { + let _ = self.gmac.as_mut().unwrap().aad_update(plaintext); + } + + /// Finish first pass and begin second pass. + #[inline(always)] + pub fn encrypt_first_pass_finish(&mut self) { + let gmac = self.gmac.as_mut().unwrap(); + let _ = gmac.finalize(&mut self.tmp); + let _ = gmac.get_tag(&mut self.tmp); + unsafe { // tag[8..16] = tmp[0..8] ^ tmp[8..16] + let tmp = self.tmp.as_mut_ptr().cast::(); + *self.tag.as_mut_ptr().cast::().offset(1) = *tmp ^ *tmp.offset(1); + } + let mut tag_tmp = [0_u8; 32]; + let _ = Crypter::new(Cipher::aes_256_ecb(), Mode::Encrypt, &self.k1, None).unwrap().update(&self.tag, &mut tag_tmp); + self.tag.copy_from_slice(&tag_tmp[0..16]); + self.tmp.copy_from_slice(&self.tag); + self.tmp[12] &= 0x7f; + let _ = self.ctr.replace(Crypter::new(Cipher::aes_256_ctr(), Mode::Encrypt, &self.k1, Some(&self.tmp)).unwrap()); + } + + /// Feed plaintext for second pass and write ciphertext to supplied buffer. + /// This may be called more than once. + #[inline(always)] + pub fn encrypt_second_pass(&mut self, plaintext: &[u8], ciphertext: &mut [u8]) { + let _ = self.ctr.as_mut().unwrap().update(plaintext, ciphertext); + } + + /// Encrypt plaintext in place. + /// This may be called more than once. + #[inline(always)] + pub fn encrypt_second_pass_in_place(&mut self, plaintext_to_ciphertext: &mut [u8]) { + let _ = self.ctr.as_mut().unwrap().update(unsafe { std::slice::from_raw_parts(plaintext_to_ciphertext.as_ptr(), plaintext_to_ciphertext.len()) }, plaintext_to_ciphertext); + } + + /// Finish second pass and return a reference to the tag for this message. + /// The tag returned remains valid until reset() is called. + #[inline(always)] + pub fn encrypt_second_pass_finish(&mut self) -> &[u8; 16] { + return &self.tag; + } + + #[inline(always)] + fn decrypt_init_internal(&mut self) { + self.tmp[12] &= 0x7f; + let _ = self.ctr.replace(Crypter::new(Cipher::aes_256_ctr(), Mode::Decrypt, &self.k1, Some(&self.tmp)).unwrap()); + let mut tag_tmp = [0_u8; 32]; + let _ = Crypter::new(Cipher::aes_256_ecb(), Mode::Decrypt, &self.k1, None).unwrap().update(&self.tag, &mut tag_tmp); + self.tag.copy_from_slice(&tag_tmp[0..16]); + unsafe { // tmp[0..8] = tag[0..8], tmp[8..16] = 0 + let tmp = self.tmp.as_mut_ptr().cast::(); + *tmp = *self.tag.as_mut_ptr().cast::(); + *tmp.offset(1) = 0; + } + let _ = self.gmac.replace(Crypter::new(Cipher::aes_256_gcm(), Mode::Encrypt, &self.k0, Some(&self.tmp)).unwrap()); + } + + /// Initialize this cipher for decryption. + /// The supplied tag must be 16 bytes in length. Any other length will panic. + #[inline(always)] + pub fn decrypt_init(&mut self, tag: &[u8]) { + self.tmp.copy_from_slice(tag); + self.tag.copy_from_slice(tag); + self.decrypt_init_internal(); + } + + /// Set additional authenticated data to be checked. + #[inline(always)] + pub fn decrypt_set_aad(&mut self, data: &[u8]) { + self.encrypt_set_aad(data); + } + + /// Decrypt ciphertext and write to plaintext. + /// This may be called more than once. + #[inline(always)] + pub fn decrypt(&mut self, ciphertext: &[u8], plaintext: &mut [u8]) { + let _ = self.ctr.as_mut().unwrap().update(ciphertext, plaintext); + let _ = self.gmac.as_mut().unwrap().aad_update(plaintext); + } + + /// Decrypt ciphertext in place. + /// This may be called more than once. + #[inline(always)] + pub fn decrypt_in_place(&mut self, ciphertext_to_plaintext: &mut [u8]) { + let _ = self.ctr.as_mut().unwrap().update(unsafe { std::slice::from_raw_parts(ciphertext_to_plaintext.as_ptr(), ciphertext_to_plaintext.len()) }, ciphertext_to_plaintext); + let _ = self.gmac.as_mut().unwrap().aad_update(ciphertext_to_plaintext); + } + + /// Finish decryption and return true if authentication appears valid. + /// If this returns false the message should be dropped. + #[inline(always)] + pub fn decrypt_finish(&mut self) -> bool { + let gmac = self.gmac.as_mut().unwrap(); + let _ = gmac.finalize(&mut self.tmp); + let _ = gmac.get_tag(&mut self.tmp); + unsafe { // tag[8..16] == tmp[0..8] ^ tmp[8..16] + let tmp = self.tmp.as_mut_ptr().cast::(); + *self.tag.as_mut_ptr().cast::().offset(1) == *tmp ^ *tmp.offset(1) + } + } +} diff --git a/aes-gmac-siv/src/lib.rs b/aes-gmac-siv/src/lib.rs index 8ba8500ea..824d2bbd0 100644 --- a/aes-gmac-siv/src/lib.rs +++ b/aes-gmac-siv/src/lib.rs @@ -1,13 +1,21 @@ #[cfg(any(target_os = "macos", target_os = "ios"))] mod impl_macos; -#[cfg(not(any(target_os = "macos", target_os = "ios")))] + +#[cfg(not(any(target_os = "macos", target_os = "ios", target_arch = "s390x")))] mod impl_gcrypt; +#[cfg(all(not(any(target_os = "macos", target_os = "ios")), target_arch = "s390x"))] +mod impl_openssl; + #[cfg(any(target_os = "macos", target_os = "ios"))] pub use impl_macos::AesGmacSiv; -#[cfg(not(any(target_os = "macos", target_os = "ios")))] + +#[cfg(not(any(target_os = "macos", target_os = "ios", target_arch = "s390x")))] pub use impl_gcrypt::AesGmacSiv; +#[cfg(all(not(any(target_os = "macos", target_os = "ios")), target_arch = "s390x"))] +pub use impl_openssl::AesGmacSiv; + pub(crate) const ZEROES: [u8; 16] = [0_u8; 16]; #[cfg(test)] diff --git a/network-hypervisor/Cargo.lock b/network-hypervisor/Cargo.lock index fd7164217..24fe6f17b 100644 --- a/network-hypervisor/Cargo.lock +++ b/network-hypervisor/Cargo.lock @@ -7,8 +7,15 @@ name = "aes-gmac-siv" version = "0.1.0" dependencies = [ "gcrypt", + "openssl", ] +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "base64" version = "0.13.0" @@ -128,6 +135,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "gcrypt" version = "0.7.0" @@ -264,6 +286,33 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549430950c79ae24e6d02e0b7404534ecf311d94cc9f861e9e4020187d13d885" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + +[[package]] +name = "openssl-sys" +version = "0.9.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a7907e3bfa08bb85105209cdfcb6c63d109f8f6c1ed6ca318fff5c1853fbc1d" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.11.1" @@ -289,6 +338,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -463,6 +518,12 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.3"