use alloc::{boxed::Box, vec, vec::Vec};
use core::hash::Hash;
use derive_more::{Display, Error};
use signature::Verifier;
#[cfg(feature = "dev")]
use arbitrary::Arbitrary;
use compact_u64::*;
use ufotofu::codec_prelude::*;
mod possibly_valid_write_capability;
pub use possibly_valid_write_capability::*;
mod possibly_valid_read_capability;
pub use possibly_valid_read_capability::*;
use crate::prelude::*;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "dev", derive(Arbitrary))]
pub enum AccessMode {
Read,
Write,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "dev", derive(Arbitrary))]
pub struct CommunalGenesis {
pub access_mode: AccessMode,
pub namespace_key: NamespaceId,
pub user_key: SubspaceId,
}
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "dev", derive(Arbitrary))]
pub struct OwnedGenesis {
pub access_mode: AccessMode,
pub namespace_key: NamespaceId,
pub user_key: SubspaceId,
pub initial_authorisation: NamespaceSignature,
}
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "dev", derive(Arbitrary))]
pub enum Genesis {
Communal(CommunalGenesis),
Owned(OwnedGenesis),
}
impl Genesis {
pub fn access_mode(&self) -> AccessMode {
match self {
Genesis::Communal(g) => g.access_mode,
Genesis::Owned(g) => g.access_mode,
}
}
pub fn namespace_key(&self) -> &NamespaceId {
match self {
Genesis::Communal(g) => &g.namespace_key,
Genesis::Owned(g) => &g.namespace_key,
}
}
pub fn user_key(&self) -> &SubspaceId {
match self {
Genesis::Communal(g) => &g.user_key,
Genesis::Owned(g) => &g.user_key,
}
}
pub fn is_owned(&self) -> bool {
matches!(self, Genesis::Owned(_))
}
pub fn includes_area(&self, area: &Area) -> bool {
match &self {
Genesis::Communal(comcap) => Some(&comcap.user_key) == area.subspace(),
Genesis::Owned(_) => true,
}
}
pub fn is_valid(&self) -> bool {
match self {
Genesis::Communal(CommunalGenesis { namespace_key, .. }) => namespace_key.is_communal(),
Genesis::Owned(OwnedGenesis {
access_mode,
namespace_key,
user_key,
initial_authorisation,
}) => {
if namespace_key.is_communal() {
false
} else {
let data_to_sign = owned_genesis_data_to_sign(*access_mode, user_key);
namespace_key
.verify(&data_to_sign[..], initial_authorisation)
.is_ok()
}
}
}
}
fn granted_area(&self) -> Area {
match self {
Genesis::Communal(comcap) => Area::new_subspace_area(comcap.user_key.clone()),
Genesis::Owned(_) => Area::full(),
}
}
}
pub(crate) fn owned_genesis_data_to_sign(
access_mode: AccessMode,
user_key: &SubspaceId,
) -> Box<[u8]>
where
SubspaceId: EncodableKnownLength,
{
let mut data_to_sign = vec![0; 1 + user_key.len_of_encoding()];
data_to_sign[0] = if access_mode == AccessMode::Read {
2
} else {
3
};
pollster::block_on(async {
user_key.encode(&mut (&mut data_to_sign[1..]).into_consumer()).await
.expect("`user_key.len_of_encoding()` should have been accurate, so the slice should have been sufficiently large");
});
data_to_sign.into_boxed_slice()
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "dev", derive(Arbitrary))]
pub struct Delegation {
pub area: Area,
pub user: SubspaceId,
pub signature: SubspaceSignature,
}
#[derive(Clone, Display, Error, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[display("operation would create invalid capability")]
#[cfg_attr(feature = "dev", derive(Arbitrary))]
pub struct InvalidCapability;
impl Delegation {
pub fn new(area: Area, user: SubspaceId, signature: SubspaceSignature) -> Self {
Delegation {
area,
user,
signature,
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) struct RawCapability {
pub genesis: Genesis,
pub delegations: Vec<Delegation>,
}
impl RawCapability {
pub fn access_mode(&self) -> AccessMode {
self.genesis.access_mode()
}
pub fn receiver(&self) -> &SubspaceId {
match self.delegations.last() {
None => self.genesis.user_key(),
Some(final_delegation) => &final_delegation.user,
}
}
pub fn granted_namespace(&self) -> &NamespaceId {
self.genesis.namespace_key()
}
pub fn granted_area_ref(&self) -> Option<&Area> {
match self.delegations.last() {
None => None,
Some(final_delegation) => Some(&final_delegation.area),
}
}
pub fn genesis(&self) -> &Genesis {
&self.genesis
}
pub fn is_owned(&self) -> bool {
self.genesis.is_owned()
}
pub fn delegations(&self) -> &[Delegation] {
&self.delegations
}
pub fn new_communal(
access_mode: AccessMode,
namespace_key: NamespaceId,
user_key: SubspaceId,
) -> Self {
Self {
genesis: Genesis::Communal(CommunalGenesis {
access_mode,
namespace_key,
user_key,
}),
delegations: vec![],
}
}
pub fn new_owned(
access_mode: AccessMode,
namespace_key: NamespaceId,
user_key: SubspaceId,
initial_authorisation: NamespaceSignature,
) -> Self {
Self {
genesis: Genesis::Owned(OwnedGenesis {
access_mode,
namespace_key,
user_key,
initial_authorisation,
}),
delegations: vec![],
}
}
pub fn includes_area(&self, area: &Area) -> bool {
match self.granted_area_ref() {
Some(granted_area) => granted_area.includes_grouping(area),
None => self.genesis.includes_area(area),
}
}
pub fn try_append_delegation(
&mut self,
delegation: Delegation,
) -> Result<(), InvalidCapability> {
if self.includes_area(&delegation.area) {
self.delegations.push(delegation);
Ok(())
} else {
Err(InvalidCapability)
}
}
pub fn append_delegation(&mut self, delegation: Delegation) {
self.try_append_delegation(delegation).unwrap()
}
pub fn granted_area(&self) -> Area {
match self.granted_area_ref() {
Some(area) => area.clone(),
None => self.genesis.granted_area(),
}
}
pub fn includes<T>(&self, t: &T) -> bool
where
T: Namespaced + Coordinatelike + ?Sized,
{
if t.namespace_id() == self.granted_namespace() {
match self.granted_area_ref() {
Some(area) => area.includes(t),
None => match &self.genesis {
Genesis::Communal(comcap) => &comcap.user_key == t.subspace_id(),
Genesis::Owned(_) => true,
},
}
} else {
false
}
}
pub fn is_valid(&self) -> bool {
if !self.genesis.is_valid() {
return false;
}
match self.delegations.first() {
None => true,
Some(first_delegation) => {
if !self.genesis.includes_area(&first_delegation.area) {
return false;
}
match self.validate_first_delegation(first_delegation) {
Err(InvalidCapability) => false,
Ok((mut prev_area, mut prev_signature, mut prev_receiver)) => {
for delegation in self.delegations[1..].iter() {
if !prev_area.includes_grouping(&delegation.area) {
return false;
}
match Self::validate_non_first_delegation(
delegation,
prev_area,
prev_signature,
prev_receiver,
) {
Ok((new_area, new_signature, new_receiver)) => {
prev_area = new_area;
prev_signature = new_signature;
prev_receiver = new_receiver;
}
Err(InvalidCapability) => {
return false;
}
}
}
true
}
}
}
}
}
pub fn try_append_delegation_checking_validity(
&mut self,
new_delegation: Delegation,
) -> Result<(), InvalidCapability> {
if self.includes_area(&new_delegation.area) {
match self.delegations.last() {
None => {
self.validate_first_delegation(&new_delegation)?;
}
Some(prev_delegation) => {
Self::validate_non_first_delegation(
&new_delegation,
&prev_delegation.area,
&prev_delegation.signature,
&prev_delegation.user,
)?;
}
}
self.delegations.push(new_delegation);
Ok(())
} else {
Err(InvalidCapability)
}
}
pub(crate) fn create_handover(
&self,
new_area: &Area,
new_receiver: &SubspaceId,
last_delegation: Option<&Delegation>,
) -> Box<[u8]> {
match last_delegation {
None => match self.genesis() {
Genesis::Communal(CommunalGenesis {
access_mode,
namespace_key,
user_key,
}) => {
let initial_area = Area::new_subspace_area(user_key.clone());
let delegation_area = new_area;
let mut handover = vec![
0;
1 + namespace_key.len_of_encoding()
+ delegation_area
.len_of_relative_encoding(&initial_area)
+ new_receiver.len_of_encoding()
];
handover[0] = if access_mode == &AccessMode::Read {
0
} else {
1
};
pollster::block_on(async {
let mut con = (&mut handover[1..]).into_consumer();
con.consume_encoded(namespace_key).await.unwrap();
con.consume_relative_encoded(&delegation_area, &initial_area)
.await
.unwrap();
con.consume_encoded(new_receiver).await.unwrap();
});
handover.into_boxed_slice()
}
Genesis::Owned(OwnedGenesis {
initial_authorisation,
..
}) => {
let initial_area = Area::full();
let mut handover = Vec::with_capacity(
new_area.len_of_relative_encoding(&initial_area)
+ initial_authorisation.len_of_encoding()
+ new_receiver.len_of_encoding(),
);
pollster::block_on(async {
let mut con = (&mut handover).into_consumer();
con.consume_relative_encoded(new_area, &initial_area)
.await
.unwrap();
con.consume_encoded(initial_authorisation).await.unwrap();
con.consume_encoded(&new_receiver).await.unwrap();
});
handover.into_boxed_slice()
}
},
Some(last_delegation) => {
let mut handover = Vec::with_capacity(
new_area.len_of_relative_encoding(&last_delegation.area)
+ last_delegation.signature.len_of_encoding()
+ new_receiver.len_of_encoding(),
);
pollster::block_on(async {
let mut con = (&mut handover).into_consumer();
con.consume_relative_encoded(new_area, &last_delegation.area)
.await
.unwrap();
con.consume_encoded(&last_delegation.signature)
.await
.unwrap();
con.consume_encoded(new_receiver).await.unwrap();
});
handover.into_boxed_slice()
}
}
}
fn validate_first_delegation<'dele>(
&self,
first_delegation: &'dele Delegation,
) -> Result<(&'dele Area, &'dele SubspaceSignature, &'dele SubspaceId), InvalidCapability> {
Ok(match self.genesis() {
Genesis::Communal(CommunalGenesis { user_key, .. }) => {
let handover =
self.create_handover(&first_delegation.area, &first_delegation.user, None);
if user_key
.verify(&handover, &first_delegation.signature)
.is_ok()
{
(
&first_delegation.area,
&first_delegation.signature,
&first_delegation.user,
)
} else {
return Err(InvalidCapability);
}
}
Genesis::Owned(OwnedGenesis { user_key, .. }) => {
let handover =
self.create_handover(&first_delegation.area, &first_delegation.user, None);
if user_key
.verify(&handover, &first_delegation.signature)
.is_ok()
{
(
&first_delegation.area,
&first_delegation.signature,
&first_delegation.user,
)
} else {
return Err(InvalidCapability);
}
}
})
}
fn validate_non_first_delegation<'dele>(
delegation: &'dele Delegation,
prev_area: &'dele Area,
prev_signature: &'dele SubspaceSignature,
prev_receiver: &'dele SubspaceId,
) -> Result<(&'dele Area, &'dele SubspaceSignature, &'dele SubspaceId), InvalidCapability> {
let current_area = &delegation.area;
let mut handover = Vec::with_capacity(
current_area.len_of_relative_encoding(prev_area)
+ prev_signature.len_of_encoding()
+ delegation.user.len_of_encoding(),
);
pollster::block_on(async {
let mut con = (&mut handover).into_consumer();
con.consume_relative_encoded(¤t_area, prev_area)
.await
.unwrap();
con.consume_encoded(prev_signature).await.unwrap();
con.consume_encoded(&delegation.user).await.unwrap();
});
if prev_receiver
.verify(&handover, &delegation.signature)
.is_ok()
{
Ok((current_area, &delegation.signature, &delegation.user))
} else {
Err(InvalidCapability)
}
}
}
impl Encodable for RawCapability {
async fn encode<C>(&self, consumer: &mut C) -> Result<(), C::Error>
where
C: BulkConsumer<Item = u8> + ?Sized,
{
let mut header = 0u8;
if self.is_owned() {
header |= 0b1000_0000;
}
if self.access_mode() == AccessMode::Write {
header |= 0b0100_0000;
}
let delegations_length = self.delegations.len() as u64;
write_tag(&mut header, 6, 2, delegations_length);
consumer.consume_item(header).await?;
let mut prev_area = match self.genesis() {
Genesis::Communal(CommunalGenesis {
namespace_key,
user_key,
..
}) => {
consumer.consume_encoded(namespace_key).await?;
consumer.consume_encoded(user_key).await?;
Area::new_subspace_area(user_key.clone())
}
Genesis::Owned(OwnedGenesis {
namespace_key,
user_key,
initial_authorisation,
..
}) => {
consumer.consume_encoded(namespace_key).await?;
consumer.consume_encoded(user_key).await?;
consumer.consume_encoded(initial_authorisation).await?;
Area::full()
}
};
cu64_encode(delegations_length, 6, consumer).await?;
let mut current_area;
for delegation in self.delegations.iter() {
current_area = delegation.area.clone();
consumer
.consume_relative_encoded(¤t_area, &prev_area)
.await?;
consumer.consume_encoded(&delegation.user).await?;
consumer.consume_encoded(&delegation.signature).await?;
prev_area = current_area;
}
Ok(())
}
}
impl EncodableKnownLength for RawCapability {
fn len_of_encoding(&self) -> usize {
let mut len = 1;
let mut prev_area = match self.genesis() {
Genesis::Communal(CommunalGenesis {
namespace_key,
user_key,
..
}) => {
len += namespace_key.len_of_encoding();
len += user_key.len_of_encoding();
Area::new_subspace_area(user_key.clone())
}
Genesis::Owned(OwnedGenesis {
namespace_key,
user_key,
initial_authorisation,
..
}) => {
len += namespace_key.len_of_encoding();
len += user_key.len_of_encoding();
len += initial_authorisation.len_of_encoding();
Area::full()
}
};
len += cu64_len_of_encoding(6, self.delegations.len() as u64);
let mut current_area;
for delegation in self.delegations.iter() {
current_area = delegation.area.clone();
len += current_area.len_of_relative_encoding(&prev_area);
len += delegation.user.len_of_encoding();
len += delegation.signature.len_of_encoding();
prev_area = current_area;
}
len
}
}
impl Decodable for RawCapability {
type ErrorReason = Blame;
async fn decode<P>(
producer: &mut P,
) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorReason>>
where
P: BulkProducer<Item = u8> + ?Sized,
Self: Sized,
{
let header = producer.produce_item().await?;
let owned = header & 0b1000_0000 > 0;
let access_mode = if header & 0b0100_0000 > 0 {
AccessMode::Write
} else {
AccessMode::Read
};
let namespace_key = producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?;
let user_key: SubspaceId = producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?;
let (genesis, initial_area) = if owned {
(
Genesis::Owned(OwnedGenesis {
access_mode,
namespace_key,
user_key,
initial_authorisation: producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?,
}),
Area::full(),
)
} else {
(
Genesis::Communal(CommunalGenesis {
access_mode,
namespace_key,
user_key: user_key.clone(),
}),
Area::new_subspace_area(user_key),
)
};
let delegations_length = cu64_decode(header, 6, 2, producer)
.await
.map_err(|err| err.map_other(|_| Blame::TheirFault))?;
let mut delegations = Vec::new();
for _ in 0..usize::try_from(delegations_length)
.map_err(|_| DecodeError::Other(Blame::OurFault))?
{
let area = producer
.produce_relative_decoded(
delegations
.last()
.map(|del_ref: &Delegation| &del_ref.area)
.unwrap_or(&initial_area),
)
.await?;
let user = producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?;
let signature = producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?;
delegations.push(Delegation {
area,
user,
signature,
});
}
Ok(Self {
genesis,
delegations,
})
}
}
impl DecodableCanonic for RawCapability {
type ErrorCanonic = Blame;
async fn decode_canonic<P>(
producer: &mut P,
) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorCanonic>>
where
P: BulkProducer<Item = u8> + ?Sized,
Self: Sized,
{
let header = producer.produce_item().await?;
let owned = header & 0b1000_0000 > 0;
let access_mode = if header & 0b0100_0000 > 0 {
AccessMode::Write
} else {
AccessMode::Read
};
let namespace_key = producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?;
let user_key: SubspaceId = producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?;
let (genesis, initial_area) = if owned {
(
Genesis::Owned(OwnedGenesis {
access_mode,
namespace_key,
user_key,
initial_authorisation: producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?,
}),
Area::full(),
)
} else {
(
Genesis::Communal(CommunalGenesis {
access_mode,
namespace_key,
user_key: user_key.clone(),
}),
Area::new_subspace_area(user_key),
)
};
let delegations_length = cu64_decode_canonic(header, 6, 2, producer)
.await
.map_err(|err| err.map_other(|_| Blame::TheirFault))?;
let mut delegations = Vec::new();
for _ in 0..usize::try_from(delegations_length)
.map_err(|_| DecodeError::Other(Blame::OurFault))?
{
let area = producer
.produce_relative_decoded_canonic(
delegations
.last()
.map(|del_ref: &Delegation| &del_ref.area)
.unwrap_or(&initial_area),
)
.await?;
let user = producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?;
let signature = producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?;
delegations.push(Delegation {
area,
user,
signature,
});
}
Ok(Self {
genesis,
delegations,
})
}
}
#[cfg(feature = "dev")]
impl<'a> Arbitrary<'a> for RawCapability {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let genesis = Genesis::arbitrary(u)?;
let delegation_count = usize::arbitrary(u)? % 8;
let mut delegations = Vec::with_capacity(delegation_count);
if delegation_count > 0 {
let area = match &genesis {
Genesis::Owned(_) => Area::full(),
Genesis::Communal(CommunalGenesis { user_key, .. }) => {
Area::new_subspace_area(user_key.clone())
}
};
delegations.push(Delegation {
area,
user: SubspaceId::arbitrary(u)?,
signature: SubspaceSignature::arbitrary(u)?,
});
}
for i in 1..delegation_count {
let prev_area = &delegations.get(i - 1).unwrap().area;
let area = arbitrary_area_in_area(prev_area, u)?;
delegations.push(Delegation {
area,
user: SubspaceId::arbitrary(u)?,
signature: SubspaceSignature::arbitrary(u)?,
});
}
Ok(RawCapability {
genesis,
delegations,
})
}
}