srsa/lib.rs
1//! A crate that uses the already well established [rsa](https://docs.rs/rsa/0.8.2/rsa/) and
2//! [pkcs8](https://docs.rs/pkcs8/0.10.2/pkcs8/) crates to provide a simple plug
3//! and play experience.
4//!
5//! # Usage
6//!
7//! ## Saving key pairs
8//! ```
9//! use srsa::Keys;
10//! use anyhow::Result as AnyResult;
11//!
12//! fn main() -> AnyResult<()> {
13//! // The values passed in will be the file names of the private and public keys.
14//! let keys = Keys::new("priv", "pub");
15//!
16//! // Saves the key pairs to a folder in the cwd called keys and encrypts the private key with a
17//! // password
18//! keys.write_to_disk("password", "keys")?;
19//!
20//! Ok(())
21//! }
22//! ```
23//!
24//! ## Using an existing key pair
25//! ```rust
26//! use srsa::Keys;
27//! use anyhow::Result as AnyResult;
28//!
29//! fn main() -> AnyResult<()> {
30//! let keys = Keys::retrieve_keys("keys/test_priv", "1234", "keys/test_pub")?;
31//! let ciphertext = keys.seal(b"hi")?;
32//! let plaintext = keys.unseal(&ciphertext)?;
33//! Ok(())
34//! }
35//! ```
36//!
37//! Encrypting and decrypt things work very similarly.
38//! 1. You retrieve the neccessary keys to do the job.
39//! 2. You run the appropriate function. The encryption function called `seal`, the decryption
40//! function is called `unseal`.
41
42use std::fs;
43
44pub mod errors;
45mod test;
46
47use anyhow::{Context, Result as AnyResult};
48use errors::KeyError;
49use pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey, LineEnding};
50use rsa::{Pkcs1v15Encrypt, PublicKey, RsaPrivateKey, RsaPublicKey};
51
52/// Contains the key pairs and their names. This struct is to use the key pairs and to retrieve
53/// them. It can also be used to create new key pairs.
54pub struct Keys<'names> {
55 priv_key: Option<RsaPrivateKey>,
56 priv_key_name: &'names str,
57 pub_key: Option<RsaPublicKey>,
58 pub_key_name: &'names str,
59}
60
61impl<'names> Keys<'names> {
62 /// Creates key pairs.
63 /// # Parameters
64 /// - `priv_key_name` & `pub_key_name`: The name of the keys. If the keys are saved to disk
65 /// with `self.write_to_disk` the values for these variables will be the file name.
66 pub fn new(priv_key_name: &'names str, pub_key_name: &'names str) -> Self {
67 let mut rng = rand::thread_rng();
68
69 let bits = 2048;
70 let priv_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
71 let pub_key = RsaPublicKey::from(&priv_key);
72
73 Self {
74 priv_key: Some(priv_key),
75 priv_key_name,
76 pub_key: Some(pub_key),
77 pub_key_name,
78 }
79 }
80
81 /// Will read the PEM encoded public and private keys and return `Self`.
82 /// If you only want a specific key look at their respective function.
83 /// - `retrieve_private_key`
84 /// - `retrieve_public_key`
85 ///
86 /// # Parameters
87 /// - `priv_key`: Path to private key file.
88 /// - `password`: The password used to encrypt the private key.
89 /// - `pub_key`: Path to public key file.
90 pub fn retrieve_keys(
91 priv_key_path: &'names str,
92 password: &str,
93 pub_key_path: &'names str,
94 ) -> AnyResult<Self> {
95 // Get *encrypted* private key first
96 let mut pem = fs::read_to_string(priv_key_path)?;
97
98 // Decrypt encrypted private key
99 let priv_key = RsaPrivateKey::from_pkcs8_encrypted_pem(&pem, password)?;
100 pem.clear();
101
102 // Then get public key
103 pem = fs::read_to_string(pub_key_path)?;
104 let pub_key = RsaPublicKey::from_public_key_pem(&pem)?;
105
106 Ok(Self {
107 priv_key: Some(priv_key),
108 priv_key_name: priv_key_path,
109 pub_key: Some(pub_key),
110 pub_key_name: pub_key_path,
111 })
112 }
113
114 pub fn retrieve_private_key(priv_key_path: &'names str, password: &str) -> AnyResult<Self> {
115 let pem = fs::read_to_string(priv_key_path)?;
116 let private_key = RsaPrivateKey::from_pkcs8_encrypted_pem(&pem, password)?;
117
118 Ok(Self {
119 priv_key: Some(private_key),
120 priv_key_name: priv_key_path,
121 pub_key: None,
122 pub_key_name: "",
123 })
124 }
125}
126
127impl Keys<'_> {
128 /// Will further encrypt the `self.priv_key` before writing it to disk.
129 /// Both keys will be PEM encoded.
130 /// # Parameters
131 /// - `priv_key_pass`: The password used to encrypt the private key.
132 /// - `folder`: The folder to write the keys to. If left empty will default to cwd.
133 pub fn write_to_disk(&self, priv_key_pass: &str, folder: &str) -> AnyResult<()> {
134 let folder = if folder.is_empty() { "." } else { folder };
135
136 let priv_key_pem = &self
137 .priv_key
138 .as_ref()
139 .ok_or(KeyError::UnableToUnpackKey)?
140 .to_pkcs8_encrypted_pem(&mut rand::thread_rng(), priv_key_pass, LineEnding::LF)?;
141
142 let pub_key_pem = &self
143 .pub_key
144 .as_ref()
145 .ok_or(KeyError::UnableToUnpackKey)?
146 .to_public_key_pem(LineEnding::LF)?;
147
148 let priv_key_path = format!("{}/{}", folder, self.priv_key_name);
149 fs::write(priv_key_path, priv_key_pem.as_bytes())?;
150
151 let pub_key_path = format!("{}/{}", folder, self.pub_key_name);
152 fs::write(pub_key_path, pub_key_pem.as_bytes())?;
153
154 Ok(())
155 }
156
157 pub fn seal(&self, plaintext: &[u8]) -> AnyResult<Vec<u8>> {
158 let mut rng = rand::thread_rng();
159 self.pub_key
160 .clone()
161 .ok_or(KeyError::UnableToUnpackKey)?
162 .encrypt(&mut rng, Pkcs1v15Encrypt, plaintext)
163 .context("Unable to encrypt plaintext with public key, you should take a look at it.")
164 }
165
166 pub fn unseal(&self, ciphertext: &[u8]) -> AnyResult<Vec<u8>> {
167 self.priv_key
168 .clone()
169 .ok_or(KeyError::UnableToUnpackKey)?
170 .decrypt(Pkcs1v15Encrypt, ciphertext)
171 .context("Unable to decrypt ciphertext with private key, you should take a look at it.")
172 }
173}