use super::pow::ProofOfWork;
use crate::relaycell::{extend::CircRequestExt, extlist::ExtList};
use caret::caret_int;
use tor_bytes::{EncodeError, EncodeResult, Error, Readable, Reader, Result, Writeable, Writer};
use tor_hscrypto::RendCookie;
use tor_linkspec::EncodedLinkSpec;
caret_int! {
struct OnionKeyType(u8) {
NTOR = 0x01,
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum OnionKey {
NtorOnionKey(tor_llcrypto::pk::curve25519::PublicKey),
}
impl Readable for OnionKey {
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
let kind: OnionKeyType = r.take_u8()?.into();
r.read_nested_u16len(|r_inner| match kind {
OnionKeyType::NTOR => Ok(OnionKey::NtorOnionKey(r_inner.extract()?)),
_ => Err(Error::InvalidMessage(
format!("Unrecognized onion key type {kind}").into(),
)),
})
}
}
impl Writeable for OnionKey {
fn write_onto<B: Writer + ?Sized>(&self, w: &mut B) -> EncodeResult<()> {
match self {
OnionKey::NtorOnionKey(key) => {
w.write_u8(OnionKeyType::NTOR.into());
let mut w_inner = w.write_nested_u16len();
w_inner.write(key)?;
w_inner.finish()?;
}
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct IntroduceHandshakePayload {
cookie: RendCookie,
extensions: ExtList<CircRequestExt>,
onion_key: OnionKey,
link_specifiers: Vec<EncodedLinkSpec>,
}
impl Readable for IntroduceHandshakePayload {
fn take_from(r: &mut Reader<'_>) -> Result<Self> {
let cookie = r.extract()?;
let extensions = r.extract()?;
let onion_key = r.extract()?;
let n_link_specifiers = r.take_u8()?;
let link_specifiers = r.extract_n(n_link_specifiers.into())?;
Ok(Self {
cookie,
extensions,
onion_key,
link_specifiers,
})
}
}
impl Writeable for IntroduceHandshakePayload {
fn write_onto<B: Writer + ?Sized>(&self, w: &mut B) -> EncodeResult<()> {
w.write(&self.cookie)?;
w.write(&self.extensions)?;
w.write(&self.onion_key)?;
w.write_u8(
self.link_specifiers
.len()
.try_into()
.map_err(|_| EncodeError::BadLengthValue)?,
);
self.link_specifiers.iter().try_for_each(|ls| w.write(ls))?;
Ok(())
}
}
impl IntroduceHandshakePayload {
pub fn new(
cookie: RendCookie,
onion_key: OnionKey,
link_specifiers: Vec<EncodedLinkSpec>,
proof_of_work: Option<ProofOfWork>,
) -> Self {
let mut extensions = ExtList::default();
if let Some(proof_of_work) = proof_of_work {
extensions.push(proof_of_work.into());
}
Self {
cookie,
extensions,
onion_key,
link_specifiers,
}
}
pub fn cookie(&self) -> &RendCookie {
&self.cookie
}
pub fn onion_key(&self) -> &OnionKey {
&self.onion_key
}
pub fn link_specifiers(&self) -> &[EncodedLinkSpec] {
&self.link_specifiers[..]
}
pub fn proof_of_work_extension(&self) -> Option<&ProofOfWork> {
self.extensions.extensions.iter().find_map(|x| match x {
CircRequestExt::ProofOfWork(x) => Some(x),
_ => None,
})
}
}