Skip to content

Commit d8c8c8e

Browse files
feat(iroh)!: switch TLS authentication to raw public keys (#2937)
## Description This introduces the configuration of the TLS authentication method, allowing to enable the usage of raw public keys, which will lead to us being able to remove the hack of using self signed certificates. ## Breaking Changes By default iroh endpoints will now use the new `RawPublicKey` TLS configuration. This means they will not be able to talk to older iroh nodes. If needed, the old mechanism can still be used by configuring the endpoint like this ```rust let endpoint = Endpoint::builder() .tls_x509() // <--- this enables the old style TLS authentication // ... .bind(); ``` Closes #2798 ## Change checklist - [x] Self-review. - [x] Documentation updates following the [style guide](https://rust-lang.github.io/rfcs/1574-more-api-documentation-conventions.html#appendix-a-full-conventions-text), if relevant. - [x] Tests if relevant. - [x] All breaking changes documented. --------- Co-authored-by: Philipp Krüger <[email protected]>
1 parent 3e16848 commit d8c8c8e

File tree

11 files changed

+719
-869
lines changed

11 files changed

+719
-869
lines changed

Cargo.lock

Lines changed: 175 additions & 171 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

iroh/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ derive_more = { version = "1.0.0", features = [
3838
"deref",
3939
"from_str"
4040
] }
41-
ed25519-dalek = "2.0"
41+
ed25519-dalek = { version = "2.0.0", features = ["serde", "rand_core", "zeroize", "pkcs8", "pem"] }
4242
http = "1"
4343
iroh-base = { version = "0.33.0", default-features = false, features = ["key", "relay"], path = "../iroh-base" }
4444
iroh-relay = { version = "0.33", path = "../iroh-relay", default-features = false }

iroh/bench/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ iroh = { path = ".." }
1313
iroh-metrics = "0.32"
1414
n0-future = "0.1.1"
1515
quinn = { package = "iroh-quinn", version = "0.13" }
16+
rand = "0.8"
1617
rcgen = "0.13"
1718
rustls = { version = "0.23", default-features = false, features = ["ring"] }
1819
clap = { version = "4", features = ["derive"] }

iroh/src/endpoint.rs

Lines changed: 81 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use std::{
2323

2424
use anyhow::{bail, Context, Result};
2525
use data_encoding::BASE32_DNSSEC;
26+
use ed25519_dalek::{pkcs8::DecodePublicKey, VerifyingKey};
2627
use iroh_base::{NodeAddr, NodeId, RelayUrl, SecretKey};
2728
use iroh_relay::RelayMap;
2829
use n0_future::{time::Duration, Stream};
@@ -126,6 +127,7 @@ pub struct Builder {
126127
addr_v6: Option<SocketAddrV6>,
127128
#[cfg(any(test, feature = "test-utils"))]
128129
path_selection: PathSelection,
130+
tls_auth: tls::Authentication,
129131
}
130132

131133
impl Default for Builder {
@@ -150,6 +152,7 @@ impl Default for Builder {
150152
addr_v6: None,
151153
#[cfg(any(test, feature = "test-utils"))]
152154
path_selection: PathSelection::default(),
155+
tls_auth: tls::Authentication::RawPublicKey,
153156
}
154157
}
155158
}
@@ -168,6 +171,7 @@ impl Builder {
168171
.unwrap_or_else(|| SecretKey::generate(rand::rngs::OsRng));
169172
let static_config = StaticConfig {
170173
transport_config: Arc::new(self.transport_config),
174+
tls_auth: self.tls_auth,
171175
keylog: self.keylog,
172176
secret_key: secret_key.clone(),
173177
};
@@ -358,6 +362,24 @@ impl Builder {
358362
self
359363
}
360364

365+
/// Use libp2p based self signed certificates for TLS.
366+
///
367+
/// For details see the libp2p spec at <https://github.com/libp2p/specs/blob/master/tls/tls.md>
368+
///
369+
/// This is the only mechanism available in `[email protected]` and earlier.
370+
pub fn tls_x509(mut self) -> Self {
371+
self.tls_auth = tls::Authentication::X509;
372+
self
373+
}
374+
375+
/// Use TLS Raw Public Keys
376+
///
377+
/// This is the default, but is not compatible with older versions of iroh.
378+
pub fn tls_raw_public_keys(mut self) -> Self {
379+
self.tls_auth = tls::Authentication::RawPublicKey;
380+
self
381+
}
382+
361383
#[cfg(feature = "discovery-pkarr-dht")]
362384
/// Configures the endpoint to also use the mainline DHT with default settings.
363385
///
@@ -501,6 +523,7 @@ impl Builder {
501523
/// Configuration for a [`quinn::Endpoint`] that cannot be changed at runtime.
502524
#[derive(Debug)]
503525
struct StaticConfig {
526+
tls_auth: tls::Authentication,
504527
secret_key: SecretKey,
505528
transport_config: Arc<quinn::TransportConfig>,
506529
keylog: bool,
@@ -509,33 +532,16 @@ struct StaticConfig {
509532
impl StaticConfig {
510533
/// Create a [`quinn::ServerConfig`] with the specified ALPN protocols.
511534
fn create_server_config(&self, alpn_protocols: Vec<Vec<u8>>) -> Result<ServerConfig> {
512-
let server_config = make_server_config(
513-
&self.secret_key,
514-
alpn_protocols,
515-
self.transport_config.clone(),
516-
self.keylog,
517-
)?;
535+
let quic_server_config =
536+
self.tls_auth
537+
.make_server_config(&self.secret_key, alpn_protocols, self.keylog)?;
538+
let mut server_config = ServerConfig::with_crypto(Arc::new(quic_server_config));
539+
server_config.transport_config(self.transport_config.clone());
540+
518541
Ok(server_config)
519542
}
520543
}
521544

522-
/// Creates a [`ServerConfig`] with the given secret key and limits.
523-
// This return type can not longer be used anywhere in our public API. It is however still
524-
// used by iroh::node::Node (or rather iroh::node::Builder) to create a plain Quinn
525-
// endpoint.
526-
pub fn make_server_config(
527-
secret_key: &SecretKey,
528-
alpn_protocols: Vec<Vec<u8>>,
529-
transport_config: Arc<TransportConfig>,
530-
keylog: bool,
531-
) -> Result<ServerConfig> {
532-
let quic_server_config = tls::make_server_config(secret_key, alpn_protocols, keylog)?;
533-
let mut server_config = ServerConfig::with_crypto(Arc::new(quic_server_config));
534-
server_config.transport_config(transport_config);
535-
536-
Ok(server_config)
537-
}
538-
539545
/// Controls an iroh node, establishing connections with other nodes.
540546
///
541547
/// This is the main API interface to create connections to, and accept connections from
@@ -730,9 +736,9 @@ impl Endpoint {
730736
);
731737
let client_config = {
732738
let alpn_protocols = vec![alpn.to_vec()];
733-
let quic_client_config = tls::make_client_config(
739+
let quic_client_config = self.static_config.tls_auth.make_client_config(
734740
&self.static_config.secret_key,
735-
Some(node_id),
741+
node_id,
736742
alpn_protocols,
737743
Some(self.session_store.clone()),
738744
self.static_config.keylog,
@@ -1399,7 +1405,10 @@ impl Future for IncomingFuture {
13991405
Poll::Pending => Poll::Pending,
14001406
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
14011407
Poll::Ready(Ok(inner)) => {
1402-
let conn = Connection { inner };
1408+
let conn = Connection {
1409+
inner,
1410+
tls_auth: this.ep.static_config.tls_auth,
1411+
};
14031412
try_send_rtt_msg(&conn, this.ep, None);
14041413
Poll::Ready(Ok(conn))
14051414
}
@@ -1469,7 +1478,10 @@ impl Connecting {
14691478
pub fn into_0rtt(self) -> Result<(Connection, ZeroRttAccepted), Self> {
14701479
match self.inner.into_0rtt() {
14711480
Ok((inner, zrtt_accepted)) => {
1472-
let conn = Connection { inner };
1481+
let conn = Connection {
1482+
inner,
1483+
tls_auth: self.ep.static_config.tls_auth,
1484+
};
14731485
let zrtt_accepted = ZeroRttAccepted {
14741486
inner: zrtt_accepted,
14751487
_discovery_drop_guard: self._discovery_drop_guard,
@@ -1521,7 +1533,10 @@ impl Future for Connecting {
15211533
Poll::Pending => Poll::Pending,
15221534
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
15231535
Poll::Ready(Ok(inner)) => {
1524-
let conn = Connection { inner };
1536+
let conn = Connection {
1537+
inner,
1538+
tls_auth: this.ep.static_config.tls_auth,
1539+
};
15251540
try_send_rtt_msg(&conn, this.ep, *this.remote_node_id);
15261541
Poll::Ready(Ok(conn))
15271542
}
@@ -1567,12 +1582,10 @@ impl Future for ZeroRttAccepted {
15671582
/// connection without losing application data.
15681583
///
15691584
/// May be cloned to obtain another handle to the same connection.
1570-
// This has repr(transparent) as it opens the door to potentially allow casting it to a
1571-
// quinn::Connection in the future. Right now however that'd be iroh_quinn::Connection.
15721585
#[derive(Debug, Clone)]
1573-
#[repr(transparent)]
15741586
pub struct Connection {
15751587
inner: quinn::Connection,
1588+
tls_auth: tls::Authentication,
15761589
}
15771590

15781591
impl Connection {
@@ -1802,8 +1815,17 @@ impl Connection {
18021815
certs.len()
18031816
);
18041817
}
1805-
let cert = tls::certificate::parse(&certs[0])?;
1806-
Ok(cert.peer_id())
1818+
1819+
match self.tls_auth {
1820+
tls::Authentication::X509 => {
1821+
let cert = tls::certificate::parse(&certs[0])?;
1822+
Ok(cert.peer_id())
1823+
}
1824+
tls::Authentication::RawPublicKey => {
1825+
let peer_id = VerifyingKey::from_public_key_der(&certs[0])?.into();
1826+
Ok(peer_id)
1827+
}
1828+
}
18071829
}
18081830
Err(_) => bail!("invalid peer certificate"),
18091831
},
@@ -2270,19 +2292,36 @@ mod tests {
22702292

22712293
#[tokio::test]
22722294
#[traced_test]
2273-
async fn endpoint_bidi_send_recv() {
2295+
async fn endpoint_bidi_send_recv_x509() {
2296+
endpoint_bidi_send_recv(tls::Authentication::X509).await
2297+
}
2298+
2299+
#[tokio::test]
2300+
#[traced_test]
2301+
async fn endpoint_bidi_send_recv_raw_public_key() {
2302+
endpoint_bidi_send_recv(tls::Authentication::RawPublicKey).await
2303+
}
2304+
2305+
async fn endpoint_bidi_send_recv(auth: tls::Authentication) {
22742306
let ep1 = Endpoint::builder()
22752307
.alpns(vec![TEST_ALPN.to_vec()])
2276-
.relay_mode(RelayMode::Disabled)
2277-
.bind()
2278-
.await
2279-
.unwrap();
2308+
.relay_mode(RelayMode::Disabled);
2309+
2310+
let ep1 = match auth {
2311+
tls::Authentication::X509 => ep1.tls_x509(),
2312+
tls::Authentication::RawPublicKey => ep1.tls_raw_public_keys(),
2313+
};
2314+
let ep1 = ep1.bind().await.unwrap();
22802315
let ep2 = Endpoint::builder()
22812316
.alpns(vec![TEST_ALPN.to_vec()])
2282-
.relay_mode(RelayMode::Disabled)
2283-
.bind()
2284-
.await
2285-
.unwrap();
2317+
.relay_mode(RelayMode::Disabled);
2318+
2319+
let ep2 = match auth {
2320+
tls::Authentication::X509 => ep2.tls_x509(),
2321+
tls::Authentication::RawPublicKey => ep2.tls_raw_public_keys(),
2322+
};
2323+
let ep2 = ep2.bind().await.unwrap();
2324+
22862325
let ep1_nodeaddr = ep1.node_addr().await.unwrap();
22872326
let ep2_nodeaddr = ep2.node_addr().await.unwrap();
22882327
ep1.add_node_addr(ep2_nodeaddr.clone()).unwrap();

0 commit comments

Comments
 (0)