Unverified Commit 29047944 authored by Steven Fackler's avatar Steven Fackler
Browse files

Add CipherCtx

parent 66635f10
Loading
Loading
Loading
Loading
+32 −1
Original line number Diff line number Diff line
@@ -39,6 +39,12 @@ cfg_if! {
            pub fn EVP_CIPHER_get_block_size(cipher: *const EVP_CIPHER) -> c_int;
            pub fn EVP_CIPHER_get_iv_length(cipher: *const EVP_CIPHER) -> c_int;
            pub fn EVP_CIPHER_get_nid(cipher: *const EVP_CIPHER) -> c_int;

            pub fn EVP_CIPHER_CTX_get0_cipher(ctx: *const EVP_CIPHER_CTX) -> *const EVP_CIPHER;
            pub fn EVP_CIPHER_CTX_get_block_size(ctx: *const EVP_CIPHER_CTX) -> c_int;
            pub fn EVP_CIPHER_CTX_get_key_length(ctx: *const EVP_CIPHER_CTX) -> c_int;
            pub fn EVP_CIPHER_CTX_get_iv_length(ctx: *const EVP_CIPHER_CTX) -> c_int;
            pub fn EVP_CIPHER_CTX_get_tag_length(ctx: *const EVP_CIPHER_CTX) -> c_int;
        }

        #[inline]
@@ -70,6 +76,26 @@ cfg_if! {
        pub unsafe fn EVP_CIPHER_nid(cipher: *const EVP_CIPHER) -> c_int {
            EVP_CIPHER_get_nid(cipher)
        }

        #[inline]
        pub unsafe fn EVP_CIPHER_CTX_block_size(ctx: *const EVP_CIPHER_CTX) -> c_int {
            EVP_CIPHER_CTX_get_block_size(ctx)
        }

        #[inline]
        pub unsafe fn EVP_CIPHER_CTX_key_length(ctx: *const EVP_CIPHER_CTX) -> c_int {
            EVP_CIPHER_CTX_get_key_length(ctx)
        }

        #[inline]
        pub unsafe fn EVP_CIPHER_CTX_iv_length(ctx: *const EVP_CIPHER_CTX) -> c_int {
            EVP_CIPHER_CTX_get_iv_length(ctx)
        }

        #[inline]
        pub unsafe fn EVP_CIPHER_CTX_tag_length(ctx: *const EVP_CIPHER_CTX) -> c_int {
            EVP_CIPHER_CTX_get_tag_length(ctx)
        }
    } else {
        extern "C" {
            pub fn EVP_MD_size(md: *const EVP_MD) -> c_int;
@@ -79,10 +105,15 @@ cfg_if! {
            pub fn EVP_CIPHER_block_size(cipher: *const EVP_CIPHER) -> c_int;
            pub fn EVP_CIPHER_iv_length(cipher: *const EVP_CIPHER) -> c_int;
            pub fn EVP_CIPHER_nid(cipher: *const EVP_CIPHER) -> c_int;

            pub fn EVP_CIPHER_CTX_cipher(ctx: *const EVP_CIPHER_CTX) -> *const EVP_CIPHER;
            pub fn EVP_CIPHER_CTX_block_size(ctx: *const EVP_CIPHER_CTX) -> c_int;
            pub fn EVP_CIPHER_CTX_key_length(ctx: *const EVP_CIPHER_CTX) -> c_int;
            pub fn EVP_CIPHER_CTX_iv_length(ctx: *const EVP_CIPHER_CTX) -> c_int;
            pub fn EVP_CIPHER_CTX_tag_length(ctx: *const EVP_CIPHER_CTX) -> c_int;
        }
    }
}
extern "C" {}

cfg_if! {
    if #[cfg(ossl110)] {
+273 −0
Original line number Diff line number Diff line
use crate::error::ErrorStack;
use crate::symm::{Cipher, Mode};
use crate::{cvt, cvt_p};
use cfg_if::cfg_if;
use foreign_types::{ForeignType, ForeignTypeRef};
use libc::c_int;
use std::convert::TryFrom;
use std::ptr;

cfg_if! {
    if #[cfg(ossl300)] {
        use ffi::EVP_CIPHER_CTX_get0_cipher;
    } else {
        use ffi::EVP_CIPHER_CTX_cipher as EVP_CIPHER_CTX_get0_cipher;
    }
}

foreign_type_and_impl_send_sync! {
    type CType = ffi::EVP_CIPHER_CTX;
    fn drop = ffi::EVP_CIPHER_CTX_free;

    pub struct CipherCtx;
    pub struct CipherCtxRef;
}

impl CipherCtx {
    pub fn new() -> Result<Self, ErrorStack> {
        ffi::init();

        unsafe {
            let ptr = cvt_p(ffi::EVP_CIPHER_CTX_new())?;
            Ok(CipherCtx::from_ptr(ptr))
        }
    }
}

impl CipherCtxRef {
    pub fn init(
        &mut self,
        // FIXME CipherRef
        type_: Option<&Cipher>,
        key: Option<&[u8]>,
        iv: Option<&[u8]>,
        mode: Mode,
    ) -> Result<(), ErrorStack> {
        if let Some(key) = key {
            if let Some(len) = self.key_length() {
                assert_eq!(len, key.len());
            }
        }

        if let Some(iv) = iv {
            if let Some(len) = self.iv_length() {
                assert_eq!(len, iv.len());
            }
        }

        let mode = match mode {
            Mode::Encrypt => 1,
            Mode::Decrypt => 0,
        };

        unsafe {
            cvt(ffi::EVP_CipherInit_ex(
                self.as_ptr(),
                type_.map_or(ptr::null(), Cipher::as_ptr),
                ptr::null_mut(),
                key.map_or(ptr::null(), |k| k.as_ptr()),
                iv.map_or(ptr::null(), |iv| iv.as_ptr()),
                mode,
            ))?;
        }

        Ok(())
    }

    fn assert_cipher(&self) {
        unsafe {
            assert!(!EVP_CIPHER_CTX_get0_cipher(self.as_ptr()).is_null());
        }
    }

    pub fn block_size(&self) -> Option<usize> {
        self.assert_cipher();

        unsafe {
            let r = ffi::EVP_CIPHER_CTX_block_size(self.as_ptr());
            if r > 0 {
                Some(r as usize)
            } else {
                None
            }
        }
    }

    pub fn key_length(&self) -> Option<usize> {
        self.assert_cipher();

        unsafe {
            let r = ffi::EVP_CIPHER_CTX_key_length(self.as_ptr());
            if r > 0 {
                Some(r as usize)
            } else {
                None
            }
        }
    }

    pub fn set_key_length(&mut self, len: usize) -> Result<(), ErrorStack> {
        self.assert_cipher();

        let len = c_int::try_from(len).unwrap();

        unsafe {
            cvt(ffi::EVP_CIPHER_CTX_set_key_length(self.as_ptr(), len))?;
        }

        Ok(())
    }

    pub fn iv_length(&self) -> Option<usize> {
        self.assert_cipher();

        unsafe {
            let r = ffi::EVP_CIPHER_CTX_iv_length(self.as_ptr());
            if r > 0 {
                Some(r as usize)
            } else {
                None
            }
        }
    }

    pub fn set_iv_length(&mut self, len: usize) -> Result<(), ErrorStack> {
        self.assert_cipher();

        let len = c_int::try_from(len).unwrap();

        unsafe {
            cvt(ffi::EVP_CIPHER_CTX_ctrl(
                self.as_ptr(),
                ffi::EVP_CTRL_GCM_SET_IVLEN,
                len,
                ptr::null_mut(),
            ))?;
        }

        Ok(())
    }

    pub fn tag_length(&self) -> Option<usize> {
        self.assert_cipher();

        unsafe {
            let r = ffi::EVP_CIPHER_CTX_tag_length(self.as_ptr());
            if r > 0 {
                Some(r as usize)
            } else {
                None
            }
        }
    }

    pub fn tag(&self, tag: &mut [u8]) -> Result<(), ErrorStack> {
        let len = c_int::try_from(tag.len()).unwrap();

        unsafe {
            cvt(ffi::EVP_CIPHER_CTX_ctrl(
                self.as_ptr(),
                ffi::EVP_CTRL_GCM_GET_TAG,
                len,
                tag.as_mut_ptr() as *mut _,
            ))?;
        }

        Ok(())
    }

    pub fn set_tag_length(&mut self, len: usize) -> Result<(), ErrorStack> {
        let len = c_int::try_from(len).unwrap();

        unsafe {
            cvt(ffi::EVP_CIPHER_CTX_ctrl(
                self.as_ptr(),
                ffi::EVP_CTRL_GCM_SET_TAG,
                len,
                ptr::null_mut(),
            ))?;
        }

        Ok(())
    }

    pub fn set_tag(&mut self, tag: &[u8]) -> Result<(), ErrorStack> {
        let len = c_int::try_from(tag.len()).unwrap();

        unsafe {
            cvt(ffi::EVP_CIPHER_CTX_ctrl(
                self.as_ptr(),
                ffi::EVP_CTRL_GCM_SET_TAG,
                len,
                tag.as_ptr() as *mut _,
            ))?;
        }

        Ok(())
    }

    pub fn set_padding(&mut self, padding: bool) {
        unsafe {
            ffi::EVP_CIPHER_CTX_set_padding(self.as_ptr(), padding as c_int);
        }
    }

    pub fn set_data_len(&mut self, len: usize) -> Result<(), ErrorStack> {
        let len = c_int::try_from(len).unwrap();

        unsafe {
            cvt(ffi::EVP_CipherUpdate(
                self.as_ptr(),
                ptr::null_mut(),
                &mut 0,
                ptr::null(),
                len,
            ))?;
        }

        Ok(())
    }

    pub fn update(&mut self, input: &[u8], output: Option<&mut [u8]>) -> Result<usize, ErrorStack> {
        let inlen = c_int::try_from(input.len()).unwrap();

        if let (Some(mut block_size), Some(output)) = (self.block_size(), &output) {
            if block_size == 1 {
                block_size = 0;
            }
            assert!(output.len() >= input.len() + block_size);
        }

        let mut outlen = 0;
        unsafe {
            cvt(ffi::EVP_CipherUpdate(
                self.as_ptr(),
                output.map_or(ptr::null_mut(), |b| b.as_mut_ptr()),
                &mut outlen,
                input.as_ptr(),
                inlen,
            ))?;
        }

        Ok(outlen as usize)
    }

    pub fn finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> {
        if let Some(block_size) = self.block_size() {
            if block_size > 1 {
                assert!(output.len() >= block_size);
            }
        }

        let mut outl = 0;
        unsafe {
            cvt(ffi::EVP_CipherFinal(
                self.as_ptr(),
                output.as_mut_ptr(),
                &mut outl,
            ))?;
        }

        Ok(outl as usize)
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -134,6 +134,7 @@ pub mod aes;
pub mod asn1;
pub mod base64;
pub mod bn;
pub mod cipher_ctx;
#[cfg(all(not(libressl), not(osslconf = "OPENSSL_NO_CMS")))]
pub mod cms;
pub mod conf;
+22 −169
Original line number Diff line number Diff line
@@ -51,15 +51,10 @@
//! assert_eq!("Foo bar", output_string);
//! println!("Decrypted: '{}'", output_string);
//! ```

use cfg_if::cfg_if;
use libc::c_int;
use std::cmp;
use std::ptr;

use crate::cipher_ctx::CipherCtx;
use crate::error::ErrorStack;
use crate::nid::Nid;
use crate::{cvt, cvt_p};
use cfg_if::cfg_if;

#[derive(Copy, Clone)]
pub enum Mode {
@@ -438,13 +433,9 @@ unsafe impl Send for Cipher {}
/// assert_eq!(b"Some Stream of Crypto Text", &plaintext[..]);
/// ```
pub struct Crypter {
    ctx: *mut ffi::EVP_CIPHER_CTX,
    block_size: usize,
    ctx: CipherCtx,
}

unsafe impl Sync for Crypter {}
unsafe impl Send for Crypter {}

impl Crypter {
    /// Creates a new `Crypter`.  The initialisation vector, `iv`, is not necessary for certain
    /// types of `Cipher`.
@@ -459,63 +450,18 @@ impl Crypter {
        key: &[u8],
        iv: Option<&[u8]>,
    ) -> Result<Crypter, ErrorStack> {
        ffi::init();
        let mut ctx = CipherCtx::new()?;
        ctx.init(Some(&t), None, None, mode)?;

        unsafe {
            let ctx = cvt_p(ffi::EVP_CIPHER_CTX_new())?;
            let crypter = Crypter {
                ctx,
                block_size: t.block_size(),
            };
        ctx.set_key_length(key.len())?;

            let mode = match mode {
                Mode::Encrypt => 1,
                Mode::Decrypt => 0,
            };
        if let Some(iv) = iv {
            ctx.set_iv_length(iv.len())?;
        }

            cvt(ffi::EVP_CipherInit_ex(
                crypter.ctx,
                t.as_ptr(),
                ptr::null_mut(),
                ptr::null_mut(),
                ptr::null_mut(),
                mode,
            ))?;

            assert!(key.len() <= c_int::max_value() as usize);
            cvt(ffi::EVP_CIPHER_CTX_set_key_length(
                crypter.ctx,
                key.len() as c_int,
            ))?;

            let key = key.as_ptr() as *mut _;
            let iv = match (iv, t.iv_len()) {
                (Some(iv), Some(len)) => {
                    if iv.len() != len {
                        assert!(iv.len() <= c_int::max_value() as usize);
                        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(),
                (None, Some(_)) => panic!("an IV is required for this cipher"),
            };
            cvt(ffi::EVP_CipherInit_ex(
                crypter.ctx,
                ptr::null(),
                ptr::null_mut(),
                key,
                iv,
                mode,
            ))?;
        ctx.init(None, Some(key), iv, mode)?;

            Ok(crypter)
        }
        Ok(Crypter { ctx })
    }

    /// Enables or disables padding.
@@ -523,26 +469,14 @@ impl Crypter {
    /// If padding is disabled, total amount of data encrypted/decrypted must
    /// be a multiple of the cipher's block size.
    pub fn pad(&mut self, padding: bool) {
        unsafe {
            ffi::EVP_CIPHER_CTX_set_padding(self.ctx, padding as c_int);
        }
        self.ctx.set_padding(padding)
    }

    /// 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(|_| ())
        }
        self.ctx.set_tag(tag)
    }

    /// Sets the length of the authentication tag to generate in AES CCM.
@@ -550,17 +484,7 @@ impl Crypter {
    /// When encrypting with AES CCM, the tag length needs to be explicitly set in order
    /// to use a value different than the default 12 bytes.
    pub fn set_tag_len(&mut self, tag_len: usize) -> 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,
                ptr::null_mut(),
            ))
            .map(|_| ())
        }
        self.ctx.set_tag_length(tag_len)
    }

    /// Feeds total plaintext length to the cipher.
@@ -568,18 +492,7 @@ impl Crypter {
    /// The total plaintext or ciphertext length MUST be passed to the cipher when it operates in
    /// CCM mode.
    pub fn set_data_len(&mut self, data_len: usize) -> Result<(), ErrorStack> {
        unsafe {
            assert!(data_len <= c_int::max_value() as usize);
            let mut len = 0;
            cvt(ffi::EVP_CipherUpdate(
                self.ctx,
                ptr::null_mut(),
                &mut len,
                ptr::null_mut(),
                data_len as c_int,
            ))
            .map(|_| ())
        }
        self.ctx.set_data_len(data_len)
    }

    /// Feeds Additional Authenticated Data (AAD) through the cipher.
@@ -588,18 +501,8 @@ impl Crypter {
    /// 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(|_| ())
        }
        self.ctx.update(input, None)?;
        Ok(())
    }

    /// Feeds data from `input` through the cipher, writing encrypted/decrypted
@@ -617,27 +520,7 @@ impl Crypter {
    ///
    /// Panics if `output.len() > c_int::max_value()`.
    pub fn update(&mut self, input: &[u8], output: &mut [u8]) -> Result<usize, ErrorStack> {
        unsafe {
            let block_size = if self.block_size > 1 {
                self.block_size
            } else {
                0
            };
            assert!(output.len() >= input.len() + block_size);
            assert!(output.len() <= c_int::max_value() as usize);
            let mut outl = output.len() as c_int;
            let inl = input.len() as c_int;

            cvt(ffi::EVP_CipherUpdate(
                self.ctx,
                output.as_mut_ptr(),
                &mut outl,
                input.as_ptr(),
                inl,
            ))?;

            Ok(outl as usize)
        }
        self.ctx.update(input, Some(output))
    }

    /// Finishes the encryption/decryption process, writing any remaining data
@@ -652,20 +535,7 @@ impl Crypter {
    /// Panics for block ciphers if `output.len() < block_size`,
    /// where `block_size` is the block size of the cipher (see `Cipher::block_size`).
    pub fn finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> {
        unsafe {
            if self.block_size > 1 {
                assert!(output.len() >= self.block_size);
            }
            let mut outl = cmp::min(output.len(), c_int::max_value() as usize) as c_int;

            cvt(ffi::EVP_CipherFinal(
                self.ctx,
                output.as_mut_ptr(),
                &mut outl,
            ))?;

            Ok(outl as usize)
        }
        self.ctx.finalize(output)
    }

    /// Retrieves the authentication tag used to authenticate ciphertext in AEAD ciphers such
@@ -677,24 +547,7 @@ impl Crypter {
    /// range of tag sizes, it is recommended to pick the maximum size. For AES GCM, this is 16
    /// bytes, for example.
    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 {
    fn drop(&mut self) {
        unsafe {
            ffi::EVP_CIPHER_CTX_free(self.ctx);
        }
        self.ctx.tag(tag)
    }
}