Open Jorropo opened 5 months ago
Multiplexing on the first set of bytes seems reasonable. But the Noise identification seems brittle.
The client dialing /ip4/1.2.3.4/tcp/12345/noise
knows the endpoint supports noise without a handshake. What about introducing a magic byte sequence to help the server identify the noise connection? The client would send this, and then start the normal noise handshake. The server sees the magic byte sequence, then, knowning it's a noise handshake, start the noise handshake.
:+1: for magic noise bytes, this would allow to update the noise protobuf schema without worrying about the protobuf serializer putting new fields first which would break existing magiselect implementations.
Requirements
With Encrypted-Client-Hello RFC in the work it would help to have plain TLS connections in Libp2p as we can then wrap them in ECH (*we will need to figure out KEM preshared keys). The quic and webtransport transports are already ECH ready, however for TCP connections we still have the multistream protocol id which blow away any hope of stealthish libp2p.
Spec
Security in the maddr
Add meaning to some maddr components after
tcp
component indicating to the client they are allowed to skip the multistream part of the handshake.tls
allows to run TLS without handshake.noise
allows to run Noise without handshake.Server side recommendations
This part of the spec is non normative because we can't check for it. We should evolve it if new things running on top of TCP are added. A valid implementation is to use a different TCP Port for each protocol you want to support, for example:
and not doing selection, and this usecase should be guaranteed so you can use a TLS / HTTPs reverse proxy switching on the ALPN as your libp2p gateway (allowing to create an anonymity set when using ECH).
However TCP listeners are costy from a user configuration end, it is much easier if the same port can be upgraded to support plain TLS just by doing a bit of magic.
First read three bytes from the inbound stream. If you failed to read three bytes within your timeout windows, this can be discarded as very likely not a libp2p connection.
If these three bytes are:
\x13/m
treat it as multistream.GET
,HEA
,POS
,PUT
,DEL
,CON
,OPT
,TRA
orPAT
treat it as an HTTP1.x160301
,160302
,160303
or160304
treat it as a TLS connection., however handling them allows your TLS server to do a proper rejection and simplify the code complexity. Turns out go's160301
and160302
might be useless as they are only used for TLS1.0 and TLS1.1 which shouldn't be usedcrypto/tls
always sends TLS1.0 withclient_version
to 1.2 or 1.3.160303
is used both by TLS1.2 and TLS1.3 and160304
is TLS1.3, so they must be used as libp2p's TLS spec requires TLS1.3.type
be the the three lsb of the third byte as an unsigned integer.type
must be<= 5
(as there is no 6 or 7 wire values). Letfield
be the the five msb of the third byte an unsigned integer shifted right three times. Depending on the value offield
:1
(identity_key
),2
(identity_sig
) or3
(data
removed in 4b052c0df0b866c246991e59385fb2bf902820b7)type
must be2
(LEN), treat it as noise.4
(extensions
)type
must be2
(LEN) or3
(SGROUP deprecated), treat it as noise.Notes:
0
protobuf field ID which is not valid protobuf.Expected usage.
A server implementing magiselect on one TCP port might announce theses maddrs:
/ip4/1.2.3.4/tcp/12345
backward compat multistream-select/ip4/1.2.3.4/tcp/12345/tls
straight tls/ip4/1.2.3.4/tcp/12345/noise
straight noise/ip4/1.2.3.4/tcp/12345/ws
websocket transport on the same port as tcp transport/ip4/1.2.3.4/tcp/12345/wss
websocket secure transport on the same port as tcp transport, differentiated from libp2p's TLS due to ALPN.multistream backward compatibility
Currently go-libp2p's server sends
/multistream/1.0.0
before having received anything. This behavior would change as we need to run detection on the first bytes sent by the client.https://github.com/libp2p/specs/tree/master/connections says:
But I don't see why this is not a compliant implementation:
/multistream/1.0.0
./multistream/1.0.0
.I read this part of the multiformats spec as understanding this as the client should go first:
I think this can be solved by reviewing known implementations and making sure the client always send
/multistream/1.0.0
without waiting on the server and updating the multistream-select spec to reflect that (while still allowing servers to optionally send it early).