use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::crypto::x25519::PublicKey;
use crate::crypto::xeddsa::{XEdDSAError, XSignature, xeddsa_verify};
use crate::key_bundle::{Lifetime, LifetimeError, OneTimePreKey, OneTimePreKeyId, PreKey};
use crate::traits::KeyBundle;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct OneTimeKeyBundle {
identity_key: PublicKey,
signed_prekey: PreKey,
prekey_signature: XSignature,
onetime_prekey: Option<OneTimePreKey>,
}
impl OneTimeKeyBundle {
pub fn new(
identity_key: PublicKey,
signed_prekey: PreKey,
prekey_signature: XSignature,
onetime_prekey: Option<OneTimePreKey>,
) -> Self {
Self {
identity_key,
signed_prekey,
prekey_signature,
onetime_prekey,
}
}
}
impl KeyBundle for OneTimeKeyBundle {
fn identity_key(&self) -> &PublicKey {
&self.identity_key
}
fn signed_prekey(&self) -> &PublicKey {
self.signed_prekey.key()
}
fn onetime_prekey(&self) -> Option<&PublicKey> {
self.onetime_prekey.as_ref().map(|key| key.key())
}
fn onetime_prekey_id(&self) -> Option<OneTimePreKeyId> {
self.onetime_prekey.as_ref().map(|key| key.id())
}
fn lifetime(&self) -> &Lifetime {
self.signed_prekey.lifetime()
}
fn verify(&self) -> Result<(), KeyBundleError> {
self.signed_prekey.verify_lifetime()?;
xeddsa_verify(
self.signed_prekey.as_bytes(),
&self.identity_key,
&self.prekey_signature,
)?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct LongTermKeyBundle {
identity_key: PublicKey,
signed_prekey: PreKey,
prekey_signature: XSignature,
}
impl LongTermKeyBundle {
pub fn new(
identity_key: PublicKey,
signed_prekey: PreKey,
prekey_signature: XSignature,
) -> Self {
Self {
identity_key,
signed_prekey,
prekey_signature,
}
}
}
impl KeyBundle for LongTermKeyBundle {
fn identity_key(&self) -> &PublicKey {
&self.identity_key
}
fn signed_prekey(&self) -> &PublicKey {
self.signed_prekey.key()
}
fn onetime_prekey(&self) -> Option<&PublicKey> {
None
}
fn onetime_prekey_id(&self) -> Option<OneTimePreKeyId> {
None
}
fn lifetime(&self) -> &Lifetime {
self.signed_prekey.lifetime()
}
fn verify(&self) -> Result<(), KeyBundleError> {
self.signed_prekey.verify_lifetime()?;
xeddsa_verify(
self.signed_prekey.as_bytes(),
&self.identity_key,
&self.prekey_signature,
)?;
Ok(())
}
}
pub fn latest_key_bundle<'a, KB>(bundles: &'a [KB]) -> Option<&'a KB>
where
KB: KeyBundle,
{
let mut latest: Option<&'a KB> = None;
for bundle in bundles {
if bundle.lifetime().verify().is_err() {
continue;
}
match latest {
Some(current_bundle) => {
if bundle.lifetime() > current_bundle.lifetime() {
latest = Some(bundle);
}
}
None => {
latest = Some(bundle);
}
}
}
latest
}
#[derive(Debug, Error)]
pub enum KeyBundleError {
#[error(transparent)]
XEdDSA(#[from] XEdDSAError),
#[error(transparent)]
Lifetime(#[from] LifetimeError),
}
#[cfg(test)]
mod tests {
use crate::crypto::Rng;
use crate::crypto::x25519::SecretKey;
use crate::crypto::xeddsa::xeddsa_sign;
use crate::key_bundle::{Lifetime, LongTermKeyBundle, OneTimePreKey, PreKey};
use crate::traits::KeyBundle;
use super::OneTimeKeyBundle;
#[test]
fn verify() {
let rng = Rng::from_seed([1; 32]);
let secret_key = SecretKey::from_bytes(rng.random_array().unwrap());
let identity_key = secret_key.verifying_key().unwrap();
let signed_prekey_secret = SecretKey::from_bytes(rng.random_array().unwrap());
let signed_prekey = PreKey::new(
signed_prekey_secret.verifying_key().unwrap(),
Lifetime::default(),
);
let prekey_signature = xeddsa_sign(signed_prekey.as_bytes(), &secret_key, &rng).unwrap();
let onetime_prekey_secret = SecretKey::from_bytes(rng.random_array().unwrap());
let onetime_prekey = OneTimePreKey::new(onetime_prekey_secret.verifying_key().unwrap(), 1);
assert!(
OneTimeKeyBundle::new(
identity_key,
signed_prekey,
prekey_signature,
Some(onetime_prekey.clone()),
)
.verify()
.is_ok()
);
assert!(
LongTermKeyBundle::new(identity_key, signed_prekey, prekey_signature)
.verify()
.is_ok()
);
let signed_prekey = PreKey::new(
signed_prekey_secret.verifying_key().unwrap(),
Lifetime::from_range(0, 0),
);
assert!(
OneTimeKeyBundle::new(
identity_key,
signed_prekey,
prekey_signature,
Some(onetime_prekey.clone()),
)
.verify()
.is_err()
);
assert!(
LongTermKeyBundle::new(identity_key, signed_prekey, prekey_signature)
.verify()
.is_err()
);
let prekey_signature = xeddsa_sign(b"wrong payload", &secret_key, &rng).unwrap();
assert!(
OneTimeKeyBundle::new(
identity_key,
signed_prekey,
prekey_signature,
Some(onetime_prekey.clone()),
)
.verify()
.is_err()
);
assert!(
LongTermKeyBundle::new(identity_key, signed_prekey, prekey_signature)
.verify()
.is_err()
);
}
}