feat: Waku v2 bridge

Issue #12610
This commit is contained in:
Michal Iskierko
2023-11-12 13:29:38 +01:00
parent 56e7bd01ca
commit 6d31343205
6716 changed files with 1982502 additions and 5891 deletions

View File

@@ -0,0 +1,77 @@
package extension
import (
"golang.org/x/crypto/cryptobyte"
)
// ALPN is a TLS extension for application-layer protocol negotiation within
// the TLS handshake.
//
// https://tools.ietf.org/html/rfc7301
type ALPN struct {
ProtocolNameList []string
}
// TypeValue returns the extension TypeValue
func (a ALPN) TypeValue() TypeValue {
return ALPNTypeValue
}
// Marshal encodes the extension
func (a *ALPN) Marshal() ([]byte, error) {
var b cryptobyte.Builder
b.AddUint16(uint16(a.TypeValue()))
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
for _, proto := range a.ProtocolNameList {
p := proto // Satisfy range scope lint
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes([]byte(p))
})
}
})
})
return b.Bytes()
}
// Unmarshal populates the extension from encoded data
func (a *ALPN) Unmarshal(data []byte) error {
val := cryptobyte.String(data)
var extension uint16
val.ReadUint16(&extension)
if TypeValue(extension) != a.TypeValue() {
return errInvalidExtensionType
}
var extData cryptobyte.String
val.ReadUint16LengthPrefixed(&extData)
var protoList cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
return ErrALPNInvalidFormat
}
for !protoList.Empty() {
var proto cryptobyte.String
if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() {
return ErrALPNInvalidFormat
}
a.ProtocolNameList = append(a.ProtocolNameList, string(proto))
}
return nil
}
// ALPNProtocolSelection negotiates a shared protocol according to #3.2 of rfc7301
func ALPNProtocolSelection(supportedProtocols, peerSupportedProtocols []string) (string, error) {
if len(supportedProtocols) == 0 || len(peerSupportedProtocols) == 0 {
return "", nil
}
for _, s := range supportedProtocols {
for _, c := range peerSupportedProtocols {
if s == c {
return s, nil
}
}
}
return "", errALPNNoAppProto
}

View File

@@ -0,0 +1,17 @@
package extension
import (
"errors"
"github.com/pion/dtls/v2/pkg/protocol"
)
var (
// ErrALPNInvalidFormat is raised when the ALPN format is invalid
ErrALPNInvalidFormat = &protocol.FatalError{Err: errors.New("invalid alpn format")} //nolint:goerr113
errALPNNoAppProto = &protocol.FatalError{Err: errors.New("no application protocol")} //nolint:goerr113
errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
errInvalidExtensionType = &protocol.FatalError{Err: errors.New("invalid extension type")} //nolint:goerr113
errInvalidSNIFormat = &protocol.FatalError{Err: errors.New("invalid server name format")} //nolint:goerr113
errLengthMismatch = &protocol.InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113
)

View File

@@ -0,0 +1,99 @@
// Package extension implements the extension values in the ClientHello/ServerHello
package extension
import "encoding/binary"
// TypeValue is the 2 byte value for a TLS Extension as registered in the IANA
//
// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml
type TypeValue uint16
// TypeValue constants
const (
ServerNameTypeValue TypeValue = 0
SupportedEllipticCurvesTypeValue TypeValue = 10
SupportedPointFormatsTypeValue TypeValue = 11
SupportedSignatureAlgorithmsTypeValue TypeValue = 13
UseSRTPTypeValue TypeValue = 14
ALPNTypeValue TypeValue = 16
UseExtendedMasterSecretTypeValue TypeValue = 23
RenegotiationInfoTypeValue TypeValue = 65281
)
// Extension represents a single TLS extension
type Extension interface {
Marshal() ([]byte, error)
Unmarshal(data []byte) error
TypeValue() TypeValue
}
// Unmarshal many extensions at once
func Unmarshal(buf []byte) ([]Extension, error) {
switch {
case len(buf) == 0:
return []Extension{}, nil
case len(buf) < 2:
return nil, errBufferTooSmall
}
declaredLen := binary.BigEndian.Uint16(buf)
if len(buf)-2 != int(declaredLen) {
return nil, errLengthMismatch
}
extensions := []Extension{}
unmarshalAndAppend := func(data []byte, e Extension) error {
err := e.Unmarshal(data)
if err != nil {
return err
}
extensions = append(extensions, e)
return nil
}
for offset := 2; offset < len(buf); {
if len(buf) < (offset + 2) {
return nil, errBufferTooSmall
}
var err error
switch TypeValue(binary.BigEndian.Uint16(buf[offset:])) {
case ServerNameTypeValue:
err = unmarshalAndAppend(buf[offset:], &ServerName{})
case SupportedEllipticCurvesTypeValue:
err = unmarshalAndAppend(buf[offset:], &SupportedEllipticCurves{})
case UseSRTPTypeValue:
err = unmarshalAndAppend(buf[offset:], &UseSRTP{})
case ALPNTypeValue:
err = unmarshalAndAppend(buf[offset:], &ALPN{})
case UseExtendedMasterSecretTypeValue:
err = unmarshalAndAppend(buf[offset:], &UseExtendedMasterSecret{})
case RenegotiationInfoTypeValue:
err = unmarshalAndAppend(buf[offset:], &RenegotiationInfo{})
default:
}
if err != nil {
return nil, err
}
if len(buf) < (offset + 4) {
return nil, errBufferTooSmall
}
extensionLength := binary.BigEndian.Uint16(buf[offset+2:])
offset += (4 + int(extensionLength))
}
return extensions, nil
}
// Marshal many extensions at once
func Marshal(e []Extension) ([]byte, error) {
extensions := []byte{}
for _, e := range e {
raw, err := e.Marshal()
if err != nil {
return nil, err
}
extensions = append(extensions, raw...)
}
out := []byte{0x00, 0x00}
binary.BigEndian.PutUint16(out, uint16(len(extensions)))
return append(out, extensions...), nil
}

View File

@@ -0,0 +1,43 @@
package extension
import "encoding/binary"
const (
renegotiationInfoHeaderSize = 5
)
// RenegotiationInfo allows a Client/Server to
// communicate their renegotation support
//
// https://tools.ietf.org/html/rfc5746
type RenegotiationInfo struct {
RenegotiatedConnection uint8
}
// TypeValue returns the extension TypeValue
func (r RenegotiationInfo) TypeValue() TypeValue {
return RenegotiationInfoTypeValue
}
// Marshal encodes the extension
func (r *RenegotiationInfo) Marshal() ([]byte, error) {
out := make([]byte, renegotiationInfoHeaderSize)
binary.BigEndian.PutUint16(out, uint16(r.TypeValue()))
binary.BigEndian.PutUint16(out[2:], uint16(1)) // length
out[4] = r.RenegotiatedConnection
return out, nil
}
// Unmarshal populates the extension from encoded data
func (r *RenegotiationInfo) Unmarshal(data []byte) error {
if len(data) < renegotiationInfoHeaderSize {
return errBufferTooSmall
} else if TypeValue(binary.BigEndian.Uint16(data)) != r.TypeValue() {
return errInvalidExtensionType
}
r.RenegotiatedConnection = data[4]
return nil
}

View File

@@ -0,0 +1,78 @@
package extension
import (
"strings"
"golang.org/x/crypto/cryptobyte"
)
const serverNameTypeDNSHostName = 0
// ServerName allows the client to inform the server the specific
// name it wishes to contact. Useful if multiple DNS names resolve
// to one IP
//
// https://tools.ietf.org/html/rfc6066#section-3
type ServerName struct {
ServerName string
}
// TypeValue returns the extension TypeValue
func (s ServerName) TypeValue() TypeValue {
return ServerNameTypeValue
}
// Marshal encodes the extension
func (s *ServerName) Marshal() ([]byte, error) {
var b cryptobyte.Builder
b.AddUint16(uint16(s.TypeValue()))
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddUint8(serverNameTypeDNSHostName)
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes([]byte(s.ServerName))
})
})
})
return b.Bytes()
}
// Unmarshal populates the extension from encoded data
func (s *ServerName) Unmarshal(data []byte) error {
val := cryptobyte.String(data)
var extension uint16
val.ReadUint16(&extension)
if TypeValue(extension) != s.TypeValue() {
return errInvalidExtensionType
}
var extData cryptobyte.String
val.ReadUint16LengthPrefixed(&extData)
var nameList cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() {
return errInvalidSNIFormat
}
for !nameList.Empty() {
var nameType uint8
var serverName cryptobyte.String
if !nameList.ReadUint8(&nameType) ||
!nameList.ReadUint16LengthPrefixed(&serverName) ||
serverName.Empty() {
return errInvalidSNIFormat
}
if nameType != serverNameTypeDNSHostName {
continue
}
if len(s.ServerName) != 0 {
// Multiple names of the same name_type are prohibited.
return errInvalidSNIFormat
}
s.ServerName = string(serverName)
// An SNI value may not include a trailing dot.
if strings.HasSuffix(s.ServerName, ".") {
return errInvalidSNIFormat
}
}
return nil
}

View File

@@ -0,0 +1,21 @@
package extension
// SRTPProtectionProfile defines the parameters and options that are in effect for the SRTP processing
// https://tools.ietf.org/html/rfc5764#section-4.1.2
type SRTPProtectionProfile uint16
const (
SRTP_AES128_CM_HMAC_SHA1_80 SRTPProtectionProfile = 0x0001 // nolint
SRTP_AES128_CM_HMAC_SHA1_32 SRTPProtectionProfile = 0x0002 // nolint
SRTP_AEAD_AES_128_GCM SRTPProtectionProfile = 0x0007 // nolint
SRTP_AEAD_AES_256_GCM SRTPProtectionProfile = 0x0008 // nolint
)
func srtpProtectionProfiles() map[SRTPProtectionProfile]bool {
return map[SRTPProtectionProfile]bool{
SRTP_AES128_CM_HMAC_SHA1_80: true,
SRTP_AES128_CM_HMAC_SHA1_32: true,
SRTP_AEAD_AES_128_GCM: true,
SRTP_AEAD_AES_256_GCM: true,
}
}

View File

@@ -0,0 +1,62 @@
package extension
import (
"encoding/binary"
"github.com/pion/dtls/v2/pkg/crypto/elliptic"
)
const (
supportedGroupsHeaderSize = 6
)
// SupportedEllipticCurves allows a Client/Server to communicate
// what curves they both support
//
// https://tools.ietf.org/html/rfc8422#section-5.1.1
type SupportedEllipticCurves struct {
EllipticCurves []elliptic.Curve
}
// TypeValue returns the extension TypeValue
func (s SupportedEllipticCurves) TypeValue() TypeValue {
return SupportedEllipticCurvesTypeValue
}
// Marshal encodes the extension
func (s *SupportedEllipticCurves) Marshal() ([]byte, error) {
out := make([]byte, supportedGroupsHeaderSize)
binary.BigEndian.PutUint16(out, uint16(s.TypeValue()))
binary.BigEndian.PutUint16(out[2:], uint16(2+(len(s.EllipticCurves)*2)))
binary.BigEndian.PutUint16(out[4:], uint16(len(s.EllipticCurves)*2))
for _, v := range s.EllipticCurves {
out = append(out, []byte{0x00, 0x00}...)
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(v))
}
return out, nil
}
// Unmarshal populates the extension from encoded data
func (s *SupportedEllipticCurves) Unmarshal(data []byte) error {
if len(data) <= supportedGroupsHeaderSize {
return errBufferTooSmall
} else if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() {
return errInvalidExtensionType
}
groupCount := int(binary.BigEndian.Uint16(data[4:]) / 2)
if supportedGroupsHeaderSize+(groupCount*2) > len(data) {
return errLengthMismatch
}
for i := 0; i < groupCount; i++ {
supportedGroupID := elliptic.Curve(binary.BigEndian.Uint16(data[(supportedGroupsHeaderSize + (i * 2)):]))
if _, ok := elliptic.Curves()[supportedGroupID]; ok {
s.EllipticCurves = append(s.EllipticCurves, supportedGroupID)
}
}
return nil
}

View File

@@ -0,0 +1,62 @@
package extension
import (
"encoding/binary"
"github.com/pion/dtls/v2/pkg/crypto/elliptic"
)
const (
supportedPointFormatsSize = 5
)
// SupportedPointFormats allows a Client/Server to negotiate
// the EllipticCurvePointFormats
//
// https://tools.ietf.org/html/rfc4492#section-5.1.2
type SupportedPointFormats struct {
PointFormats []elliptic.CurvePointFormat
}
// TypeValue returns the extension TypeValue
func (s SupportedPointFormats) TypeValue() TypeValue {
return SupportedPointFormatsTypeValue
}
// Marshal encodes the extension
func (s *SupportedPointFormats) Marshal() ([]byte, error) {
out := make([]byte, supportedPointFormatsSize)
binary.BigEndian.PutUint16(out, uint16(s.TypeValue()))
binary.BigEndian.PutUint16(out[2:], uint16(1+(len(s.PointFormats))))
out[4] = byte(len(s.PointFormats))
for _, v := range s.PointFormats {
out = append(out, byte(v))
}
return out, nil
}
// Unmarshal populates the extension from encoded data
func (s *SupportedPointFormats) Unmarshal(data []byte) error {
if len(data) <= supportedPointFormatsSize {
return errBufferTooSmall
} else if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() {
return errInvalidExtensionType
}
pointFormatCount := int(binary.BigEndian.Uint16(data[4:]))
if supportedGroupsHeaderSize+(pointFormatCount) > len(data) {
return errLengthMismatch
}
for i := 0; i < pointFormatCount; i++ {
p := elliptic.CurvePointFormat(data[supportedPointFormatsSize+i])
switch p {
case elliptic.CurvePointFormatUncompressed:
s.PointFormats = append(s.PointFormats, p)
default:
}
}
return nil
}

View File

@@ -0,0 +1,70 @@
package extension
import (
"encoding/binary"
"github.com/pion/dtls/v2/pkg/crypto/hash"
"github.com/pion/dtls/v2/pkg/crypto/signature"
"github.com/pion/dtls/v2/pkg/crypto/signaturehash"
)
const (
supportedSignatureAlgorithmsHeaderSize = 6
)
// SupportedSignatureAlgorithms allows a Client/Server to
// negotiate what SignatureHash Algorithms they both support
//
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
type SupportedSignatureAlgorithms struct {
SignatureHashAlgorithms []signaturehash.Algorithm
}
// TypeValue returns the extension TypeValue
func (s SupportedSignatureAlgorithms) TypeValue() TypeValue {
return SupportedSignatureAlgorithmsTypeValue
}
// Marshal encodes the extension
func (s *SupportedSignatureAlgorithms) Marshal() ([]byte, error) {
out := make([]byte, supportedSignatureAlgorithmsHeaderSize)
binary.BigEndian.PutUint16(out, uint16(s.TypeValue()))
binary.BigEndian.PutUint16(out[2:], uint16(2+(len(s.SignatureHashAlgorithms)*2)))
binary.BigEndian.PutUint16(out[4:], uint16(len(s.SignatureHashAlgorithms)*2))
for _, v := range s.SignatureHashAlgorithms {
out = append(out, []byte{0x00, 0x00}...)
out[len(out)-2] = byte(v.Hash)
out[len(out)-1] = byte(v.Signature)
}
return out, nil
}
// Unmarshal populates the extension from encoded data
func (s *SupportedSignatureAlgorithms) Unmarshal(data []byte) error {
if len(data) <= supportedSignatureAlgorithmsHeaderSize {
return errBufferTooSmall
} else if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() {
return errInvalidExtensionType
}
algorithmCount := int(binary.BigEndian.Uint16(data[4:]) / 2)
if supportedSignatureAlgorithmsHeaderSize+(algorithmCount*2) > len(data) {
return errLengthMismatch
}
for i := 0; i < algorithmCount; i++ {
supportedHashAlgorithm := hash.Algorithm(data[supportedSignatureAlgorithmsHeaderSize+(i*2)])
supportedSignatureAlgorithm := signature.Algorithm(data[supportedSignatureAlgorithmsHeaderSize+(i*2)+1])
if _, ok := hash.Algorithms()[supportedHashAlgorithm]; ok {
if _, ok := signature.Algorithms()[supportedSignatureAlgorithm]; ok {
s.SignatureHashAlgorithms = append(s.SignatureHashAlgorithms, signaturehash.Algorithm{
Hash: supportedHashAlgorithm,
Signature: supportedSignatureAlgorithm,
})
}
}
}
return nil
}

View File

@@ -0,0 +1,45 @@
package extension
import "encoding/binary"
const (
useExtendedMasterSecretHeaderSize = 4
)
// UseExtendedMasterSecret defines a TLS extension that contextually binds the
// master secret to a log of the full handshake that computes it, thus
// preventing MITM attacks.
type UseExtendedMasterSecret struct {
Supported bool
}
// TypeValue returns the extension TypeValue
func (u UseExtendedMasterSecret) TypeValue() TypeValue {
return UseExtendedMasterSecretTypeValue
}
// Marshal encodes the extension
func (u *UseExtendedMasterSecret) Marshal() ([]byte, error) {
if !u.Supported {
return []byte{}, nil
}
out := make([]byte, useExtendedMasterSecretHeaderSize)
binary.BigEndian.PutUint16(out, uint16(u.TypeValue()))
binary.BigEndian.PutUint16(out[2:], uint16(0)) // length
return out, nil
}
// Unmarshal populates the extension from encoded data
func (u *UseExtendedMasterSecret) Unmarshal(data []byte) error {
if len(data) < useExtendedMasterSecretHeaderSize {
return errBufferTooSmall
} else if TypeValue(binary.BigEndian.Uint16(data)) != u.TypeValue() {
return errInvalidExtensionType
}
u.Supported = true
return nil
}

View File

@@ -0,0 +1,59 @@
package extension
import "encoding/binary"
const (
useSRTPHeaderSize = 6
)
// UseSRTP allows a Client/Server to negotiate what SRTPProtectionProfiles
// they both support
//
// https://tools.ietf.org/html/rfc8422
type UseSRTP struct {
ProtectionProfiles []SRTPProtectionProfile
}
// TypeValue returns the extension TypeValue
func (u UseSRTP) TypeValue() TypeValue {
return UseSRTPTypeValue
}
// Marshal encodes the extension
func (u *UseSRTP) Marshal() ([]byte, error) {
out := make([]byte, useSRTPHeaderSize)
binary.BigEndian.PutUint16(out, uint16(u.TypeValue()))
binary.BigEndian.PutUint16(out[2:], uint16(2+(len(u.ProtectionProfiles)*2)+ /* MKI Length */ 1))
binary.BigEndian.PutUint16(out[4:], uint16(len(u.ProtectionProfiles)*2))
for _, v := range u.ProtectionProfiles {
out = append(out, []byte{0x00, 0x00}...)
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(v))
}
out = append(out, 0x00) /* MKI Length */
return out, nil
}
// Unmarshal populates the extension from encoded data
func (u *UseSRTP) Unmarshal(data []byte) error {
if len(data) <= useSRTPHeaderSize {
return errBufferTooSmall
} else if TypeValue(binary.BigEndian.Uint16(data)) != u.TypeValue() {
return errInvalidExtensionType
}
profileCount := int(binary.BigEndian.Uint16(data[4:]) / 2)
if supportedGroupsHeaderSize+(profileCount*2) > len(data) {
return errLengthMismatch
}
for i := 0; i < profileCount; i++ {
supportedProfile := SRTPProtectionProfile(binary.BigEndian.Uint16(data[(useSRTPHeaderSize + (i * 2)):]))
if _, ok := srtpProtectionProfiles()[supportedProfile]; ok {
u.ProtectionProfiles = append(u.ProtectionProfiles, supportedProfile)
}
}
return nil
}