mod client;
mod hub;
pub use self::client::{
issue_ticket, ClientError, Delivery, DeviceCredential, Event, RelaySession, SealError, Sealer,
};
pub use self::hub::{ConnId, Outgoing, RelayCore, RelayError, RelayLimits};
use crate::remote::util::{hex, unhex};
use ring::digest::{digest, SHA256};
use serde::{Deserialize, Serialize};
pub const PROTOCOL: &str = "rk-relay/1";
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub struct MailboxId(pub [u8; 32]);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub struct CircleId(pub [u8; 32]);
fn domain_hash(domain: &str, key: &[u8; 32]) -> [u8; 32] {
let mut input = Vec::with_capacity(domain.len() + 32);
input.extend_from_slice(domain.as_bytes());
input.extend_from_slice(key);
let mut out = [0u8; 32];
out.copy_from_slice(digest(&SHA256, &input).as_ref());
out
}
impl MailboxId {
pub fn of_device(device_pub: &[u8; 32]) -> Self {
Self(domain_hash("rk-relay/1 mailbox", device_pub))
}
pub fn to_hex(&self) -> String {
hex(&self.0)
}
pub fn from_hex(text: &str) -> Option<Self> {
unhex_array::<32>(text).map(Self)
}
}
impl CircleId {
pub fn of_owner(owner_pub: &[u8; 32]) -> Self {
Self(domain_hash("rk-relay/1 circle", owner_pub))
}
pub fn to_hex(&self) -> String {
hex(&self.0)
}
pub fn from_hex(text: &str) -> Option<Self> {
unhex_array::<32>(text).map(Self)
}
}
pub(crate) fn unhex_array<const N: usize>(text: &str) -> Option<[u8; N]> {
let bytes = unhex(text)?;
<[u8; N]>::try_from(bytes.as_slice()).ok()
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Class {
Small,
Clipboard,
Transfer,
Screen,
}
pub fn ticket_message(owner_pub: &[u8; 32], device_pub: &[u8; 32], exp: u64) -> Vec<u8> {
let mut m = Vec::with_capacity(PROTOCOL.len() + 8 + 32 + 32 + 8);
m.extend_from_slice(PROTOCOL.as_bytes());
m.extend_from_slice(b" ticket");
m.extend_from_slice(owner_pub);
m.extend_from_slice(device_pub);
m.extend_from_slice(&exp.to_be_bytes());
m
}
pub fn auth_message(nonce: &[u8; 32], device_pub: &[u8; 32]) -> Vec<u8> {
let mut m = Vec::with_capacity(PROTOCOL.len() + 6 + 32 + 32);
m.extend_from_slice(PROTOCOL.as_bytes());
m.extend_from_slice(b" auth");
m.extend_from_slice(nonce);
m.extend_from_slice(device_pub);
m
}
pub mod code {
pub const BAD_FRAME: &str = "bad_frame";
pub const UNAUTHENTICATED: &str = "unauthenticated";
pub const AUTH_FAILED: &str = "auth_failed";
pub const EXPIRED: &str = "expired";
pub const TOO_LARGE: &str = "too_large";
pub const DIRECT_ONLY: &str = "direct_only";
pub const QUOTA_EXCEEDED: &str = "quota_exceeded";
pub const QUEUE_FULL: &str = "queue_full";
pub const BUSY: &str = "busy";
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ClientFrame {
Auth {
owner_pub: String,
device_pub: String,
exp: u64,
ticket_sig: String,
challenge_sig: String,
},
Put {
id: String,
to: String,
class: Class,
body: String,
},
Fetch {
limit: u32,
},
Ack {
ids: Vec<String>,
},
Ping,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ServerFrame {
Challenge {
nonce: String,
},
Authed {
mailbox: String,
circle: String,
},
Stored {
id: String,
seq: u64,
},
Deliver {
id: String,
seq: u64,
from: String,
class: Class,
body: String,
},
Acked {
count: u32,
},
Error {
code: String,
message: String,
retry_after: Option<u64>,
},
Pong,
}
impl ServerFrame {
pub(crate) fn error(code: &str, message: &str) -> Self {
ServerFrame::Error {
code: code.into(),
message: message.into(),
retry_after: None,
}
}
}