-
Notifications
You must be signed in to change notification settings - Fork 296
webrtc/: Add libp2p WebRTC browser-to-server spec #412
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 38 commits
9677aa2
968d127
64a3dff
7dfcca1
28e341b
b3bd981
dc6fb4e
9728e22
13f6aaa
7ec96db
5f5fd60
84d4c41
9790d1a
4c6320e
449462b
3e9fb70
81249f3
ee09ed4
d451652
b485340
b654f11
9a90f0d
baccb1d
83e7aa0
766bb62
525bc9e
3e9e6df
a29cb45
827def2
e6c23e1
5f7ea61
f5eb1b7
a8a42b5
c8e725a
7e601e7
9e8ec87
cceed49
c5522c4
c89a72a
bc76a06
a0ae9b0
774cd52
ae1020b
4b8e890
285574d
7009f94
373bafe
93df7e3
027b539
865f4f2
a60234c
48f5fe7
7f40491
31ac65d
85364f4
9ce0d45
2570b24
7a8ebc0
f0acbb5
6df6cf3
c19ba8a
65a894a
6fb2750
73516b1
8208f02
6c7f18e
666c9f4
ae5b0d8
7ce6213
a2d1547
ded63c5
54f8264
67e12da
4a52edc
7380483
c063a81
65590f9
8dc6465
daecb5b
6267324
590c3db
a207aa5
ed5c641
42e7a20
3315b5c
2dddfdc
0deaa30
7ac21aa
8803682
b5466fd
4931f87
d5d164b
6775d10
730dca0
faf641d
e2df94c
b268693
3aebc68
50b4e12
c8df617
4de8f96
4dcf801
16e38fb
f635466
5c9b600
961ada2
75c3b5c
9abf638
1cb4093
a46919c
3058fb9
a382f18
44fd082
22fc557
1ddc317
1e3ca59
b1f629a
070ebea
072c317
b8dc6fd
a62fdd2
118a4dc
60ac97a
6846d34
c369f2b
0c4e836
dd9756b
249ebd2
be5cfeb
602f492
1c7956c
b5ac74b
b6e7eb1
2d0478c
4dc788c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,323 @@ | ||
# WebRTC | ||
|
||
| Lifecycle Stage | Maturity | Status | Latest Revision | | ||
|-----------------|---------------|--------|-----------------| | ||
| 1A | Working Draft | Active | | | ||
|
||
Authors: [@mxinden] | ||
|
||
Interest Group: [@marten-seemann] | ||
|
||
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc --> | ||
**Table of Contents** | ||
|
||
- [WebRTC](#webrtc) | ||
- [Motivation](#motivation) | ||
- [Requirements](#requirements) | ||
- [Addressing](#addressing) | ||
- [Connection Establishment](#connection-establishment) | ||
- [Browser to public Server](#browser-to-public-server) | ||
- [Open Questions](#open-questions) | ||
- [Browser to Browser](#browser-to-browser) | ||
- [Open Questions](#open-questions-1) | ||
- [Connection Security](#connection-security) | ||
- [Open Questions](#open-questions-2) | ||
- [Multiplexing](#multiplexing) | ||
- [General Open Questions](#general-open-questions) | ||
- [Previous, ongoing and related work](#previous-ongoing-and-related-work) | ||
- [FAQ](#faq) | ||
|
||
<!-- markdown-toc end --> | ||
|
||
|
||
## Motivation | ||
|
||
1. **No need for valid TLS certificates.** Enable browsers to connect to public | ||
server nodes without those server nodes providing a TLS certificate within | ||
the browsers trustchain. Note that we can not do this today with our | ||
Websocket transport. | ||
|
||
2. **Hole punching in the browser**: Enable two browsers or a browser and a | ||
non-public server node to connect. | ||
|
||
## Requirements | ||
|
||
- Loading a remote nodes certificate into ones browser trust-store is not an | ||
option, i.e. doesn't scale. | ||
|
||
- No dependency on central STUN and/or TURN servers. | ||
|
||
## Addressing | ||
|
||
WebRTC multiaddresses are composed of an IP and UDP address component, followed | ||
by `/webrtc` and a multihash of the certificate that the node uses. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In multiformats/multiaddr#130 (comment) @tomaka suggests to use
Agreed that we might add other flavors (e.g. always double encrypting via nested Noise), though I would expect that the flavor we are adding here (this specification) will be the most used one, thus deserving the short name. To the best of my knowledge, we don't need a separate protocol name for the browser-to-browser use-case. I.e. we can use the same What do folks think? @tomaka would you expect as well that this flavor will become the most prominent one and thus deserves the shorter name? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A more specific name is a good thing, given that we already have webrtc-star and webrtc-direct transports. These use the multiaddr formats There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not going to push for this, i.e. I don't intend to change the protocol name. In case folks do feel strongly about this, please speak up. |
||
|
||
Examples: | ||
- `/ip4/1.2.3.4/udp/1234/webrtc/certhash/<hash>/p2p/<peer-id>` | ||
mxinden marked this conversation as resolved.
Show resolved
Hide resolved
mxinden marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- `/ip6/fe80::1ff:fe23:4567:890a/udp/1234/webrtc/certhash/<hash>/p2p/<peer-id>` | ||
|
||
The TLS certificate fingerprint in `/certhash` is a | ||
[multibase](https://github.com/multiformats/multibase) encoded | ||
[multihash](https://github.com/multiformats/multihash). | ||
|
||
## Connection Establishment | ||
|
||
### Browser to public Server | ||
|
||
Scenario: Browser _A_ wants to connect to server node _B_ where _B_ is publicly | ||
reachable but _B_ does not have a TLS certificate trusted by _A_. | ||
|
||
As a preparation browser _A_ [generates a | ||
certificate](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-generatecertificate) | ||
and [gets the certificate's | ||
fingerprint](https://www.w3.org/TR/webrtc/#dom-rtccertificate-getfingerprints). | ||
|
||
1. Browser _A_ discovers server node _B_'s multiaddr, containing _B_'s IP, UDP | ||
|
||
port, TLS certificate fingerprint and libp2p peer ID (e.g. | ||
|
||
`/ip6/2001:db8::/udp/1234/webrtc/certhash/<hash>/p2p/<peer-id>`), | ||
through some external mechanism. | ||
|
||
2. _A_ instantiates a `RTCPeerConnection`, passing its local certificate as a | ||
parameter. See | ||
[`RTCPeerConnection()`](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection). | ||
|
||
3. _A_ constructs _B_'s SDP offer locally based on _B_'s multiaddr and sets it | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
via | ||
[`RTCPeerConnection.setRemoteDescription()`](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/setRemoteDescription). | ||
|
||
4. _A_ establishes the connection to _B_. _A_ generates a random string and uses | ||
that string as the username (_ufrag_ or _username fragment_) and password in | ||
the initial STUN message from _A_ to _B_. The random string can be used by | ||
_B_ to identify the connection, i.e. demultiplex incoming UDP datagrams per | ||
incoming connection. _B_ uses the same random string for the username and | ||
password of the STUN message from _B_ to _A_. | ||
|
||
5. _B_ does not know the TLS fingerprint of _A_. _B_ upgrades the incoming | ||
connection from _A_ as an _insecure_ connection, learning _A_'s TLS | ||
|
||
fingerprint through the WebRTC DTLS handshake. At this point the DTLS | ||
handshake provides confidentiality and integrity but not authenticity. | ||
|
||
mxinden marked this conversation as resolved.
Show resolved
Hide resolved
|
||
6. See [Connection Security](#connection-security). | ||
|
||
7. See [Multiplexing](#multiplexing). | ||
|
||
#### Open Questions | ||
|
||
- Is the fact that the server accepts STUN messages from the client prone to | ||
attacks? | ||
|
||
- Can an attacker launch an **amplification attack** with the STUN endpoint | ||
of the server? | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
The QUIC protocol defends against amplification attacks by requiring: | ||
|
||
> an endpoint MUST limit the amount of data it sends to the unvalidated | ||
> address to three times the amount of data received from that address. | ||
https://datatracker.ietf.org/doc/html/rfc9000#section-8 | ||
|
||
For WebRTC in libp2p one could require the client (_A_) to add additional | ||
bytes to its STUN message, e.g. in the STUN username and password, thus | ||
making an amplification attack less attractive. | ||
|
||
- Can a client run a **DOS attack** by sending many STUN messages with | ||
different ufrags using different UDP source ports, forcing the server to | ||
allocate a new peer connection for each? Would rate limiting suffice to | ||
defend against this attack? | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
See also: | ||
- https://datatracker.ietf.org/doc/html/rfc5389#section-16.2.1 | ||
- https://datatracker.ietf.org/doc/html/rfc5389#section-16.1.2 | ||
|
||
- Do the major WebRTC server implementations support using the same UDP port for | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
multiple WebRTC connections, thus requiring multiplexing multiple WebRTC | ||
connections on the same UDP port? In particular this came up for NodeJS. | ||
|
||
This is related to [ICE Lite](https://www.rfc-editor.org/rfc/rfc5245), having | ||
a host only advertise a single address, namely the host address, which is | ||
assumed to be public. | ||
|
||
- Do the major (Go / Rust / ...) WebRTC implementations allow us to accept a | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
WebRTC connection from a remote node without previously receiving an SDP | ||
packet from such host? | ||
|
||
- Can _Browser_ generate a _valid_ SDP packet for the remote node based on the | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
remote's Multiaddr, where that Multiaddr contains the IP, UDP port and TLS | ||
certificate fingerprint (e.g. | ||
`/ip6/2001:db8::/udp/1234/webrtc/certhash/<hash>/p2p/<peer-id>`)? _Valid_ in | ||
the sense that this generated SDP packet can then be used to establish a | ||
WebRTC connection to the remote. | ||
|
||
mxinden marked this conversation as resolved.
Show resolved
Hide resolved
mxinden marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Yes. | ||
|
||
### Browser to Browser | ||
|
||
Scenario: Browser _A_ wants to connect to Browser node _B_ with the help of | ||
server node _R_. | ||
|
||
- Replace STUN with libp2p's identify and AutoNAT | ||
- https://github.com/libp2p/specs/tree/master/identify | ||
- https://github.com/libp2p/specs/blob/master/autonat/README.md | ||
- Replace TURN with libp2p's Circuit Relay v2 | ||
- https://github.com/libp2p/specs/blob/master/relay/circuit-v2.md | ||
- Use DCUtR over Circuit Relay v2 to transmit SDP information | ||
|
||
1. Transform ICE candidates in SDP to multiaddresses. | ||
2. Transmit the set of multiaddresses to the remote via DCUtR. | ||
|
||
3. Transform the set of multiaddresses back to the remotes SDP. | ||
|
||
4. https://github.com/libp2p/specs/blob/master/relay/DCUtR.md | ||
|
||
#### Open Questions | ||
|
||
- Can _Browser_ know upfront its UDP port which it is listening for incoming | ||
connections on? Does the browser reuse the UDP port across many WebRTC | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
connections? If that is the case one could connect to any public node, with | ||
the remote telling the local node what port it is perceived on. | ||
|
||
- Can _Browser_ control the lifecycle of its local TLS certificate, i.e. can | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
_Browser_ use the same TLS certificate for multiple WebRTC connections? | ||
|
||
Yes. For the lifetime of the page, one can generate a certificate once and | ||
reuse it across connections. See also | ||
https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection#using_certificates | ||
|
||
- Can two _Browsers_ exchange their SDP packets via a third server node using | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
Circuit Relay v2 and DCUtR? Instead of exchanging the original SDP packets, | ||
could they exchange their multiaddr and construct the remote's SDP packet | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
based on it? | ||
|
||
## Connection Security | ||
|
||
While WebRTC offers confidentiality and integrity via TLS, one still needs to | ||
authenticate the remote peer by its libp2p identity. | ||
|
||
After [Connection Establishment](#connection-establishment): | ||
|
||
1. _A_ opens a WebRTC datachannel and starts a Noise handshake using _A_'s and | ||
_B_'s libp2p identity. See | ||
[noise-libp2p](https://github.com/libp2p/specs/tree/master/noise). | ||
|
||
2. _A_ and _B_ write their TLS certificate fingerprint on the negotiated Noise | ||
channel as multibase encoded multihashes (see [#addressing]). | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
3. _A_ and _B_ read the other sides TLS certificate fingerprint on the | ||
negotiated Noise channel and compare it to the ones verified during the DTLS | ||
handshake. | ||
|
||
Note: WebRTC supports different hash functions to hash the TLS certificate | ||
(see https://datatracker.ietf.org/doc/html/rfc8122#section-5). The hash | ||
function used in WebRTC, the hash function used for the hash exchanged in the | ||
additional Noise handshake and the hash function used in the multiaddr | ||
`/certhash` component MUST be the same. On mismatch the final Noise handshake | ||
MUST fail. | ||
|
||
### Open Questions | ||
|
||
- Can a _Browser_ access the fingerprint of its TLS certificate? | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
Chrome allows you to access the fingerprint of any locally-created certificate | ||
directly via `RTCCertificate#getFingerprints`. Firefox does not allow you to | ||
do so. Browser compatibility can be found | ||
[here](https://developer.mozilla.org/en-US/docs/Web/API/RTCCertificate). In | ||
practice, this is not an issue since the fingerprint is embedded in the local | ||
SDP string. | ||
|
||
- Is the above proposed additional handshake secure? See also alternative | ||
proposed Handshake for | ||
[WebTransport](https://github.com/libp2p/specs/pull/404). | ||
|
||
- Would it be more efficient for _B_ to initiate the Noise handshake? In other | ||
words, who is able to write on an established WebRTC connection first? _A_ or | ||
_B_? | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
- Instead of exchanging the TLS fingerprints once the Noise handshake finished, | ||
could one instead attach the fingerprints to the Noise handshake messages as | ||
additional payloads? That would reduce the overall connection establishment | ||
latency by one round trip. Would this schema be secure using the Noise XX | ||
handshake pattern? | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
- On the server side, can one derive the TLS certificate in a deterministic way | ||
based on a node's libp2p private key? Benefit would be that a node only needs | ||
to persist the libp2p private key and not the TLS key material while still | ||
maintaining a fixed TLS certificate fingerprint. | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
## Multiplexing | ||
|
||
After [Connection Security](#connection-security): | ||
|
||
1. On success of the authentication handshake _X_, the used datachannel is | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
closed and the plain WebRTC connection is used with its multiplexing | ||
capabilities via datachannels. | ||
|
||
### Open Questions | ||
|
||
- Can we use WebRTC’s data channels in _Browser_ to multiplex a single | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
connection, or do we need to run an additional multiplexer (e.g. yamux) on top | ||
of a WebRTC connection and WebRTC datachannel? In other words, does WebRTC | ||
provide all functionality of a libp2p muxer like Yamux (e.g. flow control)? | ||
|
||
Yes, with WebRTC's datachannels running on top of SCTP, there is no need for | ||
additional multiplexing. | ||
|
||
## General Open Questions | ||
|
||
- Should libp2p's WebRTC stack limit itself to using UDP only, or support WebRTC | ||
on top of both UDP and TCP? | ||
|
||
- Is the fact that Firefox does not allow a WebRTC to `localhost` an issue? See | ||
https://bugzilla.mozilla.org/show_bug.cgi?id=831926. | ||
|
||
## Previous, ongoing and related work | ||
|
||
- Proof of concept for the server side in rust-libp2p: | ||
https://github.com/libp2p/rust-libp2p/pull/2622 | ||
mxinden marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
- Proof of concept for the server side (native) and the client side (Rust in | ||
WASM): https://github.com/wngr/libp2p-webrtc | ||
|
||
- WebRTC using STUN and TURN: https://github.com/libp2p/js-libp2p-webrtc-star | ||
|
||
# FAQ | ||
|
||
- _Why exchange the TLS certificate fingerprint in the multiaddr? Why not | ||
base it on the libp2p public key?_ | ||
|
||
Browsers do not allow loading a custom certificate. One can only generate a | ||
certificate via | ||
[rtcpeerconnection-generatecertificate](https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-generatecertificate). | ||
|
||
- _Why not embed the peer ID in the TLS certificate, thus rendering the | ||
additional "peer certificate" exchange obsolete?_ | ||
|
||
Browsers do not allow editing the properties of the TLS certificate. | ||
|
||
- _How about distributing the multiaddr in a signed peer record, thus rendering | ||
the additional "peer certificate" exchange obsolete?_ | ||
|
||
Signed peer records are not yet rolled out across the many libp2p protocols. | ||
Making the libp2p WebRTC protocol dependent on the former is not deemed worth | ||
it at this point in time. Later versions of the libp2p WebRTC protocol might | ||
adopt this optimization. | ||
|
||
Note, one can role out a new version of the libp2p WebRTC protocol through a | ||
new multiaddr protocol, e.g. `/webrtc-2`. | ||
|
||
- _Why exchange fingerprints in an additional authentication handshake on top of | ||
an established WebRTC connection? Why not only exchange signatures of ones TLS | ||
fingerprints signed with ones libp2p private key on the plain WebRTC | ||
connection?_ | ||
|
||
Once _A_ and _B_ established a WebRTC connection, _A_ sends | ||
`signature_libp2p_a(fingerprint_a)` to _B_ and vice versa. While this has the | ||
benefit of only requring two messages, thus one round trip, it is prone to a | ||
key compromise and replay attack. Say that _E_ is able to attain | ||
`signature_libp2p_a(fingerprint_a)` and somehow compromise _A_'s TLS private | ||
key, _E_ can now impersonate _A_ without knowing _A_'s libp2p private key. | ||
|
||
If one requires the signatures to contain both fingerprints, e.g. | ||
`signature_libp2p_a(fingerprint_a, fingerprint_b)`, the above attack still | ||
works, just that _E_ can only impersonate _A_ when talking to _B_. | ||
|
||
Adding a cryptographic identifier of the unique connection (i.e. session) to | ||
the signature (`signature_libp2p_a(fingerprint_a, fingerprint_b, | ||
connection_identifier)`) would protect against this attack. To the best of our | ||
knowledge the browser does not give us access to such identifier. | ||
mxinden marked this conversation as resolved.
Show resolved
Hide resolved
|
Uh oh!
There was an error while loading. Please reload this page.