#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
)]
#![warn(missing_docs, rust_2018_idioms)]
#![cfg_attr(all(feature = "getrandom", feature = "std"), doc = "```")]
#![cfg_attr(not(all(feature = "getrandom", feature = "std")), doc = "```ignore")]
#![cfg_attr(
all(feature = "chacha20", feature = "getrandom", feature = "std"),
doc = "```"
)]
#![cfg_attr(
not(all(feature = "chacha20", feature = "getrandom", feature = "std")),
doc = "```ignore"
)]
#[cfg(feature = "seal")]
extern crate alloc;
mod public_key;
mod secret_key;
pub use crate::{public_key::PublicKey, secret_key::SecretKey};
pub use aead;
pub use crypto_secretbox::Nonce;
use aead::{
consts::{U0, U16, U24, U32, U8},
generic_array::GenericArray,
AeadCore, AeadInPlace, Buffer, Error, KeyInit,
};
use crypto_secretbox::{
cipher::{IvSizeUser, KeyIvInit, KeySizeUser, StreamCipher},
Kdf, SecretBox,
};
use zeroize::Zeroizing;
#[cfg(feature = "chacha20")]
use chacha20::ChaCha20Legacy as ChaCha20;
#[cfg(feature = "salsa20")]
use salsa20::Salsa20;
pub const KEY_SIZE: usize = 32;
pub type Tag = GenericArray<u8, U16>;
#[cfg(feature = "seal")]
const TAG_SIZE: usize = 16;
#[cfg(feature = "seal")]
pub const SEALBYTES: usize = KEY_SIZE + TAG_SIZE;
#[cfg(feature = "chacha20")]
pub type ChaChaBox = CryptoBox<ChaCha20>;
#[cfg(feature = "salsa20")]
pub type SalsaBox = CryptoBox<Salsa20>;
#[derive(Clone)]
pub struct CryptoBox<C> {
secretbox: SecretBox<C>,
}
impl<C> CryptoBox<C> {
pub fn new(public_key: &PublicKey, secret_key: &SecretKey) -> Self
where
C: Kdf,
{
let shared_secret = Zeroizing::new(secret_key.scalar * public_key.0);
let key = Zeroizing::new(C::kdf(
GenericArray::from_slice(&shared_secret.0),
&GenericArray::default(),
));
Self {
secretbox: SecretBox::<C>::new(&*key),
}
}
}
impl<C> AeadCore for CryptoBox<C> {
type NonceSize = U24;
type TagSize = U16;
type CiphertextOverhead = U0;
}
impl<C> AeadInPlace for CryptoBox<C>
where
C: Kdf + KeyIvInit + KeySizeUser<KeySize = U32> + IvSizeUser<IvSize = U8> + StreamCipher,
{
fn encrypt_in_place(
&self,
nonce: &GenericArray<u8, Self::NonceSize>,
associated_data: &[u8],
buffer: &mut dyn Buffer,
) -> Result<(), Error> {
self.secretbox
.encrypt_in_place(nonce, associated_data, buffer)
}
fn encrypt_in_place_detached(
&self,
nonce: &GenericArray<u8, Self::NonceSize>,
associated_data: &[u8],
buffer: &mut [u8],
) -> Result<Tag, Error> {
self.secretbox
.encrypt_in_place_detached(nonce, associated_data, buffer)
}
fn decrypt_in_place(
&self,
nonce: &GenericArray<u8, Self::NonceSize>,
associated_data: &[u8],
buffer: &mut dyn Buffer,
) -> Result<(), Error> {
self.secretbox
.decrypt_in_place(nonce, associated_data, buffer)
}
fn decrypt_in_place_detached(
&self,
nonce: &GenericArray<u8, Self::NonceSize>,
associated_data: &[u8],
buffer: &mut [u8],
tag: &Tag,
) -> Result<(), Error> {
self.secretbox
.decrypt_in_place_detached(nonce, associated_data, buffer, tag)
}
}
#[cfg(feature = "seal")]
fn get_seal_nonce(ephemeral_pk: &PublicKey, recipient_pk: &PublicKey) -> Nonce {
use blake2::{Blake2b, Digest};
let mut hasher = Blake2b::<U24>::new();
hasher.update(ephemeral_pk.as_bytes());
hasher.update(recipient_pk.as_bytes());
hasher.finalize()
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "serde")]
#[test]
fn test_public_key_serialization() {
use aead::rand_core::RngCore;
let mut public_key_bytes = [0; 32];
let mut rng = rand::thread_rng();
rng.fill_bytes(&mut public_key_bytes);
let public_key = PublicKey::from(public_key_bytes);
let serialized = bincode::serialize(&public_key).unwrap();
let deserialized: PublicKey = bincode::deserialize(&serialized).unwrap();
assert_eq!(deserialized, public_key,);
let serialized = rmp_serde::to_vec_named(&public_key).unwrap();
let deserialized: PublicKey = rmp_serde::from_slice(&serialized).unwrap();
assert_eq!(deserialized, public_key,);
}
#[cfg(feature = "serde")]
#[test]
fn test_secret_key_serialization() {
use aead::rand_core::RngCore;
let mut secret_key_bytes = [0; 32];
let mut rng = rand::thread_rng();
rng.fill_bytes(&mut secret_key_bytes);
let secret_key = SecretKey::from(secret_key_bytes);
let serialized = bincode::serialize(&secret_key).unwrap();
let deserialized: SecretKey = bincode::deserialize(&serialized).unwrap();
assert_eq!(deserialized.to_bytes(), secret_key.to_bytes());
let serialized = rmp_serde::to_vec_named(&secret_key).unwrap();
let deserialized: SecretKey = rmp_serde::from_slice(&serialized).unwrap();
assert_eq!(deserialized.to_bytes(), secret_key.to_bytes());
}
#[test]
fn test_public_key_from_slice() {
let array = [0; 40];
assert!(PublicKey::from_slice(&[]).is_err());
for i in 1..=31 {
assert!(PublicKey::from_slice(&array[..i]).is_err());
}
assert!(PublicKey::from_slice(&array[..32]).is_ok());
for i in 33..=40 {
assert!(PublicKey::from_slice(&array[..i]).is_err());
}
}
}