use std::ops::Add;
use anyhow::{bail, ensure, Context, Result};
use ed25519_dalek::{
ed25519::signature::Signer, Signature, SigningKey, VerifyingKey, SIGNATURE_LENGTH,
};
use n0_future::time::{Duration, SystemTime};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
pub const VERSION: u8 = 1;
pub const DST: &[u8] = b"rcan-1-delegation";
mod verifying_key_serde {
use ed25519_dalek::VerifyingKey;
use serde::{de::Error, Deserializer, Serializer};
pub fn serialize<S: Serializer>(
key: &VerifyingKey,
serializer: S,
) -> std::result::Result<S::Ok, S::Error> {
serdect::array::serialize_hex_lower_or_bin(key.as_bytes(), serializer)
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> std::result::Result<VerifyingKey, D::Error> {
let mut buf = [0u8; 32];
serdect::array::deserialize_hex_or_bin(&mut buf, deserializer)?;
VerifyingKey::from_bytes(&buf).map_err(D::Error::custom)
}
}
struct SignatureWire([u8; SIGNATURE_LENGTH]);
impl Serialize for SignatureWire {
fn serialize<S: serde::Serializer>(
&self,
serializer: S,
) -> std::result::Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
serializer.collect_str(&format_args!("{}", hex::encode(self.0)))
} else {
use serde::ser::SerializeTuple;
let mut tup = serializer.serialize_tuple(SIGNATURE_LENGTH)?;
for b in &self.0 {
tup.serialize_element(b)?;
}
tup.end()
}
}
}
impl<'de> Deserialize<'de> for SignatureWire {
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> std::result::Result<Self, D::Error> {
struct V;
impl<'de> serde::de::Visitor<'de> for V {
type Value = SignatureWire;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "an ed25519 signature ({} bytes)", SIGNATURE_LENGTH)
}
fn visit_str<E: serde::de::Error>(
self,
v: &str,
) -> std::result::Result<Self::Value, E> {
let mut bytes = [0u8; SIGNATURE_LENGTH];
hex::decode_to_slice(v, &mut bytes).map_err(E::custom)?;
Ok(SignatureWire(bytes))
}
fn visit_bytes<E: serde::de::Error>(
self,
v: &[u8],
) -> std::result::Result<Self::Value, E> {
if v.len() != SIGNATURE_LENGTH {
return Err(E::invalid_length(v.len(), &self));
}
let mut bytes = [0u8; SIGNATURE_LENGTH];
bytes.copy_from_slice(v);
Ok(SignatureWire(bytes))
}
fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut bytes = [0u8; SIGNATURE_LENGTH];
for (i, slot) in bytes.iter_mut().enumerate() {
*slot = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(i, &self))?;
}
Ok(SignatureWire(bytes))
}
}
if deserializer.is_human_readable() {
deserializer.deserialize_str(V)
} else {
deserializer.deserialize_tuple(SIGNATURE_LENGTH, V)
}
}
}
pub trait Capability: Serialize {
fn permits(&self, other: &Self) -> bool;
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Authorizer {
identity: VerifyingKey,
}
impl Authorizer {
pub fn new(identity: VerifyingKey) -> Self {
Self { identity }
}
pub fn check_invocation_from<C: Capability>(
&self,
invoker: VerifyingKey,
capability: C,
proof_chain: &[&Rcan<C>],
) -> Result<()> {
let now = SystemTime::now();
let mut current_issuer_target = &self.identity;
for proof in proof_chain {
let issuer = &proof.payload.issuer;
let audience = &proof.payload.audience;
ensure!(
issuer == current_issuer_target,
"invocation failed: expected proof to be issued by {}, but was issued by {}",
hex::encode(current_issuer_target),
hex::encode(issuer),
);
let expiry = &proof.payload.valid_until;
ensure!(
expiry.is_valid_at(now),
"invocation failed: proof expired at {expiry}"
);
ensure!(
proof.capability_issuer() == &self.identity,
"invocation failed: proof is missing delegation for capability of {}",
hex::encode(self.identity)
);
ensure!(
proof.payload.capability().permits(&capability),
"invocation failed"
);
current_issuer_target = audience;
}
ensure!(
&invoker == current_issuer_target,
"invocation failed: expected delegation chain to end in the connection's owner {}, but the connection is authenticated by {} instead",
hex::encode(invoker),
hex::encode(current_issuer_target),
);
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Rcan<C> {
pub payload: Payload<C>,
pub signature: Signature,
}
impl<C: Serialize> Serialize for Rcan<C> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeTuple;
let mut tup = serializer.serialize_tuple(2)?;
tup.serialize_element(&self.payload)?;
tup.serialize_element(&SignatureWire(self.signature.to_bytes()))?;
tup.end()
}
}
impl<'de, C: Deserialize<'de> + Serialize> Deserialize<'de> for Rcan<C> {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct RcanVisitor<C>(std::marker::PhantomData<C>);
impl<'de, C: Deserialize<'de> + Serialize> serde::de::Visitor<'de> for RcanVisitor<C> {
type Value = Rcan<C>;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("an rcan token (payload, signature)")
}
fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let payload: Payload<C> = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
let SignatureWire(sig_bytes) = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
let rcan = Rcan {
payload,
signature: Signature::from_bytes(&sig_bytes),
};
rcan.verify_signature().map_err(serde::de::Error::custom)?;
Ok(rcan)
}
}
deserializer.deserialize_tuple(2, RcanVisitor::<C>(std::marker::PhantomData))
}
}
#[derive(Clone, Serialize, Deserialize, derive_more::Debug, PartialEq, Eq)]
pub struct Payload<C> {
#[debug("{}", hex::encode(issuer))]
#[serde(with = "verifying_key_serde")]
issuer: VerifyingKey,
#[debug("{}", hex::encode(audience))]
#[serde(with = "verifying_key_serde")]
audience: VerifyingKey,
capability_origin: CapabilityOrigin,
capability: C,
valid_until: Expires,
}
impl<C> Payload<C> {
pub fn capability(&self) -> &C {
&self.capability
}
pub fn capability_origin(&self) -> &CapabilityOrigin {
&self.capability_origin
}
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
pub enum CapabilityOrigin {
Issuer,
Delegation(#[serde(with = "verifying_key_serde")] VerifyingKey),
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, derive_more::Display)]
pub enum Expires {
#[display("never")]
Never,
#[display("{_0}")]
At(u64),
}
pub struct RcanBuilder<'s, C> {
issuer: &'s SigningKey,
audience: VerifyingKey,
capability_origin: CapabilityOrigin,
capability: C,
}
impl<C> Rcan<C> {
pub fn issuing_builder(
issuer: &SigningKey,
audience: VerifyingKey,
capability: C,
) -> RcanBuilder<'_, C> {
RcanBuilder {
issuer,
audience,
capability_origin: CapabilityOrigin::Issuer,
capability,
}
}
pub fn delegating_builder(
issuer: &SigningKey,
audience: VerifyingKey,
owner: VerifyingKey,
capability: C,
) -> RcanBuilder<'_, C> {
RcanBuilder {
issuer,
audience,
capability_origin: CapabilityOrigin::Delegation(owner),
capability,
}
}
pub fn encode(&self) -> Vec<u8>
where
C: Serialize,
{
postcard::to_extend(self, vec![VERSION]).expect("vec")
}
pub fn decode(bytes: &[u8]) -> Result<Self>
where
C: DeserializeOwned + Serialize,
{
let Some(version) = bytes.first() else {
bail!("cannot decode, token is empty");
};
ensure!(*version == VERSION, "invalid version: {}", version);
let rcan: Self = postcard::from_bytes(&bytes[1..]).context("decoding")?;
Ok(rcan)
}
fn verify_signature(&self) -> Result<()>
where
C: Serialize,
{
let signed = postcard::to_extend(&self.payload, DST.to_vec())?;
self.payload
.issuer
.verify_strict(&signed, &self.signature)?;
Ok(())
}
pub fn audience(&self) -> &VerifyingKey {
&self.payload.audience
}
pub fn issuer(&self) -> &VerifyingKey {
&self.payload.issuer
}
pub fn capability(&self) -> &C {
self.payload.capability()
}
pub fn capability_origin(&self) -> &CapabilityOrigin {
self.payload.capability_origin()
}
pub fn capability_issuer(&self) -> &VerifyingKey {
match self.payload.capability_origin() {
CapabilityOrigin::Issuer => &self.payload.issuer,
CapabilityOrigin::Delegation(ref root) => root,
}
}
pub fn expires(&self) -> &Expires {
&self.payload.valid_until
}
}
impl<C> RcanBuilder<'_, C> {
pub fn sign(self, valid_until: Expires) -> Rcan<C>
where
C: Serialize,
{
let payload = Payload {
issuer: self.issuer.verifying_key(),
audience: self.audience,
capability_origin: self.capability_origin,
capability: self.capability,
valid_until,
};
let to_sign = postcard::to_extend(&payload, DST.to_vec()).expect("vec");
let signature = self.issuer.sign(&to_sign);
Rcan { signature, payload }
}
}
impl Expires {
pub fn valid_for(duration: Duration) -> Self {
Self::At(
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("now is after UNIX_EPOCH")
.add(duration)
.as_secs(),
)
}
pub fn is_valid_at(&self, time: SystemTime) -> bool {
let time = time
.duration_since(SystemTime::UNIX_EPOCH)
.expect("time must be after UNIX_EPOCH")
.as_secs();
match self {
Expires::Never => true,
Expires::At(expiry) => *expiry >= time,
}
}
}
#[cfg(test)]
mod test {
use testresult::TestResult;
use super::*;
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
enum Rpc {
Read,
ReadWrite,
All,
}
impl Capability for Rpc {
fn permits(&self, other: &Self) -> bool {
match (self, other) {
(Rpc::All, _) => true,
(Rpc::ReadWrite, Rpc::ReadWrite | Rpc::Read) => true,
(Rpc::ReadWrite, _) => false,
(Rpc::Read, Rpc::Read) => true,
(Rpc::Read, _) => false,
}
}
}
#[test]
fn test_simple_capabilitys() {
assert!(Rpc::Read.permits(&Rpc::Read));
assert!(Rpc::ReadWrite.permits(&Rpc::Read));
assert!(Rpc::ReadWrite.permits(&Rpc::ReadWrite),);
assert!(!Rpc::Read.permits(&Rpc::ReadWrite));
assert!(!Rpc::Read.permits(&Rpc::All));
assert!(Rpc::All.permits(&Rpc::All));
assert!(Rpc::All.permits(&Rpc::Read));
assert!(Rpc::All.permits(&Rpc::ReadWrite));
}
#[test]
fn test_rcan_encoding() -> TestResult {
let issuer = SigningKey::from_bytes(&[0u8; 32]);
let audience = SigningKey::from_bytes(&[1u8; 32]);
let rcan = Rcan::issuing_builder(&issuer, audience.verifying_key(), Rpc::ReadWrite)
.sign(Expires::Never);
println!("{}", hex::encode(rcan.encode()));
println!(
"{}",
hex::encode(postcard::to_allocvec(&rcan.signature).unwrap())
);
let expected: String = [
"01",
"203b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29",
"208a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c",
"00",
"01",
"00",
"54675ed0b6ba3a830fe24ec8523f776fa43001edfe4cc9e3bd639009a2058b1805de5e05958b46c03b423ed5d1c72acaab48a9f3bf8db2402c82295f085df404",
]
.join("");
assert_eq!(hex::encode(rcan.encode()), expected);
assert_eq!(Rcan::decode(&rcan.encode())?, rcan);
Ok(())
}
#[test]
fn deserialize_rejects_forged_signature() {
let issuer = SigningKey::from_bytes(&[0u8; 32]);
let audience = SigningKey::from_bytes(&[1u8; 32]);
let rcan = Rcan::issuing_builder(&issuer, audience.verifying_key(), Rpc::ReadWrite)
.sign(Expires::Never);
let mut wire = postcard::to_stdvec(&rcan).unwrap();
assert_eq!(postcard::from_bytes::<Rcan<Rpc>>(&wire).unwrap(), rcan);
let n = wire.len();
wire[n - SIGNATURE_LENGTH..].fill(0);
assert!(postcard::from_bytes::<Rcan<Rpc>>(&wire).is_err());
}
#[test]
fn test_rcan_invocation() -> TestResult {
let service = SigningKey::from_bytes(&[0u8; 32]);
let alice = SigningKey::from_bytes(&[1u8; 32]);
let bob = SigningKey::from_bytes(&[2u8; 32]);
let service_rcan = Rcan::issuing_builder(&service, alice.verifying_key(), Rpc::All)
.sign(Expires::valid_for(Duration::from_secs(60)));
let friend_rcan = Rcan::delegating_builder(
&alice,
bob.verifying_key(),
service.verifying_key(),
Rpc::Read,
)
.sign(Expires::Never);
let service_auth = Authorizer::new(service.verifying_key());
assert!(service_auth
.check_invocation_from(
bob.verifying_key(),
Rpc::Read,
&[&service_rcan, &friend_rcan],
)
.is_ok());
assert!(service_auth
.check_invocation_from(
bob.verifying_key(),
Rpc::ReadWrite,
&[&service_rcan, &friend_rcan]
)
.is_err());
Ok(())
}
#[test]
fn test_expiry() {
let issuer = SigningKey::from_bytes(&[0u8; 32]);
let audience = SigningKey::from_bytes(&[1u8; 32]).verifying_key();
let rcan = Rcan::issuing_builder(&issuer, audience, Rpc::All)
.sign(Expires::valid_for(Duration::from_secs(60)));
assert!(rcan.expires().is_valid_at(SystemTime::UNIX_EPOCH));
let now = SystemTime::now();
assert!(rcan.expires().is_valid_at(now));
let future = now + Duration::from_secs(61);
assert!(!rcan.expires().is_valid_at(future));
}
}