Commit e230d20f authored by Steven Fackler's avatar Steven Fackler Committed by GitHub
Browse files

Merge pull request #519 from sfackler/gcm-fixes

Actually support AES GCM
parents b022bd2d 203a02c3
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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,
+95 −3
Original line number Diff line number Diff line
@@ -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],
@@ -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(),
@@ -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`.
    ///
@@ -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 {
@@ -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
@@ -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());
    }
}