#![forbid(clippy::disallowed_methods, missing_docs, unsafe_code)]
#![cfg_attr(not(test), forbid(unused_crate_dependencies))]
#[macro_use]
mod common;
#[cfg(feature = "curve25519aes128-cbchmac")]
pub mod curve25519aes128_cbchmac;
#[cfg(feature = "curve25519xsalsa20hmac")]
pub mod curve25519xsalsa20hmac;
use cipher::generic_array::GenericArray;
use digest::Mac as _;
use generic_ec::Curve;
use rand_core::{CryptoRng, RngCore};
pub trait Suite {
type E: Curve;
type Mac: digest::OutputSizeUser;
type Enc;
type Dec;
}
pub(crate) type MacSize<S> = <<S as Suite>::Mac as digest::OutputSizeUser>::OutputSize;
pub const fn pad_size<S: Suite>(message_len: usize) -> usize
where
S::Enc: cipher::BlockSizeUser,
{
let block_size = <<S::Enc as cipher::BlockSizeUser>::BlockSize as cipher::Unsigned>::USIZE;
block_size - (message_len % block_size)
}
#[derive(Clone, Debug)]
pub struct PrivateKey<S: Suite> {
pub scalar: generic_ec::NonZero<generic_ec::SecretScalar<S::E>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PublicKey<S: Suite> {
pub point: generic_ec::NonZero<generic_ec::Point<S::E>>,
}
#[derive(Debug, PartialEq)]
pub struct EncryptedMessage<'m, S: Suite> {
pub ephemeral_key: generic_ec::NonZero<generic_ec::Point<S::E>>,
pub message: &'m mut [u8],
pub tag: GenericArray<u8, MacSize<S>>,
}
impl<S: Suite> PrivateKey<S> {
pub fn generate(rng: &mut (impl RngCore + CryptoRng)) -> Self {
let scalar = generic_ec::NonZero::<generic_ec::SecretScalar<S::E>>::random(rng);
Self { scalar }
}
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Option<Self> {
let scalar = generic_ec::SecretScalar::from_be_bytes(bytes.as_ref()).ok()?;
let scalar = generic_ec::NonZero::try_from(scalar).ok()?;
Some(Self { scalar })
}
pub fn to_bytes(&self) -> Vec<u8> {
let scalar: &generic_ec::Scalar<S::E> = self.scalar.as_ref();
scalar.to_be_bytes().to_vec()
}
pub fn public_key(&self) -> PublicKey<S> {
let point = generic_ec::Point::generator() * &self.scalar;
PublicKey { point }
}
}
impl<S: Suite> PublicKey<S> {
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Option<Self> {
let point = generic_ec::Point::<S::E>::from_bytes(bytes).ok()?;
let point = generic_ec::NonZero::<generic_ec::Point<S::E>>::try_from(point).ok()?;
Some(Self { point })
}
pub fn to_bytes(&self) -> Vec<u8> {
self.point.to_bytes(true).to_vec()
}
pub fn stream_encrypt_in_place<'m>(
&self,
message: &'m mut [u8],
rng: &mut (impl RngCore + CryptoRng),
) -> Result<EncryptedMessage<'m, S>, EncError>
where
S::Mac: digest::Mac + cipher::KeyInit,
S::Enc: cipher::KeyIvInit + cipher::StreamCipher,
{
stream_encrypt_in_place::<S, _>(message, &self.point, rng)
}
pub fn block_encrypt_in_place<'m>(
&self,
message: &'m mut [u8],
data_len: usize,
rng: &mut (impl RngCore + CryptoRng),
) -> Result<EncryptedMessage<'m, S>, EncError>
where
S::Mac: digest::Mac + cipher::KeyInit,
S::Enc: cipher::KeyIvInit + cipher::BlockEncryptMut,
{
block_encrypt_in_place::<S, _>(message, data_len, &self.point, rng)
}
pub fn stream_encrypt(
&self,
message: &[u8],
rng: &mut (impl RngCore + CryptoRng),
) -> Result<Vec<u8>, EncError>
where
S::Mac: digest::Mac + cipher::KeyInit,
S::Enc: cipher::KeyIvInit + cipher::StreamCipher,
{
with_copy(message, |msg| self.stream_encrypt_in_place(msg, rng))
}
pub fn block_encrypt(
&self,
message: &[u8],
rng: &mut (impl RngCore + CryptoRng),
) -> Result<Vec<u8>, EncError>
where
S::Mac: digest::Mac + cipher::KeyInit,
S::Enc: cipher::KeyIvInit + cipher::BlockEncryptMut,
{
let key_len = generic_ec::Point::<S::E>::serialized_len(true);
let mac_len = <MacSize<S> as cipher::typenum::Unsigned>::USIZE;
let msg_len = message.len();
let pad_len = pad_size::<S>(msg_len);
eprintln!("encrypting message {} with padding {}", msg_len, pad_len);
let mut bytes = vec![0; key_len + msg_len + pad_len + mac_len];
bytes[key_len..(key_len + msg_len)].copy_from_slice(message);
let message_slice = &mut bytes[key_len..(key_len + msg_len + pad_len)];
let EncryptedMessage {
ephemeral_key, tag, ..
} = self.block_encrypt_in_place(message_slice, msg_len, rng)?;
bytes[..key_len].copy_from_slice(&ephemeral_key.to_bytes(true));
bytes[(key_len + msg_len + pad_len)..].copy_from_slice(&tag);
Ok(bytes)
}
}
impl<S: Suite> PrivateKey<S> {
pub fn stream_decrypt_in_place<'m>(
&self,
message: EncryptedMessage<'m, S>,
) -> Result<&'m mut [u8], DecError>
where
S::Mac: digest::Mac + cipher::KeyInit,
S::Dec: cipher::KeyIvInit + cipher::StreamCipher,
{
stream_decrypt_in_place(message, &self.scalar)
}
pub fn stream_decrypt(&self, message: &EncryptedMessage<'_, S>) -> Result<Vec<u8>, DecError>
where
S::Mac: digest::Mac + cipher::KeyInit,
S::Dec: cipher::KeyIvInit + cipher::StreamCipher,
{
let mut msg_bytes = Vec::with_capacity(message.message.len());
msg_bytes.extend_from_slice(message.message);
let msg = EncryptedMessage {
ephemeral_key: message.ephemeral_key,
tag: message.tag.clone(),
message: &mut msg_bytes,
};
let _ = self.stream_decrypt_in_place(msg)?;
Ok(msg_bytes)
}
pub fn block_decrypt_in_place<'m>(
&self,
message: EncryptedMessage<'m, S>,
) -> Result<&'m mut [u8], DecError>
where
S::Mac: digest::Mac + cipher::KeyInit,
S::Dec: cipher::KeyIvInit + cipher::BlockDecryptMut,
{
block_decrypt_in_place(message, &self.scalar)
}
pub fn block_decrypt(&self, message: &EncryptedMessage<'_, S>) -> Result<Vec<u8>, DecError>
where
S::Mac: digest::Mac + cipher::KeyInit,
S::Dec: cipher::KeyIvInit + cipher::BlockDecryptMut,
{
let mut msg_bytes = Vec::with_capacity(message.message.len());
msg_bytes.extend_from_slice(message.message);
let msg = EncryptedMessage {
ephemeral_key: message.ephemeral_key,
tag: message.tag.clone(),
message: &mut msg_bytes,
};
let s = self.block_decrypt_in_place(msg)?;
let len_without_pad = s.len();
msg_bytes.truncate(len_without_pad);
Ok(msg_bytes)
}
}
fn ecies_kem<E: Curve>(
q: generic_ec::NonZero<generic_ec::Point<E>>,
k: &generic_ec::NonZero<generic_ec::SecretScalar<E>>,
cipher_key: &mut [u8],
mac_key: &mut [u8],
) -> Result<(), hkdf::InvalidLength> {
let z: generic_ec::NonZero<_> = k * q;
let z_bs = z.to_bytes(true);
let kdf = hkdf::Hkdf::<sha2::Sha256>::new(None, &z_bs);
let mut all_bytes = vec![0u8; cipher_key.len() + mac_key.len()];
kdf.expand(b"generic-ecies cipher and mac", &mut all_bytes)?;
let mid = cipher_key.len();
cipher_key.copy_from_slice(&all_bytes[..mid]);
mac_key.copy_from_slice(&all_bytes[mid..]);
Ok(())
}
fn stream_encrypt_in_place<'m, S, R>(
m: &'m mut [u8],
q: &generic_ec::NonZero<generic_ec::Point<S::E>>,
rng: &mut R,
) -> Result<EncryptedMessage<'m, S>, EncError>
where
R: RngCore + CryptoRng,
S: Suite,
S::Mac: digest::Mac + cipher::KeyInit,
S::Enc: cipher::KeyIvInit + cipher::StreamCipher,
{
let k = generic_ec::NonZero::<generic_ec::SecretScalar<S::E>>::random(rng);
let r = generic_ec::Point::generator() * &k;
let mut cipher_key = cipher::Key::<S::Enc>::default();
let mut mac_key = cipher::Key::<S::Mac>::default();
ecies_kem(*q, &k, &mut cipher_key, &mut mac_key).map_err(EncError::Kdf)?;
let cipher_iv = cipher::Iv::<S::Enc>::default();
let mut cipher: S::Enc = cipher::KeyIvInit::new(&cipher_key, &cipher_iv);
let mac: S::Mac = digest::Mac::new(&mac_key);
cipher::StreamCipher::try_apply_keystream(&mut cipher, m).map_err(EncError::StreamEnd)?;
let d = mac.chain_update(&*m).finalize().into_bytes();
Ok(EncryptedMessage {
ephemeral_key: r,
message: m,
tag: d,
})
}
fn block_encrypt_in_place<'m, S: Suite, R>(
m: &'m mut [u8],
data_len: usize,
q: &generic_ec::NonZero<generic_ec::Point<S::E>>,
rng: &mut R,
) -> Result<EncryptedMessage<'m, S>, EncError>
where
R: RngCore + CryptoRng,
S::Mac: digest::Mac + cipher::KeyInit,
S::Enc: cipher::KeyIvInit + cipher::BlockEncryptMut,
{
let k = generic_ec::NonZero::<generic_ec::SecretScalar<S::E>>::random(rng);
let r = generic_ec::Point::generator() * &k;
let mut cipher_key = cipher::Key::<S::Enc>::default();
let mut mac_key = cipher::Key::<S::Mac>::default();
ecies_kem(*q, &k, &mut cipher_key, &mut mac_key).map_err(EncError::Kdf)?;
let cipher_iv = cipher::Iv::<S::Enc>::default();
let cipher: S::Enc = cipher::KeyIvInit::new(&cipher_key, &cipher_iv);
let mac: S::Mac = digest::Mac::new(&mac_key);
cipher::BlockEncryptMut::encrypt_padded_mut::<cipher::block_padding::Pkcs7>(
cipher, m, data_len,
)
.map_err(EncError::PadError)?;
let d = mac.chain_update(&*m).finalize().into_bytes();
Ok(EncryptedMessage {
ephemeral_key: r,
message: m,
tag: d,
})
}
fn stream_decrypt_in_place<'m, S: Suite>(
message: EncryptedMessage<'m, S>,
d: &generic_ec::NonZero<generic_ec::SecretScalar<S::E>>,
) -> Result<&'m mut [u8], DecError>
where
S::Mac: digest::Mac + cipher::KeyInit,
S::Dec: cipher::KeyIvInit + cipher::StreamCipher,
{
let r = message.ephemeral_key;
let m = message.message;
let tag = message.tag;
let mut cipher_key = cipher::Key::<S::Dec>::default();
let mut mac_key = cipher::Key::<S::Mac>::default();
ecies_kem(r, d, &mut cipher_key, &mut mac_key).map_err(DecError::Kdf)?;
let cipher_iv = cipher::Iv::<S::Dec>::default();
let mut cipher: S::Dec = cipher::KeyIvInit::new(&cipher_key, &cipher_iv);
let mac: S::Mac = digest::Mac::new(&mac_key);
mac.chain_update(&*m)
.verify(&tag)
.map_err(DecError::MacInvalid)?;
cipher::StreamCipher::try_apply_keystream(&mut cipher, m).map_err(DecError::StreamEnd)?;
Ok(m)
}
fn block_decrypt_in_place<'m, S: Suite>(
message: EncryptedMessage<'m, S>,
d: &generic_ec::NonZero<generic_ec::SecretScalar<S::E>>,
) -> Result<&'m mut [u8], DecError>
where
S::Mac: digest::Mac + cipher::KeyInit,
S::Dec: cipher::KeyIvInit + cipher::BlockDecryptMut,
{
let r = message.ephemeral_key;
let m = message.message;
let tag = message.tag;
let mut cipher_key = cipher::Key::<S::Dec>::default();
let mut mac_key = cipher::Key::<S::Mac>::default();
ecies_kem(r, d, &mut cipher_key, &mut mac_key).map_err(DecError::Kdf)?;
let cipher_iv = cipher::Iv::<S::Dec>::default();
let cipher: S::Dec = cipher::KeyIvInit::new(&cipher_key, &cipher_iv);
let mac: S::Mac = digest::Mac::new(&mac_key);
mac.chain_update(&*m)
.verify(&tag)
.map_err(DecError::MacInvalid)?;
eprintln!("decrypting length {}", m.len());
let s = cipher::BlockDecryptMut::decrypt_padded_mut::<cipher::block_padding::Pkcs7>(cipher, m)
.map_err(DecError::PadError)?;
let len_without_padding = s.len();
Ok(&mut m[..len_without_padding])
}
impl<'m, S: Suite> EncryptedMessage<'m, S> {
pub fn to_bytes(&self) -> Vec<u8> {
let r = self.ephemeral_key.to_bytes(true);
let mut bytes = Vec::with_capacity(r.len() + self.message.len() + self.tag.len());
bytes.extend_from_slice(&r);
bytes.extend_from_slice(self.message);
bytes.extend_from_slice(&self.tag);
bytes
}
pub fn from_bytes(bytes: &'m mut [u8]) -> Result<Self, DeserializeError> {
let l = bytes.len();
let compressed_len = generic_ec::Point::<S::E>::serialized_len(true);
let (point_len, ephemeral_key) =
match generic_ec::Point::<S::E>::from_bytes(&bytes[..compressed_len]) {
Ok(point) => (compressed_len, point),
Err(e1) => {
let len = generic_ec::Point::<S::E>::serialized_len(false);
match generic_ec::Point::<S::E>::from_bytes(&bytes[..len]) {
Ok(point) => (len, point),
Err(e2) => return Err(DeserializeError::InvalidPoint(e1, e2)),
}
}
};
let ephemeral_key =
generic_ec::NonZero::<generic_ec::Point<S::E>>::try_from(ephemeral_key)?;
let tag_len = GenericArray::<u8, MacSize<S>>::default().len();
let tag = &bytes[(l - tag_len)..];
let tag = GenericArray::<u8, MacSize<S>>::clone_from_slice(tag);
let message = &mut bytes[point_len..(l - tag_len)];
Ok(EncryptedMessage {
ephemeral_key,
message,
tag,
})
}
}
fn with_copy<S: Suite>(
message: &[u8],
run: impl FnOnce(&mut [u8]) -> Result<EncryptedMessage<'_, S>, EncError>,
) -> Result<Vec<u8>, EncError> {
let key_len = generic_ec::Point::<S::E>::serialized_len(true);
let mac_len = <MacSize<S> as cipher::typenum::Unsigned>::USIZE;
let mut bytes = vec![0; key_len + message.len() + mac_len];
let message_slice = &mut bytes[key_len..(key_len + message.len())];
message_slice.copy_from_slice(message);
let EncryptedMessage {
ephemeral_key, tag, ..
} = run(message_slice)?;
bytes[..key_len].copy_from_slice(&ephemeral_key.to_bytes(true));
bytes[(key_len + message.len())..].copy_from_slice(&tag);
Ok(bytes)
}
#[derive(Debug, thiserror::Error)]
pub enum EncError {
#[error("KDF failed: {0}")]
Kdf(hkdf::InvalidLength),
#[error("Key stream end (too much data supplied): {0}")]
StreamEnd(cipher::StreamCipherError),
#[error("Pad error {0}")]
PadError(cipher::inout::PadError),
}
#[derive(Debug, thiserror::Error)]
pub enum DecError {
#[error("MAC verification failed: {0}")]
MacInvalid(digest::MacError),
#[error("KDF failed: {0}")]
Kdf(hkdf::InvalidLength),
#[error("Key stream end (too much data supplied): {0}")]
StreamEnd(cipher::StreamCipherError),
#[error("Pad error {0}")]
PadError(cipher::block_padding::UnpadError),
}
#[derive(Debug, thiserror::Error)]
pub enum DeserializeError {
#[error("Ephemeral DH key is invalid: {0}; {1}")]
InvalidPoint(
generic_ec::errors::InvalidPoint,
generic_ec::errors::InvalidPoint,
),
#[error("Ephemeral DH key is zero")]
ZeroPoint(#[from] generic_ec::errors::ZeroPoint),
}