From d34e75f4738322ee5c805d4b09b8b4c1e8fdaebd Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 14 Jul 2026 03:36:31 +0000 Subject: [PATCH 1/2] feat: add ECH multiaddr protocol Co-authored-by: Marco Munizaga --- ech_test.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ protocols.go | 9 +++++++++ transcoders.go | 27 +++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 ech_test.go diff --git a/ech_test.go b/ech_test.go new file mode 100644 index 0000000..fe46422 --- /dev/null +++ b/ech_test.go @@ -0,0 +1,50 @@ +package multiaddr + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestECHProtocol(t *testing.T) { + echConfigList := []byte{0x00, 0x04, 0xff, 0xff, 0x00, 0x00} + const encodedECHConfigList = "uAAT__wAA" + + protocol := ProtocolWithName("ech") + require.Equal(t, P_ECH, protocol.Code) + require.Equal(t, []byte{0xf9, 0x4c}, protocol.VCode) + require.Equal(t, LengthPrefixedVarSize, protocol.Size) + require.NotNil(t, protocol.Transcoder) + require.Equal(t, protocol, ProtocolWithCode(P_ECH)) + + component, err := NewComponent("ech", encodedECHConfigList) + require.NoError(t, err) + require.Equal(t, "/ech/"+encodedECHConfigList, component.String()) + require.Equal(t, echConfigList, component.RawValue()) + require.Equal(t, append([]byte{0xf9, 0x4c, 0x06}, echConfigList...), component.Bytes()) + + addr, err := NewMultiaddr("/ip4/192.0.2.1/tcp/443/tls/ech/" + encodedECHConfigList) + require.NoError(t, err) + require.Equal(t, "/ip4/192.0.2.1/tcp/443/tls/ech/"+encodedECHConfigList, addr.String()) + + value, err := addr.ValueForProtocol(P_ECH) + require.NoError(t, err) + require.Equal(t, encodedECHConfigList, value) + + fromBytes, err := NewMultiaddrBytes(addr.Bytes()) + require.NoError(t, err) + require.True(t, addr.Equal(fromBytes)) +} + +func TestECHAcceptsAnyMultibaseEncoding(t *testing.T) { + addr, err := NewMultiaddr("/quic-v1/ech/baacp77yaaa") + require.NoError(t, err) + require.Equal(t, "/quic-v1/ech/uAAT__wAA", addr.String()) +} + +func TestECHRejectsInvalidValues(t *testing.T) { + for _, value := range []string{"not-multibase", "u"} { + _, err := NewComponent("ech", value) + require.Error(t, err) + } +} diff --git a/protocols.go b/protocols.go index 0e74b5a..a1d76ea 100644 --- a/protocols.go +++ b/protocols.go @@ -34,6 +34,7 @@ const ( P_GARLIC32 = 447 P_P2P_WEBRTC_DIRECT = 276 // Deprecated. use webrtc-direct instead P_TLS = 448 + P_ECH = 9849 P_SNI = 449 P_NOISE = 454 P_WS = 477 @@ -245,6 +246,13 @@ var ( Code: P_TLS, VCode: CodeToVarint(P_TLS), } + protoECH = Protocol{ + Name: "ech", + Code: P_ECH, + VCode: CodeToVarint(P_ECH), + Size: LengthPrefixedVarSize, + Transcoder: TranscoderECH, + } protoSNI = Protocol{ Name: "sni", Size: LengthPrefixedVarSize, @@ -324,6 +332,7 @@ func init() { protoUNIX, protoP2P_WEBRTC_DIRECT, protoTLS, + protoECH, protoSNI, protoNOISE, protoWS, diff --git a/transcoders.go b/transcoders.go index 03b39ae..30e2bec 100644 --- a/transcoders.go +++ b/transcoders.go @@ -463,6 +463,33 @@ func validateCertHash(b []byte) error { return err } +var TranscoderECH = NewTranscoderFromFunctions(echStB, echBtS, validateECH) + +func echStB(s string) ([]byte, error) { + _, data, err := multibase.Decode(s) + if err != nil { + return nil, err + } + if err := validateECH(data); err != nil { + return nil, err + } + return data, nil +} + +func echBtS(b []byte) (string, error) { + if err := validateECH(b); err != nil { + return "", err + } + return multibase.Encode(multibase.Base64url, b) +} + +func validateECH(b []byte) error { + if len(b) == 0 { + return errors.New("empty ECHConfigList") + } + return nil +} + var TranscoderHTTPPath = NewTranscoderFromFunctions(httpPathStB, httpPathBtS, validateHTTPPath) func httpPathStB(s string) ([]byte, error) { From 7d6b2ed55e0702fdf8d530863ebb2b3dc3e6df88 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 14 Jul 2026 03:37:21 +0000 Subject: [PATCH 2/2] test: compare ECH protocol lookup fields Co-authored-by: Marco Munizaga --- ech_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ech_test.go b/ech_test.go index fe46422..8174e36 100644 --- a/ech_test.go +++ b/ech_test.go @@ -15,7 +15,7 @@ func TestECHProtocol(t *testing.T) { require.Equal(t, []byte{0xf9, 0x4c}, protocol.VCode) require.Equal(t, LengthPrefixedVarSize, protocol.Size) require.NotNil(t, protocol.Transcoder) - require.Equal(t, protocol, ProtocolWithCode(P_ECH)) + require.Equal(t, "ech", ProtocolWithCode(P_ECH).Name) component, err := NewComponent("ech", encodedECHConfigList) require.NoError(t, err)