Loading openssl-sys/src/lib.rs +5 −0 Original line number Diff line number Diff line Loading @@ -114,6 +114,10 @@ pub const EVP_PKEY_RSA: c_int = NID_rsaEncryption; pub const EVP_PKEY_HMAC: c_int = NID_hmac; pub const EVP_PKEY_DSA: c_int = NID_dsa; pub const EVP_CTRL_GCM_SET_IVLEN: c_int = 0x9; pub const EVP_CTRL_GCM_GET_TAG: c_int = 0x10; pub const EVP_CTRL_GCM_SET_TAG: c_int = 0x11; pub const MBSTRING_ASC: c_int = MBSTRING_FLAG | 1; pub const MBSTRING_BMP: c_int = MBSTRING_FLAG | 2; pub const MBSTRING_FLAG: c_int = 0x1000; Loading Loading @@ -1400,6 +1404,7 @@ extern { pub fn EVP_CIPHER_CTX_new() -> *mut EVP_CIPHER_CTX; pub fn EVP_CIPHER_CTX_set_padding(ctx: *mut EVP_CIPHER_CTX, padding: c_int) -> c_int; pub fn EVP_CIPHER_CTX_set_key_length(ctx: *mut EVP_CIPHER_CTX, keylen: c_int) -> c_int; pub fn EVP_CIPHER_CTX_ctrl(ctx: *mut EVP_CIPHER_CTX, type_: c_int, arg: c_int, ptr: *mut c_void) -> c_int; pub fn EVP_CIPHER_CTX_free(ctx: *mut EVP_CIPHER_CTX); pub fn EVP_CipherInit(ctx: *mut EVP_CIPHER_CTX, evp: *const EVP_CIPHER, Loading openssl/src/symm.rs +95 −3 Original line number Diff line number Diff line Loading @@ -135,8 +135,7 @@ impl Crypter { /// /// # Panics /// /// Panics if an IV is required by the cipher but not provided, or if the /// IV's length does not match the expected length (see `Cipher::iv_len`). /// Panics if an IV is required by the cipher but not provided. pub fn new(t: Cipher, mode: Mode, key: &[u8], Loading Loading @@ -169,7 +168,13 @@ impl Crypter { let key = key.as_ptr() as *mut _; let iv = match (iv, t.iv_len()) { (Some(iv), Some(len)) => { assert!(iv.len() == len); if iv.len() != len { assert!(iv.len() <= c_int::max_value() as usize); try!(cvt(ffi::EVP_CIPHER_CTX_ctrl(crypter.ctx, ffi::EVP_CTRL_GCM_SET_IVLEN, iv.len() as c_int, ptr::null_mut()))); } iv.as_ptr() as *mut _ } (Some(_), None) | (None, None) => ptr::null_mut(), Loading @@ -196,6 +201,39 @@ impl Crypter { } } /// Sets the tag used to authenticate ciphertext in AEAD ciphers such as AES GCM. /// /// When decrypting cipher text using an AEAD cipher, this must be called before `finalize`. pub fn set_tag(&mut self, tag: &[u8]) -> 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, tag.as_ptr() as *mut _)) .map(|_| ()) } } /// 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 /// is factored into the authentication tag. It must be called before the first call to /// `update`. pub fn aad_update(&mut self, input: &[u8]) -> Result<(), ErrorStack> { unsafe { assert!(input.len() <= c_int::max_value() as usize); let mut len = 0; cvt(ffi::EVP_CipherUpdate(self.ctx, ptr::null_mut(), &mut len, input.as_ptr(), input.len() as c_int)) .map(|_| ()) } } /// Feeds data from `input` through the cipher, writing encrypted/decrypted /// bytes into `output`. /// Loading Loading @@ -244,6 +282,21 @@ impl Crypter { Ok(outl as usize) } } /// Retrieves the authentication tag used to authenticate ciphertext in AEAD ciphers such /// as AES GCM. /// /// When encrypting data with an AEAD cipher, this must be called after `finalize`. pub fn get_tag(&self, tag: &mut [u8]) -> Result<(), ErrorStack> { unsafe { assert!(tag.len() <= c_int::max_value() as usize); cvt(ffi::EVP_CIPHER_CTX_ctrl(self.ctx, ffi::EVP_CTRL_GCM_GET_TAG, tag.len() as c_int, tag.as_mut_ptr() as *mut _)) .map(|_| ()) } } } impl Drop for Crypter { Loading Loading @@ -319,6 +372,7 @@ use self::compat::*; #[cfg(test)] mod tests { use serialize::hex::{FromHex, ToHex}; use super::*; // Test vectors from FIPS-197: // http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf Loading Loading @@ -534,4 +588,42 @@ mod tests { cipher_test(super::Cipher::des_ecb(), pt, ct, key, iv); } #[test] fn test_aes128_gcm() { let key = "0e00c76561d2bd9b40c3c15427e2b08f"; let iv = "492cadaccd3ca3fbc9cf9f06eb3325c4e159850b0dbe98199b89b7af528806610b6f63998e1eae80c348e7\ 4cbb921d8326631631fc6a5d304f39166daf7ea15fa1977f101819adb510b50fe9932e12c5a85aa3fd1e73\ d8d760af218be829903a77c63359d75edd91b4f6ed5465a72662f5055999e059e7654a8edc921aa0d496"; let pt = "fef03c2d7fb15bf0d2df18007d99f967c878ad59359034f7bb2c19af120685d78e32f6b8b83b032019956c\ a9c0195721476b85"; let aad = "d8f1163d8c840292a2b2dacf4ac7c36aff8733f18fabb4fa5594544125e03d1e6e5d6d0fd61656c8d8f327\ c92839ae5539bb469c9257f109ebff85aad7bd220fdaa95c022dbd0c7bb2d878ad504122c943045d3c5eba\ 8f1f56c0"; let ct = "4f6cf471be7cbd2575cd5a1747aea8fe9dea83e51936beac3e68f66206922060c697ffa7af80ad6bb68f2c\ f4fc97416ee52abe"; let tag = "e20b6655"; let mut crypter = Crypter::new(Cipher::aes_128_gcm(), Mode::Encrypt, &key.from_hex().unwrap(), Some(&iv.from_hex().unwrap())).unwrap(); let mut out = [0; 1024]; crypter.aad_update(&aad.from_hex().unwrap()).unwrap(); let mut nwritten = crypter.update(&pt.from_hex().unwrap(), &mut out).unwrap(); nwritten += crypter.finalize(&mut out[nwritten..]).unwrap(); assert_eq!(ct, out[..nwritten].to_hex()); let mut actual_tag = [0; 4]; crypter.get_tag(&mut actual_tag).unwrap(); assert_eq!(tag, actual_tag.to_hex()); let mut crypter = Crypter::new(Cipher::aes_128_gcm(), Mode::Decrypt, &key.from_hex().unwrap(), Some(&iv.from_hex().unwrap())).unwrap(); let mut out = [0; 1024]; crypter.aad_update(&aad.from_hex().unwrap()).unwrap(); let mut nwritten = crypter.update(&ct.from_hex().unwrap(), &mut out).unwrap(); crypter.set_tag(&tag.from_hex().unwrap()).unwrap(); nwritten += crypter.finalize(&mut out[nwritten..]).unwrap(); assert_eq!(pt, out[..nwritten].to_hex()); } } Loading
openssl-sys/src/lib.rs +5 −0 Original line number Diff line number Diff line Loading @@ -114,6 +114,10 @@ pub const EVP_PKEY_RSA: c_int = NID_rsaEncryption; pub const EVP_PKEY_HMAC: c_int = NID_hmac; pub const EVP_PKEY_DSA: c_int = NID_dsa; pub const EVP_CTRL_GCM_SET_IVLEN: c_int = 0x9; pub const EVP_CTRL_GCM_GET_TAG: c_int = 0x10; pub const EVP_CTRL_GCM_SET_TAG: c_int = 0x11; pub const MBSTRING_ASC: c_int = MBSTRING_FLAG | 1; pub const MBSTRING_BMP: c_int = MBSTRING_FLAG | 2; pub const MBSTRING_FLAG: c_int = 0x1000; Loading Loading @@ -1400,6 +1404,7 @@ extern { pub fn EVP_CIPHER_CTX_new() -> *mut EVP_CIPHER_CTX; pub fn EVP_CIPHER_CTX_set_padding(ctx: *mut EVP_CIPHER_CTX, padding: c_int) -> c_int; pub fn EVP_CIPHER_CTX_set_key_length(ctx: *mut EVP_CIPHER_CTX, keylen: c_int) -> c_int; pub fn EVP_CIPHER_CTX_ctrl(ctx: *mut EVP_CIPHER_CTX, type_: c_int, arg: c_int, ptr: *mut c_void) -> c_int; pub fn EVP_CIPHER_CTX_free(ctx: *mut EVP_CIPHER_CTX); pub fn EVP_CipherInit(ctx: *mut EVP_CIPHER_CTX, evp: *const EVP_CIPHER, Loading
openssl/src/symm.rs +95 −3 Original line number Diff line number Diff line Loading @@ -135,8 +135,7 @@ impl Crypter { /// /// # Panics /// /// Panics if an IV is required by the cipher but not provided, or if the /// IV's length does not match the expected length (see `Cipher::iv_len`). /// Panics if an IV is required by the cipher but not provided. pub fn new(t: Cipher, mode: Mode, key: &[u8], Loading Loading @@ -169,7 +168,13 @@ impl Crypter { let key = key.as_ptr() as *mut _; let iv = match (iv, t.iv_len()) { (Some(iv), Some(len)) => { assert!(iv.len() == len); if iv.len() != len { assert!(iv.len() <= c_int::max_value() as usize); try!(cvt(ffi::EVP_CIPHER_CTX_ctrl(crypter.ctx, ffi::EVP_CTRL_GCM_SET_IVLEN, iv.len() as c_int, ptr::null_mut()))); } iv.as_ptr() as *mut _ } (Some(_), None) | (None, None) => ptr::null_mut(), Loading @@ -196,6 +201,39 @@ impl Crypter { } } /// Sets the tag used to authenticate ciphertext in AEAD ciphers such as AES GCM. /// /// When decrypting cipher text using an AEAD cipher, this must be called before `finalize`. pub fn set_tag(&mut self, tag: &[u8]) -> 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, tag.as_ptr() as *mut _)) .map(|_| ()) } } /// 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 /// is factored into the authentication tag. It must be called before the first call to /// `update`. pub fn aad_update(&mut self, input: &[u8]) -> Result<(), ErrorStack> { unsafe { assert!(input.len() <= c_int::max_value() as usize); let mut len = 0; cvt(ffi::EVP_CipherUpdate(self.ctx, ptr::null_mut(), &mut len, input.as_ptr(), input.len() as c_int)) .map(|_| ()) } } /// Feeds data from `input` through the cipher, writing encrypted/decrypted /// bytes into `output`. /// Loading Loading @@ -244,6 +282,21 @@ impl Crypter { Ok(outl as usize) } } /// Retrieves the authentication tag used to authenticate ciphertext in AEAD ciphers such /// as AES GCM. /// /// When encrypting data with an AEAD cipher, this must be called after `finalize`. pub fn get_tag(&self, tag: &mut [u8]) -> Result<(), ErrorStack> { unsafe { assert!(tag.len() <= c_int::max_value() as usize); cvt(ffi::EVP_CIPHER_CTX_ctrl(self.ctx, ffi::EVP_CTRL_GCM_GET_TAG, tag.len() as c_int, tag.as_mut_ptr() as *mut _)) .map(|_| ()) } } } impl Drop for Crypter { Loading Loading @@ -319,6 +372,7 @@ use self::compat::*; #[cfg(test)] mod tests { use serialize::hex::{FromHex, ToHex}; use super::*; // Test vectors from FIPS-197: // http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf Loading Loading @@ -534,4 +588,42 @@ mod tests { cipher_test(super::Cipher::des_ecb(), pt, ct, key, iv); } #[test] fn test_aes128_gcm() { let key = "0e00c76561d2bd9b40c3c15427e2b08f"; let iv = "492cadaccd3ca3fbc9cf9f06eb3325c4e159850b0dbe98199b89b7af528806610b6f63998e1eae80c348e7\ 4cbb921d8326631631fc6a5d304f39166daf7ea15fa1977f101819adb510b50fe9932e12c5a85aa3fd1e73\ d8d760af218be829903a77c63359d75edd91b4f6ed5465a72662f5055999e059e7654a8edc921aa0d496"; let pt = "fef03c2d7fb15bf0d2df18007d99f967c878ad59359034f7bb2c19af120685d78e32f6b8b83b032019956c\ a9c0195721476b85"; let aad = "d8f1163d8c840292a2b2dacf4ac7c36aff8733f18fabb4fa5594544125e03d1e6e5d6d0fd61656c8d8f327\ c92839ae5539bb469c9257f109ebff85aad7bd220fdaa95c022dbd0c7bb2d878ad504122c943045d3c5eba\ 8f1f56c0"; let ct = "4f6cf471be7cbd2575cd5a1747aea8fe9dea83e51936beac3e68f66206922060c697ffa7af80ad6bb68f2c\ f4fc97416ee52abe"; let tag = "e20b6655"; let mut crypter = Crypter::new(Cipher::aes_128_gcm(), Mode::Encrypt, &key.from_hex().unwrap(), Some(&iv.from_hex().unwrap())).unwrap(); let mut out = [0; 1024]; crypter.aad_update(&aad.from_hex().unwrap()).unwrap(); let mut nwritten = crypter.update(&pt.from_hex().unwrap(), &mut out).unwrap(); nwritten += crypter.finalize(&mut out[nwritten..]).unwrap(); assert_eq!(ct, out[..nwritten].to_hex()); let mut actual_tag = [0; 4]; crypter.get_tag(&mut actual_tag).unwrap(); assert_eq!(tag, actual_tag.to_hex()); let mut crypter = Crypter::new(Cipher::aes_128_gcm(), Mode::Decrypt, &key.from_hex().unwrap(), Some(&iv.from_hex().unwrap())).unwrap(); let mut out = [0; 1024]; crypter.aad_update(&aad.from_hex().unwrap()).unwrap(); let mut nwritten = crypter.update(&ct.from_hex().unwrap(), &mut out).unwrap(); crypter.set_tag(&tag.from_hex().unwrap()).unwrap(); nwritten += crypter.finalize(&mut out[nwritten..]).unwrap(); assert_eq!(pt, out[..nwritten].to_hex()); } }