Merge branch 'bridge_containers' into 'main'
Start implementing more data structures to hold Bridge descriptors. See merge request tpo/core/arti!755
This commit is contained in:
commit
1d70bf4ddd
|
@ -3703,6 +3703,7 @@ dependencies = [
|
|||
"tor-netdoc",
|
||||
"tor-persist",
|
||||
"tor-proto",
|
||||
"tor-protover",
|
||||
"tor-rtcompat",
|
||||
"tor-rtmock",
|
||||
"tor-units",
|
||||
|
|
|
@ -19,7 +19,7 @@ experimental = ["bridge-client"]
|
|||
# Support for using bridges as a client. Note that this is not the same as
|
||||
# the pt-client feature, since here we are not concerned with
|
||||
# pluggable transports necessarily.
|
||||
bridge-client = ["tor-netdoc/routerdesc"]
|
||||
bridge-client = ["tor-netdoc/routerdesc", "tor-protover"]
|
||||
# Support for pluggable transports.
|
||||
pt-client = ["bridge-client", "tor-linkspec/pt-client"]
|
||||
|
||||
|
@ -51,6 +51,7 @@ tor-netdir = { path = "../tor-netdir", version = "0.6.0" }
|
|||
tor-netdoc = { path = "../tor-netdoc", version = "0.5.2" } # for address pattern
|
||||
tor-persist = { path = "../tor-persist", version = "0.5.1" }
|
||||
tor-proto = { path = "../tor-proto", version = "0.7.0" }
|
||||
tor-protover = { path = "../tor-protover", version = "0.3.2", optional = true }
|
||||
tor-rtcompat = { path = "../tor-rtcompat", version = "0.7.0" }
|
||||
tor-units = { path = "../tor-units", version = "0.3.1" }
|
||||
tracing = "0.1.18"
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
mod config;
|
||||
mod descs;
|
||||
mod relay;
|
||||
|
||||
pub use config::Bridge;
|
||||
pub use descs::{BridgeDescEvent, BridgeDescList, BridgeDescProvider};
|
||||
pub use descs::{BridgeDesc, BridgeDescEvent, BridgeDescList, BridgeDescProvider};
|
||||
pub use relay::BridgeRelay;
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::str::FromStr;
|
|||
|
||||
use thiserror::Error;
|
||||
|
||||
use tor_linkspec::ChannelMethod;
|
||||
use tor_linkspec::{ChannelMethod, HasRelayIds, RelayIdRef, RelayIdType};
|
||||
use tor_linkspec::{RelayId, RelayIdError, TransportIdError};
|
||||
use tor_llcrypto::pk::{ed25519::Ed25519Identity, rsa::RsaIdentity};
|
||||
|
||||
|
@ -80,6 +80,16 @@ pub struct Bridge {
|
|||
//
|
||||
// (These last two might be part of the same configuration type.)
|
||||
|
||||
impl HasRelayIds for Bridge {
|
||||
fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
|
||||
match key_type {
|
||||
RelayIdType::Ed25519 => self.ed_id.as_ref().map(RelayIdRef::Ed25519),
|
||||
RelayIdType::Rsa => Some(RelayIdRef::Rsa(&self.rsa_id)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error when parsing a bridge line from a string
|
||||
#[derive(Error, Clone, Debug)]
|
||||
#[non_exhaustive]
|
||||
|
|
|
@ -3,19 +3,53 @@
|
|||
//! Here we need to keep track of which bridge descriptors we need, and inform
|
||||
//! the directory manager of them.
|
||||
|
||||
// TODO pt-client: remove these "allow"s.
|
||||
#![allow(clippy::missing_panics_doc)]
|
||||
#![allow(dead_code, unused_variables, clippy::needless_pass_by_value)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::stream::BoxStream;
|
||||
use tor_linkspec::{OwnedChanTarget, RelayId, RelayIds};
|
||||
use tor_linkspec::OwnedChanTarget;
|
||||
use tor_llcrypto::pk::{ed25519::Ed25519Identity, rsa::RsaIdentity};
|
||||
use tor_netdoc::doc::routerdesc::RouterDesc;
|
||||
|
||||
// TODO pt-client: I think we may want another layer of abstraction between
|
||||
// RouterDesc and BridgeDesc, to implement e.g. CircTarget for RouterDesc.
|
||||
// Likely it should contain an Arc<RouterDesc>.
|
||||
use tor_netdoc::doc::routerdesc::RouterDesc as BridgeDesc;
|
||||
/// A router descriptor that can be used to build circuits through a bridge.
|
||||
///
|
||||
/// These descriptors are fetched from the bridges themselves, and used in
|
||||
/// conjunction with configured bridge information and ppluggable transports to
|
||||
/// contact bridges and build circuits through them.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BridgeDesc {
|
||||
/// The inner descriptor.
|
||||
///
|
||||
/// NOTE: This is wrapped in an `Arc<>` because we expect to pass BridgeDesc
|
||||
/// around a bit and clone it frequently. If that doesn't actually happen,
|
||||
/// we can remove the Arc here.
|
||||
desc: Arc<RouterDesc>,
|
||||
}
|
||||
|
||||
impl AsRef<RouterDesc> for BridgeDesc {
|
||||
fn as_ref(&self) -> &RouterDesc {
|
||||
self.desc.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl BridgeDesc {
|
||||
/// Construct a new BridgeDesc from `desc`.
|
||||
///
|
||||
/// The provided `desc` must be a descriptor retrieved from the bridge
|
||||
/// itself.
|
||||
pub fn new(desc: Arc<RouterDesc>) -> Self {
|
||||
Self { desc }
|
||||
}
|
||||
}
|
||||
|
||||
impl tor_linkspec::HasRelayIdsLegacy for BridgeDesc {
|
||||
fn ed_identity(&self) -> &Ed25519Identity {
|
||||
self.desc.ed_identity()
|
||||
}
|
||||
|
||||
fn rsa_identity(&self) -> &RsaIdentity {
|
||||
self.desc.rsa_identity()
|
||||
}
|
||||
}
|
||||
|
||||
/// This is analogous to NetDirProvider.
|
||||
///
|
||||
|
@ -23,6 +57,7 @@ use tor_netdoc::doc::routerdesc::RouterDesc as BridgeDesc;
|
|||
pub trait BridgeDescProvider {
|
||||
/// Return the current set of bridge descriptors.
|
||||
fn bridges(&self) -> Arc<BridgeDescList>;
|
||||
|
||||
/// Return a stream that gets a notification when the set of bridge
|
||||
/// descriptors has changed.
|
||||
fn events(&self) -> BoxStream<'static, BridgeDescEvent>;
|
||||
|
@ -46,52 +81,4 @@ pub enum BridgeDescEvent {
|
|||
}
|
||||
|
||||
/// A set of bridge descriptors, managed and modified by a BridgeDescProvider.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BridgeDescList {
|
||||
/// The known bridges.
|
||||
///
|
||||
/// TODO pt-client: This is almost certainly the wrong data structure; some
|
||||
/// kind of ID-based hashmap is likelier to be right.
|
||||
///
|
||||
/// TODO pt-client: Maybe we should have an intermediary struct between
|
||||
/// RouterDescriptors and "usable bridge", as we have for `Relay`.
|
||||
bridges: Vec<BridgeDesc>,
|
||||
}
|
||||
|
||||
impl BridgeDescList {
|
||||
/// Return the bridge descriptor, if any, for the given `RelayId`.
|
||||
pub fn by_id(&self, id: &RelayId) -> Option<&BridgeDesc> {
|
||||
todo!() // TODO pt-client: implement.
|
||||
}
|
||||
|
||||
/// Return the bridge descriptor, if any, that has all of the given `RelayIds`.
|
||||
pub fn by_ids(&self, id: &RelayIds) -> Option<&BridgeDesc> {
|
||||
todo!() // TODO pt-client: implement.
|
||||
}
|
||||
|
||||
/// Return an iterator over every bridge descriptor in this list.
|
||||
///
|
||||
/// No bridge descriptors will be returned more than once, and no more than
|
||||
/// one descriptor will be returned for any given `RelayId`.
|
||||
pub fn bridges(&self) -> impl Iterator<Item = &BridgeDesc> {
|
||||
todo!(); // TODO pt-client: implement.
|
||||
#[allow(unreachable_code)]
|
||||
[].iter()
|
||||
}
|
||||
|
||||
/// Insert `desc` into this list of bridges.
|
||||
///
|
||||
/// Replace every already-existing descriptor that shares any identity with
|
||||
/// `desc`.
|
||||
pub fn insert(&mut self, desc: BridgeDesc) {
|
||||
todo!() // TODO pt-client: implement.
|
||||
}
|
||||
|
||||
/// Drop every member of this list for which `func` returns false.
|
||||
pub fn retain<F>(&mut self, func: F)
|
||||
where
|
||||
F: FnMut(&BridgeDesc) -> bool,
|
||||
{
|
||||
todo!() // TODO pt-client: implement.
|
||||
}
|
||||
}
|
||||
pub type BridgeDescList = tor_linkspec::ByRelayIds<BridgeDesc>;
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
//! Implementation code to make a bridge something that we can connect to and use to relay traffic.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use tor_linkspec::{ChanTarget, CircTarget, HasAddrs, HasRelayIds, RelayIdRef, RelayIdType};
|
||||
|
||||
use super::{Bridge, BridgeDesc};
|
||||
|
||||
/// The information about a Bridge that is necessary to connect to it and send
|
||||
/// it traffic.
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
pub struct BridgeRelay {
|
||||
/// The local configurations for the bridge.
|
||||
///
|
||||
/// This is _always_ necessary, since it without it we can't know whether
|
||||
/// any pluggable transports are needed.
|
||||
bridge_line: Arc<Bridge>,
|
||||
|
||||
/// A descriptor for the bridge.
|
||||
///
|
||||
/// If present, it MUST have every RelayId that the `bridge_line` does.
|
||||
desc: Option<BridgeDesc>,
|
||||
}
|
||||
|
||||
/// A BridgeRelay that is known to have its full information available, and
|
||||
/// which is therefore usable for multi-hop circuits.
|
||||
///
|
||||
/// (All bridges can be used for single-hop circuits, but we need to know the
|
||||
/// bridge's descriptor in order to construct proper multi-hop circuits
|
||||
/// with forward secrecy through it.)
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BridgeRelayWithDesc<'a>(
|
||||
/// This will _always_ be a bridge relay with a non-None desc.
|
||||
&'a BridgeRelay,
|
||||
);
|
||||
|
||||
impl BridgeRelay {
|
||||
/// Return true if this BridgeRelay has a known descriptor and can be used for relays.
|
||||
pub fn has_descriptor(&self) -> bool {
|
||||
self.desc.is_some()
|
||||
}
|
||||
|
||||
/// If we have enough information about this relay to build a circuit through it,
|
||||
/// return a BridgeRelayWithDesc for it.
|
||||
// TODO pt-client rename XXXX
|
||||
pub fn for_circuit_usage(&self) -> Option<BridgeRelayWithDesc<'_>> {
|
||||
self.desc.is_some().then(|| BridgeRelayWithDesc(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl HasRelayIds for BridgeRelay {
|
||||
fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
|
||||
self.bridge_line
|
||||
.identity(key_type)
|
||||
.or_else(|| self.desc.as_ref().and_then(|d| d.identity(key_type)))
|
||||
}
|
||||
}
|
||||
|
||||
impl HasAddrs for BridgeRelay {
|
||||
fn addrs(&self) -> &[std::net::SocketAddr] {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ChanTarget for BridgeRelay {}
|
||||
|
||||
impl<'a> HasRelayIds for BridgeRelayWithDesc<'a> {
|
||||
fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
|
||||
self.0.identity(key_type)
|
||||
}
|
||||
}
|
||||
impl<'a> HasAddrs for BridgeRelayWithDesc<'a> {
|
||||
fn addrs(&self) -> &[std::net::SocketAddr] {
|
||||
// TODO pt-client: This is a tricky case and we'll need to audit the
|
||||
// semantics of HasAddrs.
|
||||
//
|
||||
// The problem is that the addresses this method returns can be _either_
|
||||
// addresses at which the relay resides, and which we use to detect
|
||||
// familyhood (in which case we should return any addresses from the
|
||||
// members of this object), _or_ they can be addresses which we should
|
||||
// try to contact directly to perform the Tor handshake, in which case
|
||||
// this method should return an empty list.
|
||||
&[]
|
||||
}
|
||||
}
|
||||
impl<'a> ChanTarget for BridgeRelayWithDesc<'a> {}
|
||||
|
||||
impl<'a> BridgeRelayWithDesc<'a> {
|
||||
/// Return a reference to the BridgeDesc in this reference.
|
||||
fn desc(&self) -> &BridgeDesc {
|
||||
self.0
|
||||
.desc
|
||||
.as_ref()
|
||||
.expect("There was supposed to be a descriptor here")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CircTarget for BridgeRelayWithDesc<'a> {
|
||||
fn ntor_onion_key(&self) -> &tor_llcrypto::pk::curve25519::PublicKey {
|
||||
self.desc().as_ref().ntor_onion_key()
|
||||
}
|
||||
|
||||
fn protovers(&self) -> &tor_protover::Protocols {
|
||||
self.desc().as_ref().protocols()
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
MODIFIED: RouterDesc has new accessors.
|
|
@ -41,6 +41,7 @@ use crate::types::policy::*;
|
|||
use crate::types::version::TorVersion;
|
||||
use crate::{doc, AllowAnnotations, Error, ParseErrorKind as EK, Result};
|
||||
|
||||
use ll::pk::ed25519::Ed25519Identity;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Arc;
|
||||
use std::{net, time};
|
||||
|
@ -129,10 +130,14 @@ pub struct RouterDesc {
|
|||
/// signing key)
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
|
||||
identity_cert: tor_cert::Ed25519Cert,
|
||||
/// RSA identity for this relay. (Deprecated; never use this without
|
||||
/// RSA identity key for this relay. (Deprecated; never use this without
|
||||
/// the ed25519 identity as well).
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
|
||||
rsa_identity: ll::pk::rsa::PublicKey,
|
||||
rsa_identity_key: ll::pk::rsa::PublicKey,
|
||||
/// RSA identity key for this relay. (Deprecated; never use this without
|
||||
/// the ed25519 identity as well).
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
|
||||
rsa_identity: ll::pk::rsa::RsaIdentity,
|
||||
/// Key for extending a circuit to this relay using the ntor protocol.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
|
||||
ntor_onion_key: ll::pk::curve25519::PublicKey,
|
||||
|
@ -352,6 +357,29 @@ const ROUTER_EXPIRY_SECONDS: u64 = 5 * 86400;
|
|||
const ROUTER_PRE_VALIDITY_SECONDS: u64 = 86400;
|
||||
|
||||
impl RouterDesc {
|
||||
/// Return a reference to this relay's RSA identity.
|
||||
pub fn rsa_identity(&self) -> &RsaIdentity {
|
||||
&self.rsa_identity
|
||||
}
|
||||
|
||||
/// Return a reference to this relay's Ed25519 identity.
|
||||
pub fn ed_identity(&self) -> &Ed25519Identity {
|
||||
self.identity_cert
|
||||
.signing_key()
|
||||
.expect("No ed25519 identity key on identity cert")
|
||||
}
|
||||
|
||||
/// Return a reference to the list of subprotocol versions supported by this
|
||||
/// relay.
|
||||
pub fn protocols(&self) -> &tor_protover::Protocols {
|
||||
self.proto.as_ref()
|
||||
}
|
||||
|
||||
/// Return a reference to this relay's Ntor onion key.
|
||||
pub fn ntor_onion_key(&self) -> &ll::pk::curve25519::PublicKey {
|
||||
&self.ntor_onion_key
|
||||
}
|
||||
|
||||
/// Helper: tokenize `s`, and divide it into three validated sections.
|
||||
fn parse_sections<'a>(
|
||||
reader: &mut NetDocReader<'a, RouterKwd>,
|
||||
|
@ -453,12 +481,13 @@ impl RouterDesc {
|
|||
}
|
||||
|
||||
// Legacy RSA identity
|
||||
let rsa_identity: ll::pk::rsa::PublicKey = body
|
||||
let rsa_identity_key: ll::pk::rsa::PublicKey = body
|
||||
.required(SIGNING_KEY)?
|
||||
.parse_obj::<RsaPublic>("RSA PUBLIC KEY")?
|
||||
.check_len_eq(1024)?
|
||||
.check_exponent(65537)?
|
||||
.into();
|
||||
let rsa_identity = rsa_identity_key.to_rsa_identity();
|
||||
|
||||
let ed_sig = sig.required(ROUTER_SIG_ED25519)?;
|
||||
let rsa_sig = sig.required(ROUTER_SIGNATURE)?;
|
||||
|
@ -498,7 +527,7 @@ impl RouterDesc {
|
|||
let sig = rsa_sig.obj("SIGNATURE")?;
|
||||
// TODO: we need to accept prefixes here. COMPAT BLOCKER.
|
||||
|
||||
ll::pk::rsa::ValidatableRsaSignature::new(&rsa_identity, &sig, &d)
|
||||
ll::pk::rsa::ValidatableRsaSignature::new(&rsa_identity_key, &sig, &d)
|
||||
};
|
||||
|
||||
// router nickname ipv4addr orport socksport dirport
|
||||
|
@ -562,7 +591,7 @@ impl RouterDesc {
|
|||
let cc_tok = body.required(ONION_KEY_CROSSCERT)?;
|
||||
let cc_val = cc_tok.obj("CROSSCERT")?;
|
||||
let mut signed = Vec::new();
|
||||
signed.extend(rsa_identity.to_rsa_identity().as_bytes());
|
||||
signed.extend(rsa_identity.as_bytes());
|
||||
signed.extend(identity_cert.peek_signing_key().as_bytes());
|
||||
ll::pk::rsa::ValidatableRsaSignature::new(&tap_onion_key, &cc_val, &signed)
|
||||
};
|
||||
|
@ -587,7 +616,7 @@ impl RouterDesc {
|
|||
// fingerprint: check for consistency with RSA identity.
|
||||
if let Some(fp_tok) = body.get(FINGERPRINT) {
|
||||
let fp: RsaIdentity = fp_tok.args_as_str().parse::<SpFingerprint>()?.into();
|
||||
if fp != rsa_identity.to_rsa_identity() {
|
||||
if fp != rsa_identity {
|
||||
return Err(EK::BadArgument
|
||||
.at_pos(fp_tok.pos())
|
||||
.with_msg("fingerprint does not match RSA identity"));
|
||||
|
@ -606,7 +635,7 @@ impl RouterDesc {
|
|||
// canonical family shared by all of the members of this family.
|
||||
// If the family is empty, there's no point in adding our own ID
|
||||
// to it, and doing so would only waste memory.
|
||||
family.push(rsa_identity.to_rsa_identity());
|
||||
family.push(rsa_identity);
|
||||
}
|
||||
family.intern()
|
||||
};
|
||||
|
@ -701,6 +730,7 @@ impl RouterDesc {
|
|||
uptime,
|
||||
published,
|
||||
identity_cert,
|
||||
rsa_identity_key,
|
||||
rsa_identity,
|
||||
ntor_onion_key,
|
||||
tap_onion_key,
|
||||
|
|
Loading…
Reference in New Issue