1 //! Shared secret derivation.
2 use foreign_types::ForeignTypeRef;
3 use std::marker::PhantomData;
4 use std::ptr;
5 
6 use crate::error::ErrorStack;
7 use crate::pkey::{HasPrivate, HasPublic, PKeyRef};
8 use crate::{cvt, cvt_p};
9 
10 /// A type used to derive a shared secret between two keys.
11 pub struct Deriver<'a>(*mut ffi::EVP_PKEY_CTX, PhantomData<&'a ()>);
12 
13 unsafe impl<'a> Sync for Deriver<'a> {}
14 unsafe impl<'a> Send for Deriver<'a> {}
15 
16 #[allow(clippy::len_without_is_empty)]
17 impl<'a> Deriver<'a> {
18     /// Creates a new `Deriver` using the provided private key.
19     ///
20     /// This corresponds to [`EVP_PKEY_derive_init`].
21     ///
22     /// [`EVP_PKEY_derive_init`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
new<T>(key: &'a PKeyRef<T>) -> Result<Deriver<'a>, ErrorStack> where T: HasPrivate,23     pub fn new<T>(key: &'a PKeyRef<T>) -> Result<Deriver<'a>, ErrorStack>
24     where
25         T: HasPrivate,
26     {
27         unsafe {
28             cvt_p(ffi::EVP_PKEY_CTX_new(key.as_ptr(), ptr::null_mut()))
29                 .map(|p| Deriver(p, PhantomData))
30                 .and_then(|ctx| cvt(ffi::EVP_PKEY_derive_init(ctx.0)).map(|_| ctx))
31         }
32     }
33 
34     /// Sets the peer key used for secret derivation.
35     ///
36     /// This corresponds to [`EVP_PKEY_derive_set_peer`]:
37     ///
38     /// [`EVP_PKEY_derive_set_peer`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
set_peer<T>(&mut self, key: &'a PKeyRef<T>) -> Result<(), ErrorStack> where T: HasPublic,39     pub fn set_peer<T>(&mut self, key: &'a PKeyRef<T>) -> Result<(), ErrorStack>
40     where
41         T: HasPublic,
42     {
43         unsafe { cvt(ffi::EVP_PKEY_derive_set_peer(self.0, key.as_ptr())).map(|_| ()) }
44     }
45 
46     /// Returns the size of the shared secret.
47     ///
48     /// It can be used to size the buffer passed to [`Deriver::derive`].
49     ///
50     /// This corresponds to [`EVP_PKEY_derive`].
51     ///
52     /// [`Deriver::derive`]: #method.derive
53     /// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
len(&mut self) -> Result<usize, ErrorStack>54     pub fn len(&mut self) -> Result<usize, ErrorStack> {
55         unsafe {
56             let mut len = 0;
57             cvt(ffi::EVP_PKEY_derive(self.0, ptr::null_mut(), &mut len)).map(|_| len)
58         }
59     }
60 
61     /// Derives a shared secret between the two keys, writing it into the buffer.
62     ///
63     /// Returns the number of bytes written.
64     ///
65     /// This corresponds to [`EVP_PKEY_derive`].
66     ///
67     /// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
derive(&mut self, buf: &mut [u8]) -> Result<usize, ErrorStack>68     pub fn derive(&mut self, buf: &mut [u8]) -> Result<usize, ErrorStack> {
69         let mut len = buf.len();
70         unsafe {
71             cvt(ffi::EVP_PKEY_derive(
72                 self.0,
73                 buf.as_mut_ptr() as *mut _,
74                 &mut len,
75             ))
76             .map(|_| len)
77         }
78     }
79 
80     /// A convenience function which derives a shared secret and returns it in a new buffer.
81     ///
82     /// This simply wraps [`Deriver::len`] and [`Deriver::derive`].
83     ///
84     /// [`Deriver::len`]: #method.len
85     /// [`Deriver::derive`]: #method.derive
derive_to_vec(&mut self) -> Result<Vec<u8>, ErrorStack>86     pub fn derive_to_vec(&mut self) -> Result<Vec<u8>, ErrorStack> {
87         let len = self.len()?;
88         let mut buf = vec![0; len];
89         let len = self.derive(&mut buf)?;
90         buf.truncate(len);
91         Ok(buf)
92     }
93 }
94 
95 impl<'a> Drop for Deriver<'a> {
drop(&mut self)96     fn drop(&mut self) {
97         unsafe {
98             ffi::EVP_PKEY_CTX_free(self.0);
99         }
100     }
101 }
102 
103 #[cfg(test)]
104 mod test {
105     use super::*;
106 
107     use crate::ec::{EcGroup, EcKey};
108     use crate::nid::Nid;
109     use crate::pkey::PKey;
110 
111     #[test]
derive_without_peer()112     fn derive_without_peer() {
113         let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
114         let ec_key = EcKey::generate(&group).unwrap();
115         let pkey = PKey::from_ec_key(ec_key).unwrap();
116         let mut deriver = Deriver::new(&pkey).unwrap();
117         deriver.derive_to_vec().unwrap_err();
118     }
119 
120     #[test]
test_ec_key_derive()121     fn test_ec_key_derive() {
122         let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
123         let ec_key = EcKey::generate(&group).unwrap();
124         let ec_key2 = EcKey::generate(&group).unwrap();
125         let pkey = PKey::from_ec_key(ec_key).unwrap();
126         let pkey2 = PKey::from_ec_key(ec_key2).unwrap();
127         let mut deriver = Deriver::new(&pkey).unwrap();
128         deriver.set_peer(&pkey2).unwrap();
129         let shared = deriver.derive_to_vec().unwrap();
130         assert!(!shared.is_empty());
131     }
132 }
133