      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125

    //! Shared secret derivation.
    use ffi;
    use foreign_types::ForeignTypeRef;
    use std::marker::PhantomData;
    use std::ptr;

    use error::ErrorStack;
    use pkey::{HasPrivate, HasPublic, PKeyRef};
    use {cvt, cvt_p};

    /// A type used to derive a shared secret between two keys.
    pub struct Deriver<'a>(*mut ffi::EVP_PKEY_CTX, PhantomData<&'a ()>);

    unsafe impl<'a> Sync for Deriver<'a> {}
    unsafe impl<'a> Send for Deriver<'a> {}

    #[allow(clippy::len_without_is_empty)]
    impl<'a> Deriver<'a> {
        /// Creates a new `Deriver` using the provided private key.
        ///
        /// This corresponds to [`EVP_PKEY_derive_init`].
        ///
        /// [`EVP_PKEY_derive_init`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
        pub fn new<T>(key: &'a PKeyRef<T>) -> Result<Deriver<'a>, ErrorStack>
        where
            T: HasPrivate,
        {
            unsafe {
                cvt_p(ffi::EVP_PKEY_CTX_new(key.as_ptr(), ptr::null_mut()))
                    .map(|p| Deriver(p, PhantomData))
                    .and_then(|ctx| cvt(ffi::EVP_PKEY_derive_init(ctx.0)).map(|_| ctx))
            }
        }

        /// Sets the peer key used for secret derivation.
        ///
        /// This corresponds to [`EVP_PKEY_derive_set_peer`]:
        ///
        /// [`EVP_PKEY_derive_set_peer`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
        pub fn set_peer<T>(&mut self, key: &'a PKeyRef<T>) -> Result<(), ErrorStack>
        where
            T: HasPublic,
        {
            unsafe { cvt(ffi::EVP_PKEY_derive_set_peer(self.0, key.as_ptr())).map(|_| ()) }
        }

        /// Returns the size of the shared secret.
        ///
        /// It can be used to size the buffer passed to [`Deriver::derive`].
        ///
        /// This corresponds to [`EVP_PKEY_derive`].
        ///
        /// [`Deriver::derive`]: #method.derive
        /// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
        pub fn len(&mut self) -> Result<usize, ErrorStack> {
            unsafe {
                let mut len = 0;
                cvt(ffi::EVP_PKEY_derive(self.0, ptr::null_mut(), &mut len)).map(|_| len)
            }
        }

        /// Derives a shared secret between the two keys, writing it into the buffer.
        ///
        /// Returns the number of bytes written.
        ///
        /// This corresponds to [`EVP_PKEY_derive`].
        ///
        /// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
        pub fn derive(&mut self, buf: &mut [u8]) -> Result<usize, ErrorStack> {
            let mut len = buf.len();
            unsafe {
                cvt(ffi::EVP_PKEY_derive(
                    self.0,
                    buf.as_mut_ptr() as *mut _,
                    &mut len,
                ))
                .map(|_| len)
            }
        }

        /// A convenience function which derives a shared secret and returns it in a new buffer.
        ///
        /// This simply wraps [`Deriver::len`] and [`Deriver::derive`].
        ///
        /// [`Deriver::len`]: #method.len
        /// [`Deriver::derive`]: #method.derive
        pub fn derive_to_vec(&mut self) -> Result<Vec<u8>, ErrorStack> {
            let len = self.len()?;
            let mut buf = vec![0; len];
            let len = self.derive(&mut buf)?;
            buf.truncate(len);
            Ok(buf)
        }
    }

    #[cfg(test)]
    mod test {
        use super::*;

        use ec::{EcGroup, EcKey};
        use nid::Nid;
        use pkey::PKey;

        #[test]
        fn derive_without_peer() {
            let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
            let ec_key = EcKey::generate(&group).unwrap();
            let pkey = PKey::from_ec_key(ec_key).unwrap();
            let mut deriver = Deriver::new(&pkey).unwrap();
            deriver.derive_to_vec().unwrap_err();
        }

        #[test]
        fn test_ec_key_derive() {
            let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
            let ec_key = EcKey::generate(&group).unwrap();
            let ec_key2 = EcKey::generate(&group).unwrap();
            let pkey = PKey::from_ec_key(ec_key).unwrap();
            let pkey2 = PKey::from_ec_key(ec_key2).unwrap();
            let mut deriver = Deriver::new(&pkey).unwrap();
            deriver.set_peer(&pkey2).unwrap();
            let shared = deriver.derive_to_vec().unwrap();
            assert!(!shared.is_empty());
        }
    }