use aranya_crypto::{
aqc::{
BidiAuthorSecretId, BidiChannel, BidiChannelId, BidiPeerEncap, BidiPsk, UniAuthorSecretId,
UniChannel, UniChannelId, UniPeerEncap, UniRecvPsk, UniSendPsk,
},
custom_id, CipherSuite, DeviceId, EncryptionKeyId, Engine, Id, KeyStore, KeyStoreExt,
};
use buggy::{bug, Bug};
use serde::{Deserialize, Serialize};
use crate::shared::{decode_enc_pk, LabelId};
macro_rules! error {
($($arg:tt)+) => { ::tracing::error!(target: "aqc-handler", $($arg)+) };
}
#[derive(Clone)]
pub struct Handler<S> {
device_id: DeviceId,
store: S,
}
impl<S> Handler<S> {
pub const fn new(device_id: DeviceId, store: S) -> Self {
Self { device_id, store }
}
}
impl<S: KeyStore> Handler<S> {
pub fn bidi_channel_created<E: Engine>(
&mut self,
eng: &mut E,
effect: &BidiChannelCreated<'_>,
) -> Result<BidiPsk<E::CS>, Error> {
if self.device_id != effect.author_id {
return Err(Error::NotAuthor);
}
let secret = self
.store
.remove_key(eng, effect.author_secrets_id.into())
.map_err(|_| Error::KeyStore)?
.ok_or(Error::KeyNotFound("BidiAuthorSecret"))?;
let our_sk = &self
.store
.get_key(eng, effect.author_enc_key_id.into())
.map_err(|_| Error::KeyStore)?
.ok_or(Error::KeyNotFound("device encryption key"))?;
let their_pk = &decode_enc_pk(effect.peer_enc_pk).map_err(|err| {
error!("unable to decode `EncryptionPublicKey`: {err}");
Error::Encoding
})?;
let ch = BidiChannel {
psk_length_in_bytes: effect.psk_length_in_bytes,
parent_cmd_id: effect.parent_cmd_id,
our_sk,
our_id: effect.author_id,
their_pk,
their_id: effect.peer_id,
label: effect.label_id.into(),
};
let psk = BidiPsk::from_author_secret(&ch, secret).inspect_err(|err| {
error!(?err, "unable to derive bidi PSK from author secret");
})?;
if psk.identity() != effect.channel_id {
bug!("PSK identity does not match `effect.channel_id`");
}
Ok(psk)
}
pub fn bidi_channel_received<E: Engine>(
&mut self,
eng: &mut E,
effect: &BidiChannelReceived<'_>,
) -> Result<BidiPsk<E::CS>, Error> {
if self.device_id != effect.peer_id {
return Err(Error::NotRecipient);
}
let encap =
BidiPeerEncap::from_bytes(effect.encap).map_err(|err| Error::Crypto(err.into()))?;
let our_sk = &self
.store
.get_key(eng, effect.peer_enc_key_id.into())
.map_err(|_| Error::KeyStore)?
.ok_or(Error::KeyNotFound("device encryption key"))?;
let their_pk = &decode_enc_pk(effect.author_enc_pk).map_err(|err| {
error!("unable to decode `EncryptionPublicKey`: {err}");
Error::Encoding
})?;
let ch = BidiChannel {
psk_length_in_bytes: effect.psk_length_in_bytes,
parent_cmd_id: effect.parent_cmd_id,
our_sk,
our_id: effect.peer_id,
their_pk,
their_id: effect.author_id,
label: effect.label_id.into(),
};
let psk = BidiPsk::from_peer_encap(&ch, encap).inspect_err(|err| {
error!(?err, "unable to derive bidi PSK from peer encap");
})?;
if psk.identity() != effect.channel_id {
bug!("PSK identity does not match `effect.channel_id`");
}
Ok(psk)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BidiChannelCreated<'a> {
pub channel_id: BidiChannelId,
pub parent_cmd_id: Id,
pub author_id: DeviceId,
pub author_enc_key_id: EncryptionKeyId,
pub peer_id: DeviceId,
pub peer_enc_pk: &'a [u8],
pub label_id: LabelId,
pub author_secrets_id: BidiAuthorSecretId,
pub psk_length_in_bytes: u16,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BidiChannelReceived<'a> {
pub channel_id: BidiChannelId,
pub parent_cmd_id: Id,
pub author_id: DeviceId,
pub author_enc_pk: &'a [u8],
pub peer_id: DeviceId,
pub peer_enc_key_id: EncryptionKeyId,
pub label_id: LabelId,
pub encap: &'a [u8],
pub psk_length_in_bytes: u16,
}
impl<S: KeyStore> Handler<S> {
pub fn uni_channel_created<E: Engine>(
&mut self,
eng: &mut E,
effect: &UniChannelCreated<'_>,
) -> Result<UniPsk<E::CS>, Error> {
if (self.device_id != effect.send_id && self.device_id != effect.recv_id)
|| self.device_id != effect.author_id
{
return Err(Error::NotAuthor);
}
let secret = self
.store
.remove_key(eng, effect.author_secrets_id.into())
.map_err(|_| Error::KeyStore)?
.ok_or(Error::KeyNotFound("UniAuthorSecret"))?;
let our_sk = &self
.store
.get_key(eng, effect.author_enc_key_id.into())
.map_err(|_| Error::KeyStore)?
.ok_or(Error::KeyNotFound("device encryption key"))?;
let their_pk = &decode_enc_pk(effect.peer_enc_pk).map_err(|err| {
error!("unable to decode `EncryptionPublicKey`: {err}");
Error::Encoding
})?;
let ch = UniChannel {
psk_length_in_bytes: effect.psk_length_in_bytes,
parent_cmd_id: effect.parent_cmd_id,
seal_id: effect.send_id,
open_id: effect.recv_id,
our_sk,
their_pk,
label: effect.label_id.into(),
};
let psk = if self.device_id == effect.send_id {
UniSendPsk::from_author_secret(&ch, secret)
.inspect_err(|err| {
error!(?err, "unable to derive uni send PSK from author secret");
})
.map(UniPsk::SendOnly)?
} else {
UniRecvPsk::from_author_secret(&ch, secret)
.inspect_err(|err| {
error!(?err, "unable to derive uni recv PSK from author secret");
})
.map(UniPsk::RecvOnly)?
};
if psk.identity() != effect.channel_id {
bug!("PSK identity does not match `effect.channel_id`");
}
Ok(psk)
}
pub fn uni_channel_received<E: Engine>(
&mut self,
eng: &mut E,
effect: &UniChannelReceived<'_>,
) -> Result<UniPsk<E::CS>, Error> {
if (self.device_id != effect.send_id && self.device_id != effect.recv_id)
|| self.device_id == effect.author_id
{
return Err(Error::NotRecipient);
}
let encap =
UniPeerEncap::from_bytes(effect.encap).map_err(|err| Error::Crypto(err.into()))?;
let our_sk = &self
.store
.get_key(eng, effect.peer_enc_key_id.into())
.map_err(|_| Error::KeyStore)?
.ok_or(Error::KeyNotFound("device encryption key"))?;
let their_pk = &decode_enc_pk(effect.author_enc_pk).map_err(|err| {
error!("unable to decode `EncryptionPublicKey`: {err}");
Error::Encoding
})?;
let ch = UniChannel {
psk_length_in_bytes: effect.psk_length_in_bytes,
parent_cmd_id: effect.parent_cmd_id,
seal_id: effect.send_id,
open_id: effect.recv_id,
our_sk,
their_pk,
label: effect.label_id.into(),
};
let psk = if self.device_id == effect.send_id {
UniSendPsk::from_peer_encap(&ch, encap)
.inspect_err(|err| {
error!(?err, "unable to derive uni send PSK from peer encap");
})
.map(UniPsk::SendOnly)?
} else {
UniRecvPsk::from_peer_encap(&ch, encap)
.inspect_err(|err| {
error!(?err, "unable to derive uni recv PSK from peer encap");
})
.map(UniPsk::RecvOnly)?
};
if psk.identity() != effect.channel_id {
bug!("PSK identity does not match `effect.channel_id`");
}
Ok(psk)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UniChannelCreated<'a> {
pub channel_id: UniChannelId,
pub parent_cmd_id: Id,
pub author_id: DeviceId,
pub send_id: DeviceId,
pub recv_id: DeviceId,
pub author_enc_key_id: EncryptionKeyId,
pub peer_enc_pk: &'a [u8],
pub label_id: LabelId,
pub author_secrets_id: UniAuthorSecretId,
pub psk_length_in_bytes: u16,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UniChannelReceived<'a> {
pub channel_id: UniChannelId,
pub parent_cmd_id: Id,
pub author_id: DeviceId,
pub send_id: DeviceId,
pub recv_id: DeviceId,
pub author_enc_pk: &'a [u8],
pub peer_enc_key_id: EncryptionKeyId,
pub label_id: LabelId,
pub encap: &'a [u8],
pub psk_length_in_bytes: u16,
}
custom_id! {
pub struct UniKeyId;
}
#[derive(Debug)]
pub enum UniPsk<CS: CipherSuite> {
SendOnly(UniSendPsk<CS>),
RecvOnly(UniRecvPsk<CS>),
}
impl<CS: CipherSuite> UniPsk<CS> {
#[inline]
pub fn identity(&self) -> UniChannelId {
match self {
Self::SendOnly(psk) => psk.identity(),
Self::RecvOnly(psk) => psk.identity(),
}
}
#[inline]
pub fn raw_secret_bytes(&self) -> &[u8] {
match self {
Self::SendOnly(psk) => psk.raw_secret_bytes(),
Self::RecvOnly(psk) => psk.raw_secret_bytes(),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("{0}")]
Bug(#[from] Bug),
#[error("not command author")]
NotAuthor,
#[error("not command recipient")]
NotRecipient,
#[error("keystore failure")]
KeyStore,
#[error("unable to find key: {0}")]
KeyNotFound(&'static str),
#[error("unable to transform key")]
Transform,
#[error("unable to encode/decode")]
Encoding,
#[error(transparent)]
Crypto(#[from] aranya_crypto::Error),
}