With so many crates available, I doubt the one for composing an encrypted S/MIME message would be missing. I'm aware of pgp which should handle PGP/MIME. I'm also aware of lettre_email emailmessage and mailparse mail-core which could be used to compose a MIME e-mail message...
If there isn't one, I'm asking if someone already does this so I can copy cat and perhaps publish. Or else I'll be battling with it myself and will appreciate a good head start.
The goal is to encrypt messages at rest while stored on a mail server Samotop. Knowing the recipient's public key, I should be able to wrap-encrypt any incoming message for that recipient so that only the user owning the key will be able to decrypt the message. It may well be that S/MIME is not the right fit for this but I'd fancy to make this usable with existing e-mail clients with S/MIME support.
To start off, I suppose there will be a symmetric key that encrypts the message and this key will be encrypted using asymmetric key for the recipient (potentially for multiple recipients) and included in the payload. Here is a sketch of my ideas.
Random symmetric key is made:
let mut key = [0u8; 32];
SystemRandom::new().fill(&mut key).unwrap();
Content gets encrypted:
// Sealing key used to encrypt data
let mut sealing_key = SealingKey::new(
UnboundKey::new(&CHACHA20_POLY1305, key.as_ref()).unwrap(),
Nonces::new(5),
);
// Encrypt data into in_out variable
sealing_key
.seal_in_place_append_tag(Aad::empty(), &mut encrypted)
.unwrap();
Symmetric key gets asymmetrically encrypted for the recipient:
let enc_key = pub_key.encrypt(&mut rng, PaddingScheme::new_pkcs1v15_encrypt(), &key[..]).expect("failed to encrypt");
assert_ne!(&key[..], &enc_key[..]);
Now comes the time to compose the encrypted MIME part... ideas? crates? reference implementations? rfc8551
I figured this much. If I have to depend on openssl, I might just as well depend on it externally, running it as a child process. This will enable async io streaming as a bonus which doesn't seem to be supported by rust bindings (yet?).
fn main() -> Result<(), Box<dyn std::error::Error>> {
async_std::task::block_on(main_fut())
}
async fn main_fut() -> Result<(), Box<dyn std::error::Error>> {
let mut sign = async_process::Command::new("openssl")
.arg("smime")
.arg("-stream")
.arg("-in")
.arg("test/msg")
.arg("-sign")
.arg("-inkey")
.arg("test/my.key")
.arg("-signer")
.arg("test/my.crt")
.kill_on_drop(true)
.reap_on_drop(true)
.stdout(async_process::Stdio::piped())
.spawn()?;
let mut encrypt = async_process::Command::new("openssl")
.arg("smime")
.arg("-stream")
.arg("-out")
.arg("test/enc")
.arg("-encrypt")
.arg("test/her.crt")
.kill_on_drop(true)
.reap_on_drop(true)
.stdin(async_process::Stdio::piped())
.spawn()?;
let pipe = async_std::io::copy(
sign.stdout.as_mut().expect("sign output"),
encrypt.stdin.as_mut().expect("encrypt input"),
);
pipe.await?;
println!("sign: {:?}", sign.status().await);
println!("encrypt: {:?}", encrypt.status().await);
Ok(())
}