summaryrefslogtreecommitdiff
path: root/openssl
diff options
context:
space:
mode:
authorEdoardo Morandi <morandidodo@gmail.com>2020-12-04 15:15:39 +0100
committerEdoardo Morandi <morandidodo@gmail.com>2020-12-04 15:17:59 +0100
commitacfda134eab8049940a5fe04fba5f34a0e0dab7b (patch)
tree1b38dde1061386bd74126a5b4ee145f69037818e /openssl
parent624effd3a6314ddfa4abbbf3b1eddf414edf8842 (diff)
downloadrust-openssl-acfda134eab8049940a5fe04fba5f34a0e0dab7b.zip
Add encrypt module and RSA OAEP support
Diffstat (limited to 'openssl')
-rw-r--r--openssl/src/encrypt.rs342
-rw-r--r--openssl/src/lib.rs1
2 files changed, 343 insertions, 0 deletions
diff --git a/openssl/src/encrypt.rs b/openssl/src/encrypt.rs
new file mode 100644
index 00000000..909bf6bd
--- /dev/null
+++ b/openssl/src/encrypt.rs
@@ -0,0 +1,342 @@
+use std::{marker::PhantomData, ptr};
+
+use error::ErrorStack;
+use foreign_types::ForeignTypeRef;
+use hash::MessageDigest;
+use pkey::{HasPrivate, HasPublic, PKeyRef};
+use rsa::Padding;
+use {cvt, cvt_p};
+
+pub struct Encrypter<'a> {
+ pctx: *mut ffi::EVP_PKEY_CTX,
+ _p: PhantomData<&'a ()>,
+}
+
+unsafe impl<'a> Sync for Encrypter<'a> {}
+unsafe impl<'a> Send for Encrypter<'a> {}
+
+impl<'a> Drop for Encrypter<'a> {
+ fn drop(&mut self) {
+ unsafe {
+ ffi::EVP_PKEY_CTX_free(self.pctx);
+ }
+ }
+}
+
+impl<'a> Encrypter<'a> {
+ /// Creates a new `Encrypter`.
+ ///
+ /// OpenSSL documentation at [`EVP_PKEY_encrypt_init`].
+ ///
+ /// [`EVP_PKEY_encrypt_init`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_encrypt_init.html
+ pub fn new<T>(pkey: &'a PKeyRef<T>) -> Result<Encrypter<'a>, ErrorStack>
+ where
+ T: HasPublic,
+ {
+ Self::new_intern(pkey)
+ }
+
+ fn new_intern<T>(pkey: &'a PKeyRef<T>) -> Result<Encrypter<'a>, ErrorStack>
+ where
+ T: HasPublic,
+ {
+ unsafe {
+ ffi::init();
+
+ let pctx = cvt_p(ffi::EVP_PKEY_CTX_new(pkey.as_ptr(), ptr::null_mut()))?;
+ let r = ffi::EVP_PKEY_encrypt_init(pctx);
+ if r != 1 {
+ ffi::EVP_PKEY_CTX_free(pctx);
+ return Err(ErrorStack::get());
+ }
+
+ Ok(Encrypter {
+ pctx,
+ _p: PhantomData,
+ })
+ }
+ }
+
+ /// Returns the RSA padding mode in use.
+ ///
+ /// This is only useful for RSA keys.
+ ///
+ /// This corresponds to `EVP_PKEY_CTX_get_rsa_padding`.
+ pub fn rsa_padding(&self) -> Result<Padding, ErrorStack> {
+ unsafe {
+ let mut pad = 0;
+ cvt(ffi::EVP_PKEY_CTX_get_rsa_padding(self.pctx, &mut pad))
+ .map(|_| Padding::from_raw(pad))
+ }
+ }
+
+ /// Sets the RSA padding mode.
+ ///
+ /// This is only useful for RSA keys.
+ ///
+ /// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`].
+ ///
+ /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_padding.html
+ pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EVP_PKEY_CTX_set_rsa_padding(
+ self.pctx,
+ padding.as_raw(),
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the RSA MGF1 algorithm.
+ ///
+ /// This is only useful for RSA keys.
+ ///
+ /// This corresponds to [`EVP_PKEY_CTX_set_rsa_mgf1_md`].
+ ///
+ /// [`EVP_PKEY_CTX_set_rsa_mgf1_md`]: https://www.openssl.org/docs/manmaster/man7/RSA-PSS.html
+ pub fn set_rsa_mgf1_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EVP_PKEY_CTX_set_rsa_mgf1_md(
+ self.pctx,
+ md.as_ptr() as *mut _,
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the RSA OAEP algorithm.
+ ///
+ /// This is only useful for RSA keys.
+ ///
+ /// This corresponds to [`EVP_PKEY_CTX_set_rsa_oaep_md`].
+ ///
+ /// [`EVP_PKEY_CTX_set_rsa_oaep_md`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_rsa_oaep_md.html
+ pub fn set_rsa_oaep_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EVP_PKEY_CTX_set_rsa_oaep_md(
+ self.pctx,
+ md.as_ptr() as *mut _,
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Performs public key encryption.
+ ///
+ /// This corresponds to [`EVP_PKEY_encrypt`].
+ ///
+ /// [`EVP_PKEY_encrypt`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_encrypt.html
+ pub fn encrypt(&self, from: &[u8], to: &mut [u8]) -> Result<usize, ErrorStack> {
+ let mut written = to.len();
+ unsafe {
+ cvt(ffi::EVP_PKEY_encrypt(
+ self.pctx,
+ to.as_mut_ptr(),
+ &mut written,
+ from.as_ptr(),
+ from.len(),
+ ))?;
+ }
+
+ Ok(written)
+ }
+}
+pub struct Decrypter<'a> {
+ pctx: *mut ffi::EVP_PKEY_CTX,
+ _p: PhantomData<&'a ()>,
+}
+
+unsafe impl<'a> Sync for Decrypter<'a> {}
+unsafe impl<'a> Send for Decrypter<'a> {}
+
+impl<'a> Drop for Decrypter<'a> {
+ fn drop(&mut self) {
+ unsafe {
+ ffi::EVP_PKEY_CTX_free(self.pctx);
+ }
+ }
+}
+
+impl<'a> Decrypter<'a> {
+ /// Creates a new `Decrypter`.
+ ///
+ /// OpenSSL documentation at [`EVP_PKEY_decrypt_init`].
+ ///
+ /// [`EVP_PKEY_decrypt_init`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_decrypt_init.html
+ pub fn new<T>(pkey: &'a PKeyRef<T>) -> Result<Decrypter<'a>, ErrorStack>
+ where
+ T: HasPrivate,
+ {
+ Self::new_intern(pkey)
+ }
+
+ fn new_intern<T>(pkey: &'a PKeyRef<T>) -> Result<Decrypter<'a>, ErrorStack>
+ where
+ T: HasPrivate,
+ {
+ unsafe {
+ ffi::init();
+
+ let pctx = cvt_p(ffi::EVP_PKEY_CTX_new(pkey.as_ptr(), ptr::null_mut()))?;
+ let r = ffi::EVP_PKEY_decrypt_init(pctx);
+ if r != 1 {
+ ffi::EVP_PKEY_CTX_free(pctx);
+ return Err(ErrorStack::get());
+ }
+
+ Ok(Decrypter {
+ pctx,
+ _p: PhantomData,
+ })
+ }
+ }
+
+ /// Returns the RSA padding mode in use.
+ ///
+ /// This is only useful for RSA keys.
+ ///
+ /// This corresponds to `EVP_PKEY_CTX_get_rsa_padding`.
+ pub fn rsa_padding(&self) -> Result<Padding, ErrorStack> {
+ unsafe {
+ let mut pad = 0;
+ cvt(ffi::EVP_PKEY_CTX_get_rsa_padding(self.pctx, &mut pad))
+ .map(|_| Padding::from_raw(pad))
+ }
+ }
+
+ /// Sets the RSA padding mode.
+ ///
+ /// This is only useful for RSA keys.
+ ///
+ /// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`].
+ ///
+ /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_padding.html
+ pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EVP_PKEY_CTX_set_rsa_padding(
+ self.pctx,
+ padding.as_raw(),
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the RSA MGF1 algorithm.
+ ///
+ /// This is only useful for RSA keys.
+ ///
+ /// This corresponds to [`EVP_PKEY_CTX_set_rsa_mgf1_md`].
+ ///
+ /// [`EVP_PKEY_CTX_set_rsa_mgf1_md`]: https://www.openssl.org/docs/manmaster/man7/RSA-PSS.html
+ pub fn set_rsa_mgf1_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EVP_PKEY_CTX_set_rsa_mgf1_md(
+ self.pctx,
+ md.as_ptr() as *mut _,
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Sets the RSA OAEP algorithm.
+ ///
+ /// This is only useful for RSA keys.
+ ///
+ /// This corresponds to [`EVP_PKEY_CTX_set_rsa_oaep_md`].
+ ///
+ /// [`EVP_PKEY_CTX_set_rsa_oaep_md`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_rsa_oaep_md.html
+ pub fn set_rsa_oaep_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EVP_PKEY_CTX_set_rsa_oaep_md(
+ self.pctx,
+ md.as_ptr() as *mut _,
+ ))
+ .map(|_| ())
+ }
+ }
+
+ /// Performs public key decryption.
+ ///
+ /// This corresponds to [`EVP_PKEY_decrypt`].
+ ///
+ /// [`EVP_PKEY_decrypt`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_decrypt.html
+ pub fn decrypt(&self, from: &[u8], to: &mut [u8]) -> Result<usize, ErrorStack> {
+ let mut written = to.len();
+ unsafe {
+ cvt(ffi::EVP_PKEY_decrypt(
+ self.pctx,
+ to.as_mut_ptr(),
+ &mut written,
+ from.as_ptr(),
+ from.len(),
+ ))?;
+ }
+
+ Ok(written)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use hex::FromHex;
+
+ use encrypt::{Decrypter, Encrypter};
+ use hash::MessageDigest;
+ use pkey::PKey;
+ use rsa::{Padding, Rsa};
+
+ const INPUT: &str =
+ "65794a68624763694f694a53557a49314e694a392e65794a7063334d694f694a71623255694c41304b49434a6c\
+ 654841694f6a457a4d4441344d546b7a4f44417344516f67496d6830644841364c79396c654746746347786c4c\
+ 6d4e76625339706331397962323930496a7030636e566c6651";
+
+ #[test]
+ fn rsa_encrypt_decrypt() {
+ let key = include_bytes!("../test/rsa.pem");
+ let private_key = Rsa::private_key_from_pem(key).unwrap();
+ let pkey = PKey::from_rsa(private_key).unwrap();
+
+ let mut encrypter = Encrypter::new(&pkey).unwrap();
+ encrypter.set_rsa_padding(Padding::PKCS1).unwrap();
+ let input = Vec::from_hex(INPUT).unwrap();
+ let mut encoded = vec![0u8; INPUT.len() * 3];
+ let encoded_len = encrypter.encrypt(&input, &mut encoded).unwrap();
+ let encoded = &encoded[..encoded_len];
+
+ let mut decrypter = Decrypter::new(&pkey).unwrap();
+ decrypter.set_rsa_padding(Padding::PKCS1).unwrap();
+ let mut decoded = vec![0u8; encoded.len()];
+ let decoded_len = decrypter.decrypt(&encoded, &mut decoded).unwrap();
+ let decoded = &decoded[..decoded_len];
+
+ assert_eq!(decoded, input);
+ }
+
+ #[test]
+ fn rsa_encrypt_decrypt_with_sha256() {
+ let key = include_bytes!("../test/rsa.pem");
+ let private_key = Rsa::private_key_from_pem(key).unwrap();
+ let pkey = PKey::from_rsa(private_key).unwrap();
+
+ let md = MessageDigest::sha256();
+
+ let mut encrypter = Encrypter::new(&pkey).unwrap();
+ encrypter.set_rsa_padding(Padding::PKCS1_OAEP).unwrap();
+ encrypter.set_rsa_oaep_md(md).unwrap();
+ encrypter.set_rsa_mgf1_md(md).unwrap();
+ let input = Vec::from_hex(INPUT).unwrap();
+ let mut encoded = vec![0u8; INPUT.len() * 3];
+ let encoded_len = encrypter.encrypt(&input, &mut encoded).unwrap();
+ let encoded = &encoded[..encoded_len];
+
+ let mut decrypter = Decrypter::new(&pkey).unwrap();
+ decrypter.set_rsa_padding(Padding::PKCS1_OAEP).unwrap();
+ decrypter.set_rsa_oaep_md(md).unwrap();
+ decrypter.set_rsa_mgf1_md(md).unwrap();
+ let mut decoded = vec![0u8; encoded.len()];
+ let decoded_len = decrypter.decrypt(&encoded, &mut decoded).unwrap();
+ let decoded = &decoded[..decoded_len];
+
+ assert_eq!(decoded, input);
+ }
+}
diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs
index b4e647c0..5389ed59 100644
--- a/openssl/src/lib.rs
+++ b/openssl/src/lib.rs
@@ -151,6 +151,7 @@ pub mod dh;
pub mod dsa;
pub mod ec;
pub mod ecdsa;
+pub mod encrypt;
pub mod envelope;
pub mod error;
pub mod ex_data;