From 4badac469ce2ca94d1f1c4897adb29cf62f9f136 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sat, 19 Aug 2023 18:01:05 +0200 Subject: [PATCH] Use certhash fingerprint --- Cargo.lock | 3 + transports/webtransport/Cargo.toml | 3 + transports/webtransport/src/bin/server.rs | 2 +- transports/webtransport/src/fingerprint.rs | 97 +++++++++++++++++++ transports/webtransport/src/lib.rs | 23 ++++- .../webtransport-tests/echo-server/main.go | 2 +- 6 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 transports/webtransport/src/fingerprint.rs diff --git a/Cargo.lock b/Cargo.lock index 417f3b6b09d..177d6df97e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3413,6 +3413,8 @@ dependencies = [ "h3", "h3-quinn", "h3-webtransport", + "hex", + "hex-literal", "http", "if-watch", "libp2p", @@ -3425,6 +3427,7 @@ dependencies = [ "quinn", "rand 0.8.5", "rustls 0.21.5", + "sha2 0.10.7", "socket2 0.5.3", "thiserror", "tokio", diff --git a/transports/webtransport/Cargo.toml b/transports/webtransport/Cargo.toml index d52a2d1b676..660cfb173c1 100644 --- a/transports/webtransport/Cargo.toml +++ b/transports/webtransport/Cargo.toml @@ -13,6 +13,7 @@ async-std = { version = "1.12.0", optional = true } bytes = "1.4.0" futures = "0.3.28" futures-timer = "3.0.2" +hex = "0.4" if-watch = "3.0.1" # TODO: Remove libp2p = { path = "../../libp2p", features = ["tokio", "macros", "ping"] } @@ -24,6 +25,7 @@ parking_lot = "0.12.0" quinn = { version = "0.10.1", default-features = false, features = ["tls-rustls", "futures-io", "runtime-tokio"] } rand = "0.8.5" rustls = { version = "0.21.2", default-features = false } +sha2 = "0.10.7" thiserror = "1.0.44" # TODO redo features tokio = { version = "1.31.0", default-features = false, features = ["net", "rt", "time", "macros", "rt-multi-thread"] } @@ -49,3 +51,4 @@ rustc-args = ["--cfg", "docsrs"] async-std = { version = "1.12.0", features = ["attributes"] } quickcheck = "1" tokio = { version = "1.31.0", features = ["macros", "rt-multi-thread", "time"] } +hex-literal = "0.4" diff --git a/transports/webtransport/src/bin/server.rs b/transports/webtransport/src/bin/server.rs index 917255e197c..8b040e91e75 100644 --- a/transports/webtransport/src/bin/server.rs +++ b/transports/webtransport/src/bin/server.rs @@ -15,7 +15,7 @@ async fn main() -> Result<(), Box> { let local_peer_id = PeerId::from(local_key.public()); println!("Local peer id: {local_peer_id:?}"); - let transport = libp2p_webtransport::Transport::new().unwrap().boxed(); + let transport = libp2p_webtransport::Transport::new(local_peer_id).unwrap().boxed(); let mut swarm = SwarmBuilder::with_tokio_executor(transport, Behaviour::default(), local_peer_id).build(); diff --git a/transports/webtransport/src/fingerprint.rs b/transports/webtransport/src/fingerprint.rs new file mode 100644 index 00000000000..404d1953656 --- /dev/null +++ b/transports/webtransport/src/fingerprint.rs @@ -0,0 +1,97 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use sha2::Digest as _; +use std::fmt; + +const SHA256: &str = "sha-256"; +const MULTIHASH_SHA256_CODE: u64 = 0x12; + +type Multihash = libp2p::core::multihash::Multihash<64>; + +/// A certificate fingerprint that is assumed to be created using the SHA256 hash algorithm. +#[derive(Eq, PartialEq, Copy, Clone)] +pub struct Fingerprint([u8; 32]); + +impl Fingerprint { + pub(crate) const FF: Fingerprint = Fingerprint([0xFF; 32]); + + #[cfg(test)] + pub fn raw(bytes: [u8; 32]) -> Self { + Self(bytes) + } + + /// Creates a fingerprint from a raw certificate. + pub fn from_certificate(bytes: &[u8]) -> Self { + Fingerprint(sha2::Sha256::digest(bytes).into()) + } + + /// Converts [`Multihash`](multihash::Multihash) to [`Fingerprint`]. + pub fn try_from_multihash(hash: Multihash) -> Option { + if hash.code() != MULTIHASH_SHA256_CODE { + // Only support SHA256 for now. + return None; + } + + let bytes = hash.digest().try_into().ok()?; + + Some(Self(bytes)) + } + + /// Converts this fingerprint to [`Multihash`](multihash::Multihash). + pub fn to_multihash(self) -> Multihash { + Multihash::wrap(MULTIHASH_SHA256_CODE, &self.0).expect("fingerprint's len to be 32 bytes") + } + + /// Formats this fingerprint as uppercase hex, separated by colons (`:`). + /// + /// This is the format described in . + pub fn to_sdp_format(self) -> String { + self.0.map(|byte| format!("{byte:02X}")).join(":") + } + + /// Returns the algorithm used (e.g. "sha-256"). + /// See + pub fn algorithm(&self) -> String { + SHA256.to_owned() + } +} + +impl fmt::Debug for Fingerprint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&hex::encode(self.0)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sdp_format() { + let fp = Fingerprint::raw(hex_literal::hex!( + "7DE3D83F81A680592A471E6B6ABB0747ABD35385A8093FDFE112C1EEBB6CC6AC" + )); + + let sdp_format = fp.to_sdp_format(); + + assert_eq!(sdp_format, "7D:E3:D8:3F:81:A6:80:59:2A:47:1E:6B:6A:BB:07:47:AB:D3:53:85:A8:09:3F:DF:E1:12:C1:EE:BB:6C:C6:AC") + } +} diff --git a/transports/webtransport/src/lib.rs b/transports/webtransport/src/lib.rs index 14105e7020a..2e310c34479 100644 --- a/transports/webtransport/src/lib.rs +++ b/transports/webtransport/src/lib.rs @@ -2,10 +2,12 @@ use futures::{future::BoxFuture, ready, FutureExt, SinkExt, StreamExt}; use h3::{error::ErrorLevel, ext::Protocol}; use libp2p_core::{muxing::StreamMuxerBox, transport::TransportEvent}; use libp2p_identity::PeerId; -use std::{sync::Arc, task::Poll}; +use std::{net::SocketAddr, sync::Arc, task::Poll}; use rustls::{Certificate, PrivateKey}; +mod fingerprint; + const P2P_ALPN: [u8; 6] = *b"libp2p"; pub struct Transport( @@ -13,10 +15,25 @@ pub struct Transport( ); impl Transport { - pub fn new() -> Result> { + pub fn new(peer_id: PeerId) -> Result> { let cert = Certificate(std::fs::read("server.cert")?); + let fingerprint = fingerprint::Fingerprint::from_certificate(cert.as_ref()); let key = PrivateKey(std::fs::read("server.key")?); + let socket_addr: SocketAddr = "[::1]:4433".parse().unwrap(); + + let addr = libp2p::core::Multiaddr::empty() + .with(socket_addr.ip().into()) + .with(libp2p::core::multiaddr::Protocol::Udp(socket_addr.port())) + .with(libp2p::core::multiaddr::Protocol::QuicV1) + .with(libp2p::core::multiaddr::Protocol::WebTransport) + .with(libp2p::core::multiaddr::Protocol::Certhash( + fingerprint.to_multihash(), + )) + .with(libp2p::core::multiaddr::Protocol::P2p(peer_id)); + + println!("Listening on: {addr:?}"); + let mut tls_config = rustls::ServerConfig::builder() .with_safe_default_cipher_suites() .with_safe_default_kx_groups() @@ -36,7 +53,7 @@ impl Transport { tls_config.alpn_protocols = alpn; let server_config = quinn::ServerConfig::with_crypto(Arc::new(tls_config)); - let endpoint = quinn::Endpoint::server(server_config, "[::1]:4433".parse().unwrap())?; + let endpoint = quinn::Endpoint::server(server_config, socket_addr)?; let (sender, receiver) = futures::channel::mpsc::channel(0); diff --git a/wasm-tests/webtransport-tests/echo-server/main.go b/wasm-tests/webtransport-tests/echo-server/main.go index 1ed0d8aa4b9..876cc223750 100644 --- a/wasm-tests/webtransport-tests/echo-server/main.go +++ b/wasm-tests/webtransport-tests/echo-server/main.go @@ -24,7 +24,7 @@ func addrReporter(ma multiaddr.Multiaddr) { h.Add("Cross-Origin-Resource-Policy", "cross-origin") h.Add("Content-Type", "text/plain; charset=utf-8") - fmt.Fprint(w, "/ip6/::1/udp/4433/quic-v1/webtransport/certhash/uEiBXLQJRE5AxMMO35cvj8mstOhnVem8zALKI6uP8URQCng/certhash/uEiCUNmVivYLh1oAejkCyYz2rbw4edcXAs1SNBhdYXAo80w/p2p/12D3KooWQLcKqKE33CbaNGQSQakEW6sAYnVxAMvnZDxTKmFFrmK9") + fmt.Fprint(w, "/ip6/::1/udp/4433/quic-v1/webtransport/certhash/uEiBAOgXcfExykedZKTU4zp3TRdt_qsaOepASE_sE92D1Cw/p2p/12D3KooWN4H4urugWTvubDGpEMH21VZsiZGWXUzLZ3SQUEjfmD1y") })