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:
Nick Mathewson 2022-10-06 17:16:44 +00:00
commit 1d70bf4ddd
8 changed files with 207 additions and 68 deletions

1
Cargo.lock generated
View File

@ -3703,6 +3703,7 @@ dependencies = [
"tor-netdoc",
"tor-persist",
"tor-proto",
"tor-protover",
"tor-rtcompat",
"tor-rtmock",
"tor-units",

View File

@ -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"

View File

@ -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;

View File

@ -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]

View File

@ -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>;

View File

@ -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()
}
}

View File

@ -0,0 +1 @@
MODIFIED: RouterDesc has new accessors.

View File

@ -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,