use crate::{
identity::{new_identity_key, ContainedIdKey, IdentityKey},
lockbox::*,
stream::{new_stream_key, stream_key_encrypt, ContainedStreamKey, StreamKey},
CryptoError, CryptoSrc,
};
use rand_core::{CryptoRng, RngCore};
use zeroize::Zeroize;
use std::{convert::TryFrom, fmt, sync::Arc};
pub const DEFAULT_LOCK_VERSION: u8 = 1;
pub const MIN_LOCK_VERSION: u8 = 1;
pub const MAX_LOCK_VERSION: u8 = 1;
const V1_LOCK_ID_SIZE: usize = 32;
const V1_LOCK_KEY_SIZE: usize = 32;
pub(crate) fn lock_id_size(_version: u8) -> usize {
1 + V1_LOCK_ID_SIZE
}
pub(crate) fn lock_eph_size(_version: u8) -> usize {
V1_LOCK_ID_SIZE
}
#[derive(Clone)]
pub struct LockKey {
interface: Arc<dyn LockInterface>,
}
impl LockKey {
pub fn new_temp<R>(csprng: &mut R) -> LockKey
where
R: CryptoRng + RngCore,
{
let interface = Arc::new(ContainedLockKey::generate(csprng));
new_lock_key(interface)
}
pub fn new_temp_with_version<R>(csprng: &mut R, version: u8) -> Result<LockKey, CryptoError>
where
R: CryptoRng + RngCore,
{
let interface = Arc::new(ContainedLockKey::with_version(csprng, version)?);
Ok(new_lock_key(interface))
}
pub fn version(&self) -> u8 {
self.interface.id().version()
}
pub fn id(&self) -> &LockId {
self.interface.id()
}
pub fn decrypt_lock_key(&self, lockbox: &LockLockboxRef) -> Result<LockKey, CryptoError> {
self.interface.decrypt_lock_key(lockbox)
}
pub fn decrypt_identity_key(
&self,
lockbox: &IdentityLockboxRef,
) -> Result<IdentityKey, CryptoError> {
self.interface.decrypt_identity_key(lockbox)
}
pub fn decrypt_stream_key(&self, lockbox: &StreamLockboxRef) -> Result<StreamKey, CryptoError> {
self.interface.decrypt_stream_key(lockbox)
}
pub fn decrypt_data(&self, lockbox: &DataLockboxRef) -> Result<Vec<u8>, CryptoError> {
self.interface.decrypt_data(lockbox)
}
pub fn export_for_lock<R: CryptoRng + RngCore>(
&self,
csprng: &mut R,
lock: &LockId,
) -> Option<LockLockbox> {
self.interface.self_export_lock(csprng, lock)
}
pub fn export_for_stream<R: CryptoRng + RngCore>(
&self,
csprng: &mut R,
stream: &StreamKey,
) -> Option<LockLockbox> {
self.interface.self_export_stream(csprng, stream)
}
}
impl fmt::Debug for LockKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LockKey")
.field("version", &self.version())
.field("lock_id", &self.id().raw_public_key())
.finish()
}
}
impl fmt::Display for LockKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self.id(), f)
}
}
pub fn new_lock_key(interface: Arc<dyn LockInterface>) -> LockKey {
LockKey { interface }
}
pub trait LockInterface {
fn id(&self) -> &LockId;
fn decrypt_lock_key(&self, lockbox: &LockLockboxRef) -> Result<LockKey, CryptoError>;
fn decrypt_identity_key(
&self,
lockbox: &IdentityLockboxRef,
) -> Result<IdentityKey, CryptoError>;
fn decrypt_stream_key(&self, lockbox: &StreamLockboxRef) -> Result<StreamKey, CryptoError>;
fn decrypt_data(&self, lockbox: &DataLockboxRef) -> Result<Vec<u8>, CryptoError>;
fn self_export_lock(
&self,
csprng: &mut dyn CryptoSrc,
receive_lock: &LockId,
) -> Option<LockLockbox>;
fn self_export_stream(
&self,
csprng: &mut dyn CryptoSrc,
receive_stream: &StreamKey,
) -> Option<LockLockbox>;
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct LockId {
inner: x25519_dalek::PublicKey,
}
impl LockId {
pub fn encrypt_data<R>(&self, csprng: &mut R, content: &[u8]) -> DataLockbox
where
R: CryptoRng + RngCore,
{
data_lockbox_from_parts(lock_id_encrypt(
&self,
csprng,
LockboxType::Data(false),
content,
))
}
pub fn version(&self) -> u8 {
1u8
}
pub fn raw_public_key(&self) -> &[u8] {
self.inner.as_bytes()
}
pub fn as_vec(&self) -> Vec<u8> {
let mut v = Vec::new();
self.encode_vec(&mut v);
v
}
pub fn from_base58(s: &str) -> Result<Self, CryptoError> {
let raw = bs58::decode(s)
.into_vec()
.or(Err(CryptoError::BadFormat("Not valid Base58")))?;
Self::try_from(&raw[..])
}
pub fn to_base58(&self) -> String {
bs58::encode(&(self.as_vec())).into_string()
}
pub fn encode_vec(&self, buf: &mut Vec<u8>) {
buf.reserve(self.size());
buf.push(self.version());
buf.extend_from_slice(self.inner.as_bytes());
}
pub fn size(&self) -> usize {
1 + V1_LOCK_ID_SIZE
}
}
impl TryFrom<&[u8]> for LockId {
type Error = CryptoError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let (&version, data) = value.split_first().ok_or(CryptoError::BadLength {
step: "get LockId version",
expected: 1,
actual: 0,
})?;
if version != 1u8 {
return Err(CryptoError::UnsupportedVersion(version));
}
if data.len() != V1_LOCK_ID_SIZE {
return Err(CryptoError::BadLength {
step: "get LockId public key",
expected: V1_LOCK_ID_SIZE,
actual: data.len(),
});
}
let inner: [u8; V1_LOCK_ID_SIZE] =
TryFrom::try_from(data).map_err(|_| CryptoError::BadLength {
step: "get LockId public key",
expected: V1_LOCK_ID_SIZE,
actual: data.len(),
})?;
Ok(Self {
inner: x25519_dalek::PublicKey::from(inner),
})
}
}
impl fmt::Debug for LockId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Identity")
.field("version", &self.version())
.field("public_key", &self.raw_public_key())
.finish()
}
}
impl fmt::Display for LockId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_base58())
}
}
impl fmt::LowerHex for LockId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for byte in self.as_vec().iter() {
write!(f, "{:x}", byte)?;
}
Ok(())
}
}
impl fmt::UpperHex for LockId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for byte in self.as_vec().iter() {
write!(f, "{:X}", byte)?;
}
Ok(())
}
}
pub fn lock_id_encrypt(
id: &LockId,
csprng: &mut dyn CryptoSrc,
lock_type: LockboxType,
content: &[u8],
) -> Vec<u8> {
assert!(
!lock_type.is_for_stream(),
"Tried to encrypt a non-lock-recipient lockbox with a LockId"
);
use chacha20poly1305::aead::{AeadInPlace, NewAead};
use chacha20poly1305::{Key, XChaCha20Poly1305, XNonce};
let mut nonce = [0u8; crate::lockbox::V1_LOCKBOX_NONCE_SIZE];
csprng.fill_bytes(nonce.as_mut());
let eph = x25519_dalek::EphemeralSecret::new(csprng);
let eph_pub = x25519_dalek::PublicKey::from(&eph);
let version = id.version();
let tag_len = lockbox_tag_size(version);
let nonce_len = lockbox_nonce_size(version);
let header_len = 2 + id.size() + eph_pub.as_bytes().len();
let len = header_len + nonce_len + content.len() + tag_len;
let mut lockbox = Vec::with_capacity(len);
lockbox.push(version);
lockbox.push(lock_type.as_u8());
id.encode_vec(&mut lockbox);
lockbox.extend_from_slice(eph_pub.as_bytes());
lockbox.extend_from_slice(&nonce);
lockbox.extend_from_slice(content);
let (additional, nonce_and_content) = lockbox.split_at_mut(header_len);
let (_, content) = nonce_and_content.split_at_mut(nonce_len);
let secret = eph.diffie_hellman(&id.inner);
let aead = XChaCha20Poly1305::new(Key::from_slice(secret.as_bytes()));
let nonce = XNonce::from(nonce);
let tag = aead
.encrypt_in_place_detached(&nonce, additional, content)
.expect("More data than the cipher can accept was put in");
lockbox.extend_from_slice(&tag);
lockbox
}
pub struct ContainedLockKey {
id: LockId,
key: x25519_dalek::StaticSecret,
}
impl ContainedLockKey {
pub fn generate<R>(csprng: &mut R) -> Self
where
R: CryptoRng + RngCore,
{
Self::with_version(csprng, DEFAULT_LOCK_VERSION).unwrap()
}
pub fn with_version<R>(csprng: &mut R, version: u8) -> Result<Self, CryptoError>
where
R: CryptoRng + RngCore,
{
if (version < MIN_LOCK_VERSION) || (version > MAX_LOCK_VERSION) {
return Err(CryptoError::UnsupportedVersion(version));
}
let key = x25519_dalek::StaticSecret::new(csprng);
let id = LockId {
inner: x25519_dalek::PublicKey::from(&key),
};
Ok(Self { key, id })
}
pub fn encode_vec(&self, buf: &mut Vec<u8>) {
buf.reserve(1 + V1_LOCK_KEY_SIZE);
buf.push(1u8);
let mut raw_key = self.key.to_bytes();
buf.extend_from_slice(&raw_key);
raw_key.zeroize();
}
fn decrypt_parts(
&self,
recipient: &LockboxRecipient,
parts: LockboxParts,
) -> Result<Vec<u8>, CryptoError> {
if let LockboxRecipient::LockId(id) = recipient {
if id != &self.id {
return Err(CryptoError::ObjectMismatch(
"StreamKey being used on a lockbox meant for a different LockId",
));
}
} else {
return Err(CryptoError::ObjectMismatch(
"Attempted to use a LockKey to decrypt a lockbox with a StreamId recipient",
));
}
let eph_pub = parts.eph_pub.unwrap();
if eph_pub.len() != V1_LOCK_ID_SIZE {
return Err(CryptoError::BadLength {
step: "get Lockbox ephemeral public key",
expected: V1_LOCK_ID_SIZE,
actual: eph_pub.len(),
});
}
let eph_pub: [u8; 32] = TryFrom::try_from(eph_pub).map_err(|_| CryptoError::BadLength {
step: "get Lockbox ephemeral public key",
expected: V1_LOCK_ID_SIZE,
actual: eph_pub.len(),
})?;
let eph_pub = x25519_dalek::PublicKey::from(eph_pub);
let secret = self.key.diffie_hellman(&eph_pub);
use chacha20poly1305::aead::{Aead, NewAead};
use chacha20poly1305::*;
let aead = XChaCha20Poly1305::new(Key::from_slice(secret.as_bytes()));
let nonce = XNonce::from_slice(parts.nonce);
let payload = aead::Payload {
msg: parts.ciphertext,
aad: parts.additional,
};
aead.decrypt(nonce, payload)
.map_err(|_| CryptoError::DecryptFailed)
}
}
impl TryFrom<&[u8]> for ContainedLockKey {
type Error = CryptoError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let (version, raw_key) = value.split_first().ok_or(CryptoError::BadLength {
step: "get LockKey version",
expected: 1,
actual: 0,
})?;
let version = *version;
if version < MIN_LOCK_VERSION {
return Err(CryptoError::OldVersion(version));
}
if version > MAX_LOCK_VERSION {
return Err(CryptoError::UnsupportedVersion(version));
}
if raw_key.len() != V1_LOCK_KEY_SIZE {
return Err(CryptoError::BadLength {
step: "get LockKey key bytes",
expected: V1_LOCK_KEY_SIZE,
actual: raw_key.len(),
});
}
let mut raw_key: [u8; V1_LOCK_KEY_SIZE] =
TryFrom::try_from(raw_key).map_err(|_| CryptoError::BadLength {
step: "get LockKey key bytes",
expected: V1_LOCK_KEY_SIZE,
actual: raw_key.len(),
})?;
let key = x25519_dalek::StaticSecret::from(raw_key);
raw_key.zeroize();
let public = x25519_dalek::PublicKey::from(&key);
Ok(Self {
key,
id: LockId { inner: public },
})
}
}
impl LockInterface for ContainedLockKey {
fn id(&self) -> &LockId {
&self.id
}
fn decrypt_lock_key(&self, lockbox: &LockLockboxRef) -> Result<LockKey, CryptoError> {
let recipient = lockbox.recipient();
let parts = lockbox.as_parts();
let mut key = self.decrypt_parts(&recipient, parts)?;
let result = ContainedLockKey::try_from(key.as_ref());
key.zeroize();
Ok(new_lock_key(Arc::new(result?)))
}
fn decrypt_identity_key(
&self,
lockbox: &IdentityLockboxRef,
) -> Result<IdentityKey, CryptoError> {
let recipient = lockbox.recipient();
let parts = lockbox.as_parts();
let mut key = self.decrypt_parts(&recipient, parts)?;
let result = ContainedIdKey::try_from(key.as_ref());
key.zeroize();
Ok(new_identity_key(Arc::new(result?)))
}
fn decrypt_stream_key(&self, lockbox: &StreamLockboxRef) -> Result<StreamKey, CryptoError> {
let recipient = lockbox.recipient();
let parts = lockbox.as_parts();
let mut key = self.decrypt_parts(&recipient, parts)?;
let result = ContainedStreamKey::try_from(key.as_ref());
key.zeroize();
Ok(new_stream_key(Arc::new(result?)))
}
fn decrypt_data(&self, lockbox: &DataLockboxRef) -> Result<Vec<u8>, CryptoError> {
let recipient = lockbox.recipient();
let parts = lockbox.as_parts();
self.decrypt_parts(&recipient, parts)
}
fn self_export_lock(
&self,
csprng: &mut dyn CryptoSrc,
receive_lock: &LockId,
) -> Option<LockLockbox> {
let mut raw_secret = Vec::new();
self.encode_vec(&mut raw_secret);
let lockbox_vec =
lock_id_encrypt(receive_lock, csprng, LockboxType::Lock(false), &raw_secret);
raw_secret.zeroize();
debug_assert!(raw_secret.iter().all(|&x| x == 0));
Some(lock_lockbox_from_parts(lockbox_vec))
}
fn self_export_stream(
&self,
csprng: &mut dyn CryptoSrc,
receive_stream: &StreamKey,
) -> Option<LockLockbox> {
let mut raw_secret = Vec::new();
self.encode_vec(&mut raw_secret);
let lockbox_vec =
stream_key_encrypt(receive_stream, csprng, LockboxType::Lock(true), &raw_secret);
raw_secret.zeroize();
debug_assert!(raw_secret.iter().all(|&x| x == 0));
Some(lock_lockbox_from_parts(lockbox_vec))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basics() {
let mut csprng = rand::rngs::OsRng;
let key = LockKey::new_temp(&mut csprng);
assert_eq!(key.version(), DEFAULT_LOCK_VERSION);
let key = LockKey::new_temp_with_version(&mut csprng, DEFAULT_LOCK_VERSION).unwrap();
assert_eq!(key.version(), DEFAULT_LOCK_VERSION);
let result = LockKey::new_temp_with_version(&mut csprng, 99u8);
if let Err(CryptoError::UnsupportedVersion(99u8)) = result {
} else {
panic!("Didn't get expected error on new_temp_with_version");
}
}
#[test]
fn display() {
let mut csprng = rand::rngs::OsRng;
let key = LockKey::new_temp(&mut csprng);
let disp_key = format!("{}", &key);
let disp_id = format!("{}", key.id());
let base58 = key.id().to_base58();
assert_eq!(disp_key, disp_id);
assert_eq!(disp_key, base58);
assert!(disp_key.len() > 1);
}
#[test]
fn base58() {
let mut csprng = rand::rngs::OsRng;
let key = LockKey::new_temp(&mut csprng);
let mut base58 = key.id().to_base58();
assert!(base58.len() > 1);
let id = LockId::from_base58(&base58).unwrap();
assert_eq!(&id, key.id());
base58.push('a');
base58.push('a');
assert!(LockId::from_base58(&base58).is_err());
base58.pop();
base58.pop();
base58.pop();
assert!(LockId::from_base58(&base58).is_err());
}
#[test]
fn encode() {
let mut csprng = rand::rngs::OsRng;
let key = LockKey::new_temp(&mut csprng);
let id = key.id();
let mut id_vec = Vec::new();
id.encode_vec(&mut id_vec);
assert_eq!(id_vec.len(), id.size());
let id = LockId::try_from(&id_vec[..]).unwrap();
assert_eq!(&id, key.id());
}
fn corrupt_version<F1, F2>(mut enc: Vec<u8>, check_decode: F1, check_decrypt: F2)
where
F1: Fn(&[u8]) -> bool,
F2: Fn(&[u8]) -> bool,
{
let version = enc[0];
enc[0] = 0;
assert!(!check_decode(&enc[..]));
enc[0] = 2;
assert!(!check_decode(&enc[..]));
enc[0] = version;
assert!(check_decrypt(&enc[..]));
}
fn corrupt_type<F1, F2>(mut enc: Vec<u8>, check_decode: F1, check_decrypt: F2)
where
F1: Fn(&[u8]) -> bool,
F2: Fn(&[u8]) -> bool,
{
enc[1] |= 0x80;
assert!(!check_decode(&enc[..]));
enc[1] &= 0x07;
assert!(check_decrypt(&enc[..]));
enc[1] = (enc[1] + 1) & 0x7;
assert!(check_decode(&enc[..]));
assert!(!check_decrypt(&enc[..]));
for _ in 0..6 {
enc[1] = (enc[1] + 1) & 0x7;
assert!(!check_decode(&enc[..]));
}
enc[1] = (enc[1] + 1) & 0x7;
assert!(check_decrypt(&enc[..]));
}
fn corrupt_id<F1, F2>(mut enc: Vec<u8>, check_decode: F1, check_decrypt: F2)
where
F1: Fn(&[u8]) -> bool,
F2: Fn(&[u8]) -> bool,
{
enc[2] = 0;
assert!(!check_decode(&enc[..]));
enc[2] = 2;
assert!(!check_decode(&enc[..]));
enc[2] = DEFAULT_LOCK_VERSION;
assert!(check_decrypt(&enc[..]));
enc[3] ^= 0xFF;
assert!(!check_decrypt(&enc[..]));
enc[3] ^= 0xFF;
assert!(check_decrypt(&enc[..]));
}
fn corrupt_ephemeral<F1, F2>(mut enc: Vec<u8>, check_decode: F1, check_decrypt: F2)
where
F1: Fn(&[u8]) -> bool,
F2: Fn(&[u8]) -> bool,
{
enc[35] ^= 0xFF;
assert!(check_decode(&enc[..]));
assert!(!check_decrypt(&enc[..]));
enc[35] ^= 0xFF;
assert!(check_decrypt(&enc[..]));
}
fn corrupt_nonce<F1, F2>(mut enc: Vec<u8>, check_decode: F1, check_decrypt: F2)
where
F1: Fn(&[u8]) -> bool,
F2: Fn(&[u8]) -> bool,
{
enc[67] ^= 0xFF;
assert!(check_decode(&enc[..]));
assert!(!check_decrypt(&enc[..]));
enc[67] ^= 0xFF;
assert!(check_decrypt(&enc[..]));
}
fn corrupt_ciphertext<F1, F2>(mut enc: Vec<u8>, check_decode: F1, check_decrypt: F2)
where
F1: Fn(&[u8]) -> bool,
F2: Fn(&[u8]) -> bool,
{
enc[91] ^= 0xFF;
assert!(check_decode(&enc[..]));
assert!(!check_decrypt(&enc[..]));
enc[91] ^= 0xFF;
assert!(check_decrypt(&enc[..]));
}
fn corrupt_tag<F1, F2>(mut enc: Vec<u8>, check_decode: F1, check_decrypt: F2)
where
F1: Fn(&[u8]) -> bool,
F2: Fn(&[u8]) -> bool,
{
let tag_end = enc.last_mut().unwrap();
*tag_end ^= 0xFF;
assert!(check_decode(&enc[..]));
assert!(!check_decrypt(&enc[..]));
let tag_end = enc.last_mut().unwrap();
*tag_end ^= 0xFF;
assert!(check_decrypt(&enc[..]));
}
fn corrupt_length_extend<F1, F2>(mut enc: Vec<u8>, check_decode: F1, check_decrypt: F2)
where
F1: Fn(&[u8]) -> bool,
F2: Fn(&[u8]) -> bool,
{
enc.push(0);
assert!(check_decode(&enc[..]));
assert!(!check_decrypt(&enc[..]));
enc.pop();
assert!(check_decrypt(&enc[..]));
}
fn corrupt_truncation<F1, F2>(mut enc: Vec<u8>, check_decode: F1, check_decrypt: F2)
where
F1: Fn(&[u8]) -> bool,
F2: Fn(&[u8]) -> bool,
{
enc.pop();
assert!(check_decode(&enc[..]));
assert!(!check_decrypt(&enc[..]));
}
fn corrupt_each_byte<F1, F2>(mut enc: Vec<u8>, _check_decode: F1, check_decrypt: F2)
where
F1: Fn(&[u8]) -> bool,
F2: Fn(&[u8]) -> bool,
{
for i in 0..enc.len() {
enc[i] ^= 0xFF;
assert!(!check_decrypt(&enc[..]));
enc[i] ^= 0xFF;
}
}
fn corrupt_inner_version<F: Fn(&[u8]) -> bool>(mut content: Vec<u8>, check_sequence: F) {
content[0] = 0u8;
assert!(!check_sequence(&content[..]));
content[0] = 99u8;
assert!(!check_sequence(&content[..]));
}
fn corrupt_inner_length_extend<F: Fn(&[u8]) -> bool>(mut content: Vec<u8>, check_sequence: F) {
content.push(0u8);
assert!(!check_sequence(&content[..]));
}
fn corrupt_inner_truncate<F: Fn(&[u8]) -> bool>(mut content: Vec<u8>, check_sequence: F) {
content.pop();
assert!(!check_sequence(&content[..]));
}
fn setup_data() -> (Vec<u8>, impl Fn(&[u8]) -> bool, impl Fn(&[u8]) -> bool) {
let mut csprng = rand::rngs::OsRng;
let key = LockKey::new_temp(&mut csprng);
let message = b"I am a test message, going undercover";
let lockbox = key.id().encrypt_data(&mut csprng, message);
let recipient = LockboxRecipient::LockId(key.id().clone());
assert_eq!(recipient, lockbox.recipient());
let enc = Vec::from(lockbox.as_bytes());
(
enc,
|enc| DataLockboxRef::from_bytes(enc).is_ok(),
move |enc| {
let dec_lockbox = if let Ok(d) = DataLockboxRef::from_bytes(enc) {
d
} else {
return false;
};
if LockboxRecipient::LockId(key.id().clone()) != dec_lockbox.recipient() {
return false;
}
if let Ok(dec) = key.decrypt_data(&dec_lockbox) {
dec == message
} else {
false
}
},
)
}
#[test]
fn data_clean_decrypt() {
let (enc, _check_decode, check_decrypt) = setup_data();
assert!(check_decrypt(&enc[..]));
}
#[test]
fn data_corrupt_version() {
let (enc, check_decode, check_decrypt) = setup_data();
corrupt_version(enc, check_decode, check_decrypt);
}
#[test]
fn data_corrupt_type() {
let (enc, check_decode, check_decrypt) = setup_data();
corrupt_type(enc, check_decode, check_decrypt);
}
#[test]
fn data_corrupt_id() {
let (enc, check_decode, check_decrypt) = setup_data();
corrupt_id(enc, check_decode, check_decrypt);
}
#[test]
fn data_corrupt_ephemeral() {
let (enc, check_decode, check_decrypt) = setup_data();
corrupt_ephemeral(enc, check_decode, check_decrypt);
}
#[test]
fn data_corrupt_nonce() {
let (enc, check_decode, check_decrypt) = setup_data();
corrupt_nonce(enc, check_decode, check_decrypt);
}
#[test]
fn data_corrupt_ciphertext() {
let (enc, check_decode, check_decrypt) = setup_data();
corrupt_ciphertext(enc, check_decode, check_decrypt);
}
#[test]
fn data_corrupt_tag() {
let (enc, check_decode, check_decrypt) = setup_data();
corrupt_tag(enc, check_decode, check_decrypt);
}
#[test]
fn data_corrupt_length_extend() {
let (enc, check_decode, check_decrypt) = setup_data();
corrupt_length_extend(enc, check_decode, check_decrypt);
}
#[test]
fn data_corrupt_truncation() {
let (enc, check_decode, check_decrypt) = setup_data();
corrupt_truncation(enc, check_decode, check_decrypt);
}
#[test]
fn data_corrupt_each_byte() {
let (enc, check_decode, check_decrypt) = setup_data();
corrupt_each_byte(enc, check_decode, check_decrypt);
}
fn setup_id() -> (Vec<u8>, impl Fn(&[u8]) -> bool, impl Fn(&[u8]) -> bool) {
let mut csprng = rand::rngs::OsRng;
let key = LockKey::new_temp(&mut csprng);
let to_send = IdentityKey::new_temp(&mut csprng);
let lockbox = to_send.export_for_lock(&mut csprng, key.id()).unwrap();
let recipient = LockboxRecipient::LockId(key.id().clone());
assert_eq!(recipient, lockbox.recipient());
let enc = Vec::from(lockbox.as_bytes());
(
enc,
|enc| IdentityLockboxRef::from_bytes(enc).is_ok(),
move |enc| {
let dec_lockbox = if let Ok(d) = IdentityLockboxRef::from_bytes(enc) {
d
} else {
return false;
};
if LockboxRecipient::LockId(key.id().clone()) != dec_lockbox.recipient() {
return false;
}
if let Ok(dec) = key.decrypt_identity_key(&dec_lockbox) {
dec.id() == to_send.id()
} else {
false
}
},
)
}
#[test]
fn id_clean_decrypt() {
let (enc, _check_decode, check_decrypt) = setup_id();
assert!(check_decrypt(&enc[..]));
}
#[test]
fn id_corrupt_version() {
let (enc, check_decode, check_decrypt) = setup_id();
corrupt_version(enc, check_decode, check_decrypt);
}
#[test]
fn id_corrupt_type() {
let (enc, check_decode, check_decrypt) = setup_id();
corrupt_type(enc, check_decode, check_decrypt);
}
#[test]
fn id_corrupt_id() {
let (enc, check_decode, check_decrypt) = setup_id();
corrupt_id(enc, check_decode, check_decrypt);
}
#[test]
fn id_corrupt_ephemeral() {
let (enc, check_decode, check_decrypt) = setup_id();
corrupt_ephemeral(enc, check_decode, check_decrypt);
}
#[test]
fn id_corrupt_nonce() {
let (enc, check_decode, check_decrypt) = setup_id();
corrupt_nonce(enc, check_decode, check_decrypt);
}
#[test]
fn id_corrupt_ciphertext() {
let (enc, check_decode, check_decrypt) = setup_id();
corrupt_ciphertext(enc, check_decode, check_decrypt);
}
#[test]
fn id_corrupt_tag() {
let (enc, check_decode, check_decrypt) = setup_id();
corrupt_tag(enc, check_decode, check_decrypt);
}
#[test]
fn id_corrupt_length_extend() {
let (enc, check_decode, check_decrypt) = setup_id();
corrupt_length_extend(enc, check_decode, check_decrypt);
}
#[test]
fn id_corrupt_truncation() {
let (enc, check_decode, check_decrypt) = setup_id();
corrupt_truncation(enc, check_decode, check_decrypt);
}
#[test]
fn id_corrupt_each_byte() {
let (enc, check_decode, check_decrypt) = setup_id();
corrupt_each_byte(enc, check_decode, check_decrypt);
}
fn setup_id_raw() -> (Vec<u8>, impl Fn(&[u8]) -> bool) {
use crate::identity::SignInterface;
let mut csprng = rand::rngs::OsRng;
let key = LockKey::new_temp(&mut csprng);
let to_send = crate::ContainedIdKey::generate(&mut csprng);
let mut content = Vec::new();
to_send.encode_vec(&mut content);
(content, move |content| {
let mut csprng = rand::rngs::OsRng;
let lockbox = identity_lockbox_from_parts(crate::lock::lock_id_encrypt(
key.id(),
&mut csprng,
crate::lockbox::LockboxType::Identity(false),
&content[..],
));
let enc = Vec::from(lockbox.as_bytes());
let lockbox = if let Ok(l) = IdentityLockboxRef::from_bytes(&enc[..]) {
l
} else {
return false;
};
if let Ok(dec) = key.decrypt_identity_key(&lockbox) {
dec.id() == to_send.id()
} else {
false
}
})
}
#[test]
fn id_inner_ok() {
let (content, check_sequence) = setup_id_raw();
assert!(check_sequence(&content[..]));
}
#[test]
fn id_corrupt_inner_version() {
let (content, check_sequence) = setup_id_raw();
corrupt_inner_version(content, check_sequence);
}
#[test]
fn id_corrupt_inner_length_extend() {
let (content, check_sequence) = setup_id_raw();
corrupt_inner_length_extend(content, check_sequence);
}
#[test]
fn id_corrupt_inner_truncate() {
let (content, check_sequence) = setup_id_raw();
corrupt_inner_truncate(content, check_sequence);
}
fn setup_lock_stream() -> (Vec<u8>, impl Fn(&[u8]) -> bool, impl Fn(&[u8]) -> bool) {
let mut csprng = rand::rngs::OsRng;
let key = LockKey::new_temp(&mut csprng);
let to_send = StreamKey::new_temp(&mut csprng);
let lockbox = to_send.export_for_lock(&mut csprng, key.id()).unwrap();
let recipient = LockboxRecipient::LockId(key.id().clone());
assert_eq!(recipient, lockbox.recipient());
let enc = Vec::from(lockbox.as_bytes());
(
enc,
|enc| StreamLockboxRef::from_bytes(enc).is_ok(),
move |enc| {
let dec_lockbox = if let Ok(d) = StreamLockboxRef::from_bytes(enc) {
d
} else {
return false;
};
if LockboxRecipient::LockId(key.id().clone()) != dec_lockbox.recipient() {
return false;
}
if let Ok(dec) = key.decrypt_stream_key(&dec_lockbox) {
dec.id() == to_send.id()
} else {
false
}
},
)
}
#[test]
fn stream_clean_decrypt() {
let (enc, _check_decode, check_decrypt) = setup_lock_stream();
assert!(check_decrypt(&enc[..]));
}
#[test]
fn stream_corrupt_version() {
let (enc, check_decode, check_decrypt) = setup_lock_stream();
corrupt_version(enc, check_decode, check_decrypt);
}
#[test]
fn stream_corrupt_type() {
let (enc, check_decode, check_decrypt) = setup_lock_stream();
corrupt_type(enc, check_decode, check_decrypt);
}
#[test]
fn stream_corrupt_id() {
let (enc, check_decode, check_decrypt) = setup_lock_stream();
corrupt_id(enc, check_decode, check_decrypt);
}
#[test]
fn stream_corrupt_ephemeral() {
let (enc, check_decode, check_decrypt) = setup_lock_stream();
corrupt_ephemeral(enc, check_decode, check_decrypt);
}
#[test]
fn stream_corrupt_nonce() {
let (enc, check_decode, check_decrypt) = setup_lock_stream();
corrupt_nonce(enc, check_decode, check_decrypt);
}
#[test]
fn stream_corrupt_ciphertext() {
let (enc, check_decode, check_decrypt) = setup_lock_stream();
corrupt_ciphertext(enc, check_decode, check_decrypt);
}
#[test]
fn stream_corrupt_tag() {
let (enc, check_decode, check_decrypt) = setup_lock_stream();
corrupt_tag(enc, check_decode, check_decrypt);
}
#[test]
fn stream_corrupt_length_extend() {
let (enc, check_decode, check_decrypt) = setup_lock_stream();
corrupt_length_extend(enc, check_decode, check_decrypt);
}
#[test]
fn stream_corrupt_truncation() {
let (enc, check_decode, check_decrypt) = setup_lock_stream();
corrupt_truncation(enc, check_decode, check_decrypt);
}
#[test]
fn stream_corrupt_each_byte() {
let (enc, check_decode, check_decrypt) = setup_lock_stream();
corrupt_each_byte(enc, check_decode, check_decrypt);
}
fn setup_lock_stream_raw() -> (Vec<u8>, impl Fn(&[u8]) -> bool) {
use crate::stream::StreamInterface;
let mut csprng = rand::rngs::OsRng;
let key = LockKey::new_temp(&mut csprng);
let to_send = crate::ContainedStreamKey::generate(&mut csprng);
let mut content = Vec::new();
to_send.encode_vec(&mut content);
(content, move |content| {
let mut csprng = rand::rngs::OsRng;
let lockbox = stream_lockbox_from_parts(crate::lock::lock_id_encrypt(
key.id(),
&mut csprng,
crate::lockbox::LockboxType::Stream(false),
&content[..],
));
let enc = Vec::from(lockbox.as_bytes());
let lockbox = if let Ok(l) = StreamLockboxRef::from_bytes(&enc[..]) {
l
} else {
return false;
};
if let Ok(dec) = key.decrypt_stream_key(&lockbox) {
dec.id() == to_send.id()
} else {
false
}
})
}
#[test]
fn stream_inner_ok() {
let (content, check_sequence) = setup_lock_stream_raw();
assert!(check_sequence(&content[..]));
}
#[test]
fn stream_corrupt_inner_version() {
let (content, check_sequence) = setup_lock_stream_raw();
corrupt_inner_version(content, check_sequence);
}
#[test]
fn stream_corrupt_inner_length_extend() {
let (content, check_sequence) = setup_lock_stream_raw();
corrupt_inner_length_extend(content, check_sequence);
}
#[test]
fn stream_corrupt_inner_truncate() {
let (content, check_sequence) = setup_lock_stream_raw();
corrupt_inner_truncate(content, check_sequence);
}
fn setup_lock() -> (Vec<u8>, impl Fn(&[u8]) -> bool, impl Fn(&[u8]) -> bool) {
let mut csprng = rand::rngs::OsRng;
let key = LockKey::new_temp(&mut csprng);
let to_send = LockKey::new_temp(&mut csprng);
let lockbox = to_send.export_for_lock(&mut csprng, key.id()).unwrap();
let recipient = LockboxRecipient::LockId(key.id().clone());
assert_eq!(recipient, lockbox.recipient());
let enc = Vec::from(lockbox.as_bytes());
(
enc,
|enc| LockLockboxRef::from_bytes(enc).is_ok(),
move |enc| {
let dec_lockbox = if let Ok(d) = LockLockboxRef::from_bytes(enc) {
d
} else {
return false;
};
if LockboxRecipient::LockId(key.id().clone()) != dec_lockbox.recipient() {
return false;
}
if let Ok(dec) = key.decrypt_lock_key(&dec_lockbox) {
dec.id() == to_send.id()
} else {
false
}
},
)
}
#[test]
fn lock_clean_decrypt() {
let (enc, _check_decode, check_decrypt) = setup_lock();
assert!(check_decrypt(&enc[..]));
}
#[test]
fn lock_corrupt_version() {
let (enc, check_decode, check_decrypt) = setup_lock();
corrupt_version(enc, check_decode, check_decrypt);
}
#[test]
fn lock_corrupt_type() {
let (enc, check_decode, check_decrypt) = setup_lock();
corrupt_type(enc, check_decode, check_decrypt);
}
#[test]
fn lock_corrupt_id() {
let (enc, check_decode, check_decrypt) = setup_lock();
corrupt_id(enc, check_decode, check_decrypt);
}
#[test]
fn lock_corrupt_ephemeral() {
let (enc, check_decode, check_decrypt) = setup_lock();
corrupt_ephemeral(enc, check_decode, check_decrypt);
}
#[test]
fn lock_corrupt_nonce() {
let (enc, check_decode, check_decrypt) = setup_lock();
corrupt_nonce(enc, check_decode, check_decrypt);
}
#[test]
fn lock_corrupt_ciphertext() {
let (enc, check_decode, check_decrypt) = setup_lock();
corrupt_ciphertext(enc, check_decode, check_decrypt);
}
#[test]
fn lock_corrupt_tag() {
let (enc, check_decode, check_decrypt) = setup_lock();
corrupt_tag(enc, check_decode, check_decrypt);
}
#[test]
fn lock_corrupt_length_extend() {
let (enc, check_decode, check_decrypt) = setup_lock();
corrupt_length_extend(enc, check_decode, check_decrypt);
}
#[test]
fn lock_corrupt_truncation() {
let (enc, check_decode, check_decrypt) = setup_lock();
corrupt_truncation(enc, check_decode, check_decrypt);
}
#[test]
fn lock_corrupt_each_byte() {
let (enc, check_decode, check_decrypt) = setup_lock();
corrupt_each_byte(enc, check_decode, check_decrypt);
}
fn setup_lock_raw() -> (Vec<u8>, impl Fn(&[u8]) -> bool) {
let mut csprng = rand::rngs::OsRng;
let key = LockKey::new_temp(&mut csprng);
let to_send = crate::ContainedLockKey::generate(&mut csprng);
let mut content = Vec::new();
to_send.encode_vec(&mut content);
(content, move |content| {
let mut csprng = rand::rngs::OsRng;
let lockbox = lock_lockbox_from_parts(crate::lock::lock_id_encrypt(
key.id(),
&mut csprng,
crate::lockbox::LockboxType::Lock(false),
&content[..],
));
let enc = Vec::from(lockbox.as_bytes());
let lockbox = if let Ok(l) = LockLockboxRef::from_bytes(&enc[..]) {
l
} else {
return false;
};
if let Ok(dec) = key.decrypt_lock_key(&lockbox) {
dec.id() == to_send.id()
} else {
false
}
})
}
#[test]
fn lock_inner_ok() {
let (content, check_sequence) = setup_lock_raw();
assert!(check_sequence(&content[..]));
}
#[test]
fn lock_corrupt_inner_version() {
let (content, check_sequence) = setup_lock_raw();
corrupt_inner_version(content, check_sequence);
}
#[test]
fn lock_corrupt_inner_length_extend() {
let (content, check_sequence) = setup_lock_raw();
corrupt_inner_length_extend(content, check_sequence);
}
#[test]
fn lock_corrupt_inner_truncate() {
let (content, check_sequence) = setup_lock_raw();
corrupt_inner_truncate(content, check_sequence);
}
}