use safelog::Redactable;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
use std::net::SocketAddr;
use tor_config::impl_standard_builder;
use tor_llcrypto::pk;
use crate::{
ChanTarget, ChannelMethod, CircTarget, HasAddrs, HasChanMethod, HasRelayIds, RelayIdRef,
RelayIdType,
};
#[derive(
Debug,
Clone,
Eq,
PartialEq,
Hash,
PartialOrd,
Ord,
Serialize,
Deserialize,
derive_builder::Builder,
)]
#[builder(derive(Debug))]
pub struct RelayIds {
#[serde(rename = "ed25519")]
#[builder(default, setter(strip_option))]
ed_identity: Option<pk::ed25519::Ed25519Identity>,
#[serde(rename = "rsa")]
#[builder(default, setter(strip_option))]
rsa_identity: Option<pk::rsa::RsaIdentity>,
}
impl_standard_builder! { RelayIds : !Deserialize + !Builder + !Default }
impl HasRelayIds for RelayIds {
fn identity(&self, key_type: RelayIdType) -> Option<crate::RelayIdRef<'_>> {
match key_type {
RelayIdType::Ed25519 => self.ed_identity.as_ref().map(RelayIdRef::from),
RelayIdType::Rsa => self.rsa_identity.as_ref().map(RelayIdRef::from),
}
}
}
impl RelayIds {
pub fn empty() -> Self {
Self {
ed_identity: None,
rsa_identity: None,
}
}
pub fn from_relay_ids<T: HasRelayIds + ?Sized>(other: &T) -> Self {
Self {
ed_identity: other
.identity(RelayIdType::Ed25519)
.map(|r| *r.unwrap_ed25519()),
rsa_identity: other.identity(RelayIdType::Rsa).map(|r| *r.unwrap_rsa()),
}
}
}
impl std::fmt::Display for RelayIds {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.display_relay_ids())
}
}
impl Redactable for RelayIds {
fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.display_relay_ids().redacted())
}
}
#[derive(Debug, Clone, derive_builder::Builder)]
#[builder(derive(Debug))]
pub struct OwnedChanTarget {
#[builder(default)]
addrs: Vec<SocketAddr>,
#[builder(default = "self.make_method()")]
method: ChannelMethod,
#[builder(sub_builder)]
ids: RelayIds,
}
impl_standard_builder! { OwnedChanTarget : !Deserialize + !Builder + !Default }
impl OwnedChanTargetBuilder {
pub fn ed_identity(&mut self, id: pk::ed25519::Ed25519Identity) -> &mut Self {
self.ids().ed_identity(id);
self
}
pub fn rsa_identity(&mut self, id: pk::rsa::RsaIdentity) -> &mut Self {
self.ids().rsa_identity(id);
self
}
fn make_method(&self) -> ChannelMethod {
ChannelMethod::Direct(self.addrs.clone().unwrap_or_default())
}
}
impl HasAddrs for OwnedChanTarget {
fn addrs(&self) -> &[SocketAddr] {
&self.addrs[..]
}
}
impl HasChanMethod for OwnedChanTarget {
fn chan_method(&self) -> ChannelMethod {
self.method.clone()
}
}
impl HasRelayIds for OwnedChanTarget {
fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
self.ids.identity(key_type)
}
}
impl ChanTarget for OwnedChanTarget {}
impl OwnedChanTarget {
pub fn from_chan_target<C>(target: &C) -> Self
where
C: ChanTarget + ?Sized,
{
OwnedChanTarget {
addrs: target.addrs().to_vec(),
method: target.chan_method(),
ids: RelayIds::from_relay_ids(target),
}
}
pub fn chan_method_mut(&mut self) -> &mut ChannelMethod {
&mut self.method
}
}
impl Display for OwnedChanTarget {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.display_chan_target())
}
}
impl Redactable for OwnedChanTarget {
fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.display_chan_target().display_redacted(f)
}
fn debug_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.display_chan_target().debug_redacted(f)
}
}
#[derive(Debug, Clone, derive_builder::Builder)]
#[builder(derive(Debug))]
pub struct OwnedCircTarget {
#[builder(sub_builder)]
chan_target: OwnedChanTarget,
ntor_onion_key: pk::curve25519::PublicKey,
protocols: tor_protover::Protocols,
}
impl_standard_builder! { OwnedCircTarget : !Deserialize + !Builder + !Default }
impl OwnedCircTarget {
pub fn from_circ_target<C>(target: &C) -> Self
where
C: CircTarget + ?Sized,
{
OwnedCircTarget {
chan_target: OwnedChanTarget::from_chan_target(target),
ntor_onion_key: *target.ntor_onion_key(),
protocols: target.protovers().clone(),
}
}
pub fn chan_target_mut(&mut self) -> &mut OwnedChanTarget {
&mut self.chan_target
}
}
impl HasAddrs for OwnedCircTarget {
fn addrs(&self) -> &[SocketAddr] {
self.chan_target.addrs()
}
}
impl HasRelayIds for OwnedCircTarget {
fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
self.chan_target.identity(key_type)
}
}
impl HasChanMethod for OwnedCircTarget {
fn chan_method(&self) -> ChannelMethod {
self.chan_target.chan_method()
}
}
impl ChanTarget for OwnedCircTarget {}
impl CircTarget for OwnedCircTarget {
fn ntor_onion_key(&self) -> &pk::curve25519::PublicKey {
&self.ntor_onion_key
}
fn protovers(&self) -> &tor_protover::Protocols {
&self.protocols
}
}
pub trait IntoOwnedChanTarget {
fn to_owned(self) -> OwnedChanTarget;
fn to_logged(self) -> LoggedChanTarget
where
Self: Sized,
{
self.to_owned().into()
}
}
impl<'a, T: ChanTarget + ?Sized> IntoOwnedChanTarget for &'a T {
fn to_owned(self) -> OwnedChanTarget {
OwnedChanTarget::from_chan_target(self)
}
}
impl IntoOwnedChanTarget for OwnedChanTarget {
fn to_owned(self) -> OwnedChanTarget {
self
}
}
pub type LoggedChanTarget = safelog::BoxSensitive<OwnedChanTarget>;
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_duration_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use super::*;
#[test]
#[allow(clippy::redundant_clone)]
fn chan_target() {
let ti = OwnedChanTarget::builder()
.addrs(vec!["127.0.0.1:11".parse().unwrap()])
.ed_identity([42; 32].into())
.rsa_identity([45; 20].into())
.build()
.unwrap();
let ti2 = OwnedChanTarget::from_chan_target(&ti);
assert_eq!(ti.addrs(), ti2.addrs());
assert!(ti.same_relay_ids(&ti2));
assert_eq!(format!("{:?}", ti), format!("{:?}", ti2));
assert_eq!(format!("{:?}", ti), format!("{:?}", ti.clone()));
}
#[test]
#[allow(clippy::redundant_clone)]
fn circ_target() {
let mut builder = OwnedCircTarget::builder();
builder
.chan_target()
.addrs(vec!["127.0.0.1:11".parse().unwrap()])
.ed_identity([42; 32].into())
.rsa_identity([45; 20].into());
let ct = builder
.ntor_onion_key([99; 32].into())
.protocols("FlowCtrl=7".parse().unwrap())
.build()
.unwrap();
let ch = ct.chan_target.clone();
assert_eq!(ct.addrs(), ch.addrs());
assert!(ct.same_relay_ids(&ch));
assert_eq!(ct.ntor_onion_key().as_bytes(), &[99; 32]);
assert_eq!(&ct.protovers().to_string(), "FlowCtrl=7");
let ct2 = OwnedCircTarget::from_circ_target(&ct);
assert_eq!(format!("{:?}", ct), format!("{:?}", ct2));
assert_eq!(format!("{:?}", ct), format!("{:?}", ct.clone()));
}
#[test]
fn format_relay_ids() {
let mut builder = RelayIds::builder();
builder
.ed_identity([42; 32].into())
.rsa_identity([45; 20].into());
let ids = builder.build().unwrap();
assert_eq!(format!("{}", ids), "ed25519:KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKio $2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d");
assert_eq!(format!("{}", ids.redacted()), "ed25519:Ki…");
}
}