2020-01-05 16:11:00 +00:00
|
|
|
use crate::crypto::ToSodiumObject;
|
|
|
|
|
2020-08-12 07:55:13 +00:00
|
|
|
use sodiumoxide::crypto::{
|
|
|
|
scalarmult::curve25519,
|
|
|
|
secretbox,
|
|
|
|
sign::{ed25519, SecretKey},
|
|
|
|
};
|
2020-01-05 22:15:43 +00:00
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
use super::error::{Error, Result};
|
2020-01-05 16:11:00 +00:00
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
pub const SUFFIX: &str = ".box";
|
2020-01-05 16:11:00 +00:00
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
pub const MAX_RECIPIENTS: u8 = 7;
|
2020-01-05 16:11:00 +00:00
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
const RECIPIENT_COUNT_LEN: usize = 1;
|
|
|
|
const ENCRYPTED_HEADER_LEN: usize = RECIPIENT_COUNT_LEN + secretbox::KEYBYTES + secretbox::MACBYTES;
|
2020-01-05 16:11:00 +00:00
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
pub fn is_privatebox(text: &str) -> bool {
|
|
|
|
text.ends_with(SUFFIX)
|
2020-01-05 16:11:00 +00:00
|
|
|
}
|
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
pub fn privatebox_cipher(plaintext: &str, recipients: &[&str]) -> Result<String> {
|
|
|
|
let recipients: crate::crypto::Result<Vec<_>> = recipients
|
2020-01-07 16:45:09 +00:00
|
|
|
.iter()
|
2020-02-14 15:39:31 +00:00
|
|
|
.map(|id| id[1..].to_ed25519_pk())
|
|
|
|
.collect();
|
|
|
|
|
2020-01-05 22:15:43 +00:00
|
|
|
let recipients = recipients?;
|
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
let recipients_ref: Vec<_> = recipients.iter().map(|r| r).collect();
|
2020-01-05 16:11:00 +00:00
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
let ciphertext = cipher(plaintext.as_bytes(), &recipients_ref[..])?;
|
2020-01-05 16:11:00 +00:00
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
Ok(format!("{}{}", base64::encode(&ciphertext), SUFFIX))
|
|
|
|
}
|
2020-01-05 16:11:00 +00:00
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
pub fn privatebox_decipher(ciphertext: &str, sk: &SecretKey) -> Result<Option<String>> {
|
|
|
|
let msg = &ciphertext.as_bytes()[..ciphertext.len() - SUFFIX.len()];
|
2020-01-05 22:15:43 +00:00
|
|
|
let msg = base64::decode(msg)?;
|
2020-01-05 16:11:00 +00:00
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
let plaintext = decipher(&msg, &sk)?.map(|msg| String::from_utf8_lossy(&msg).to_string());
|
2020-01-05 16:11:00 +00:00
|
|
|
|
|
|
|
Ok(plaintext)
|
2020-02-14 15:39:31 +00:00
|
|
|
}
|
2020-01-05 16:11:00 +00:00
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
fn cipher(plaintext: &[u8], recipients: &[&ed25519::PublicKey]) -> Result<Box<[u8]>> {
|
2020-01-05 16:11:00 +00:00
|
|
|
// Precondition checks
|
|
|
|
if plaintext.is_empty() {
|
2020-01-05 22:15:43 +00:00
|
|
|
return Err(Error::EmptyPlaintext);
|
2020-01-05 16:11:00 +00:00
|
|
|
}
|
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
if recipients.is_empty() || recipients.len() > MAX_RECIPIENTS as usize {
|
2020-01-05 22:15:43 +00:00
|
|
|
return Err(Error::BadRecipientCount);
|
2020-01-05 16:11:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generated Curve25519 key pair to encrypt message header
|
|
|
|
let (h_pk, h_sk) = ed25519::gen_keypair();
|
|
|
|
|
|
|
|
// Generated random 32-byte secret key used to encrypt the message body
|
|
|
|
let y = sodiumoxide::crypto::secretbox::gen_key();
|
2020-02-14 15:39:31 +00:00
|
|
|
|
2020-01-05 16:11:00 +00:00
|
|
|
// Encrypt the plaintext with y, with a random nonce
|
|
|
|
let nonce = secretbox::gen_nonce();
|
|
|
|
let cipher_message = secretbox::seal(plaintext, &nonce, &y);
|
|
|
|
|
|
|
|
// The sender uses scalar multiplication to derive a shared secret for each recipient,
|
|
|
|
// and encrypts (number_of_recipents || y) for each one
|
|
|
|
let h_sk_scalar = &h_sk.to_curve25519();
|
2020-02-14 15:39:31 +00:00
|
|
|
let mut plain_header = [0u8; RECIPIENT_COUNT_LEN + secretbox::KEYBYTES];
|
|
|
|
plain_header[0] = recipients.len() as u8;
|
2020-01-07 16:45:09 +00:00
|
|
|
plain_header[RECIPIENT_COUNT_LEN..].copy_from_slice(&y[..]);
|
2020-01-05 16:11:00 +00:00
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
let mut buffer: Vec<u8> = Vec::with_capacity(
|
|
|
|
secretbox::NONCEBYTES
|
|
|
|
+ ed25519::PUBLICKEYBYTES
|
|
|
|
+ ENCRYPTED_HEADER_LEN * recipients.len()
|
|
|
|
+ secretbox::MACBYTES
|
|
|
|
+ plaintext.len(),
|
2020-01-05 16:11:00 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
buffer.extend_from_slice(&nonce[..]);
|
|
|
|
buffer.extend_from_slice(&h_pk.to_curve25519()[..]);
|
|
|
|
|
|
|
|
for recipient in recipients {
|
|
|
|
let key = curve25519::scalarmult(&h_sk_scalar, &recipient.to_curve25519())
|
2020-01-05 22:15:43 +00:00
|
|
|
.map_err(|_| Error::CryptoScalarMultFailed)?;
|
2020-01-05 16:11:00 +00:00
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
let key = secretbox::Key::from_slice(&key[..]).ok_or(Error::CryptoKeyFromGrupFailed)?;
|
2020-01-05 16:11:00 +00:00
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
buffer.extend_from_slice(&secretbox::seal(&plain_header[..], &nonce, &key));
|
2020-01-05 16:11:00 +00:00
|
|
|
}
|
|
|
|
buffer.extend_from_slice(&cipher_message[..]);
|
|
|
|
|
|
|
|
Ok(buffer.into_boxed_slice())
|
|
|
|
}
|
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
fn decipher(ciphertext: &[u8], sk: &SecretKey) -> Result<Option<Vec<u8>>> {
|
2020-01-05 16:11:00 +00:00
|
|
|
let mut cursor = ciphertext;
|
2020-02-14 15:39:31 +00:00
|
|
|
|
2020-01-05 16:11:00 +00:00
|
|
|
let nonce = secretbox::Nonce::from_slice(&cursor[..secretbox::NONCEBYTES])
|
2020-01-05 22:15:43 +00:00
|
|
|
.ok_or(Error::CannotReadNonce)?;
|
2020-01-05 16:11:00 +00:00
|
|
|
cursor = &cursor[secretbox::NONCEBYTES..];
|
|
|
|
|
|
|
|
let h_pk = curve25519::GroupElement::from_slice(&cursor[..ed25519::PUBLICKEYBYTES])
|
2020-01-05 22:15:43 +00:00
|
|
|
.ok_or(Error::CannotReadNonce)?;
|
2020-01-05 16:11:00 +00:00
|
|
|
cursor = &cursor[ed25519::PUBLICKEYBYTES..];
|
2020-02-14 15:39:31 +00:00
|
|
|
|
2020-01-05 16:11:00 +00:00
|
|
|
let key = curve25519::scalarmult(&sk.to_curve25519(), &h_pk)
|
|
|
|
.and_then(|key| secretbox::Key::from_slice(&key[..]).ok_or(()))
|
2020-01-05 22:15:43 +00:00
|
|
|
.map_err(|_| Error::CannotCreateKey)?;
|
2020-01-05 16:11:00 +00:00
|
|
|
|
|
|
|
let mut header_no = 0;
|
2020-02-14 15:39:31 +00:00
|
|
|
while header_no < MAX_RECIPIENTS && cursor.len() > ENCRYPTED_HEADER_LEN + secretbox::MACBYTES {
|
2020-01-05 16:11:00 +00:00
|
|
|
if let Ok(header) = secretbox::open(&cursor[..ENCRYPTED_HEADER_LEN], &nonce, &key) {
|
2020-02-14 15:39:31 +00:00
|
|
|
let encrypted_message_offset = ENCRYPTED_HEADER_LEN * (header[0] - header_no) as usize;
|
|
|
|
let y = secretbox::Key::from_slice(&header[1..]).ok_or(Error::CannotCreateKey)?;
|
2020-01-05 16:11:00 +00:00
|
|
|
let plaintext = secretbox::open(&cursor[encrypted_message_offset..], &nonce, &y)
|
2020-01-05 22:15:43 +00:00
|
|
|
.map_err(|_| Error::FailedToDecipher)?;
|
2020-01-05 16:11:00 +00:00
|
|
|
return Ok(Some(plaintext));
|
|
|
|
}
|
|
|
|
header_no += 1;
|
|
|
|
cursor = &cursor[49..];
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
2020-02-26 18:18:07 +00:00
|
|
|
use crate::keystore::OwnedIdentity;
|
2020-02-14 15:39:31 +00:00
|
|
|
|
2020-01-05 16:11:00 +00:00
|
|
|
#[test]
|
2020-01-05 22:15:43 +00:00
|
|
|
fn test_msg_cipher_to_one() -> Result<()> {
|
2020-01-05 16:11:00 +00:00
|
|
|
let (u1_pk, u1_sk) = ed25519::gen_keypair();
|
|
|
|
let plaintext = "hola".as_bytes();
|
|
|
|
let ciphertext = cipher(plaintext, &[&u1_pk])?;
|
|
|
|
let plaintext_1 = decipher(&ciphertext, &u1_sk)?.unwrap();
|
2020-02-14 15:39:31 +00:00
|
|
|
assert_eq!(plaintext.to_vec(), plaintext_1);
|
2020-01-05 16:11:00 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-01-05 22:15:43 +00:00
|
|
|
fn test_msg_cipher_to_one_helper() -> Result<()> {
|
2020-02-26 18:46:11 +00:00
|
|
|
let id = OwnedIdentity::create();
|
2020-01-05 16:11:00 +00:00
|
|
|
let plaintext = "holar";
|
|
|
|
let ciphertext = privatebox_cipher(plaintext, &[&id.id])?;
|
2020-02-14 15:39:31 +00:00
|
|
|
assert_eq!(is_privatebox(&ciphertext), true);
|
2020-01-05 16:11:00 +00:00
|
|
|
let plaintext_1 = privatebox_decipher(&ciphertext, &id.sk)?.unwrap();
|
2020-02-14 15:39:31 +00:00
|
|
|
assert_eq!(plaintext, plaintext_1);
|
2020-01-05 16:11:00 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-01-05 22:15:43 +00:00
|
|
|
fn test_msg_cipher_to_none() -> Result<()> {
|
2020-01-05 16:11:00 +00:00
|
|
|
let (u1_pk, _) = ed25519::gen_keypair();
|
|
|
|
let (_, u1_sk) = ed25519::gen_keypair();
|
|
|
|
let plaintext = "hola".as_bytes();
|
|
|
|
let ciphertext = cipher(plaintext, &[&u1_pk])?;
|
|
|
|
let plaintext_1 = decipher(&ciphertext, &u1_sk)?;
|
2020-02-14 15:39:31 +00:00
|
|
|
assert_eq!(None, plaintext_1);
|
2020-01-05 16:11:00 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-01-05 22:15:43 +00:00
|
|
|
fn test_msg_cipher_to_multiple() -> Result<()> {
|
2020-01-05 16:11:00 +00:00
|
|
|
let u = (0..7).map(|_| ed25519::gen_keypair()).collect::<Vec<_>>();
|
2020-02-14 15:39:31 +00:00
|
|
|
let u_pk = u.iter().map(|(pk, _)| pk).collect::<Vec<_>>();
|
2020-01-05 16:11:00 +00:00
|
|
|
|
|
|
|
let plaintext = "hola".as_bytes();
|
|
|
|
let ciphertext = cipher(plaintext, &u_pk)?;
|
|
|
|
|
2020-02-14 15:39:31 +00:00
|
|
|
for (_, sk) in u.iter() {
|
2020-01-05 16:11:00 +00:00
|
|
|
let plaintext_1 = decipher(&ciphertext, sk)?.unwrap();
|
2020-02-14 15:39:31 +00:00
|
|
|
assert_eq!(plaintext.to_vec(), plaintext_1);
|
2020-01-05 16:11:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|