添加 github.com/pion/dtls 代码

This commit is contained in:
bjdgyc
2021-05-21 19:03:00 +08:00
parent 54a0cb7928
commit 28b5119f50
380 changed files with 16870 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
package extension
import (
"errors"
"github.com/pion/dtls/v2/pkg/protocol"
)
var (
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,96 @@
// 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
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 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,22 @@
package extension
import (
"errors"
"testing"
)
func TestExtensions(t *testing.T) {
t.Run("Zero", func(t *testing.T) {
extensions, err := Unmarshal([]byte{})
if err != nil || len(extensions) != 0 {
t.Fatal("Failed to decode zero extensions")
}
})
t.Run("Invalid", func(t *testing.T) {
extensions, err := Unmarshal([]byte{0x00})
if !errors.Is(err, errBufferTooSmall) || len(extensions) != 0 {
t.Fatal("Failed to error on invalid extension")
}
})
}

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,22 @@
package extension
import "testing"
func TestRenegotiationInfo(t *testing.T) {
extension := RenegotiationInfo{RenegotiatedConnection: 0}
raw, err := extension.Marshal()
if err != nil {
t.Fatal(err)
}
newExtension := RenegotiationInfo{}
err = newExtension.Unmarshal(raw)
if err != nil {
t.Fatal(err)
}
if newExtension.RenegotiatedConnection != extension.RenegotiatedConnection {
t.Errorf("extensionRenegotiationInfo marshal: got %d expected %d", newExtension.RenegotiatedConnection, extension.RenegotiatedConnection)
}
}

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 wishs 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,22 @@
package extension
import "testing"
func TestServerName(t *testing.T) {
extension := ServerName{ServerName: "test.domain"}
raw, err := extension.Marshal()
if err != nil {
t.Fatal(err)
}
newExtension := ServerName{}
err = newExtension.Unmarshal(raw)
if err != nil {
t.Fatal(err)
}
if newExtension.ServerName != extension.ServerName {
t.Errorf("extensionServerName marshal: got %s expected %s", newExtension.ServerName, extension.ServerName)
}
}

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,22 @@
package extension
import (
"reflect"
"testing"
"github.com/pion/dtls/v2/pkg/crypto/elliptic"
)
func TestExtensionSupportedGroups(t *testing.T) {
rawSupportedGroups := []byte{0x0, 0xa, 0x0, 0x4, 0x0, 0x2, 0x0, 0x1d}
parsedSupportedGroups := &SupportedEllipticCurves{
EllipticCurves: []elliptic.Curve{elliptic.X25519},
}
raw, err := parsedSupportedGroups.Marshal()
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(raw, rawSupportedGroups) {
t.Errorf("extensionSupportedGroups marshal: got %#v, want %#v", raw, rawSupportedGroups)
}
}

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,22 @@
package extension
import (
"reflect"
"testing"
"github.com/pion/dtls/v2/pkg/crypto/elliptic"
)
func TestExtensionSupportedPointFormats(t *testing.T) {
rawExtensionSupportedPointFormats := []byte{0x00, 0x0b, 0x00, 0x02, 0x01, 0x00}
parsedExtensionSupportedPointFormats := &SupportedPointFormats{
PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed},
}
raw, err := parsedExtensionSupportedPointFormats.Marshal()
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(raw, rawExtensionSupportedPointFormats) {
t.Errorf("extensionSupportedPointFormats marshal: got %#v, want %#v", raw, rawExtensionSupportedPointFormats)
}
}

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,35 @@
package extension
import (
"reflect"
"testing"
"github.com/pion/dtls/v2/pkg/crypto/hash"
"github.com/pion/dtls/v2/pkg/crypto/signature"
"github.com/pion/dtls/v2/pkg/crypto/signaturehash"
)
func TestExtensionSupportedSignatureAlgorithms(t *testing.T) {
rawExtensionSupportedSignatureAlgorithms := []byte{
0x00, 0x0d,
0x00, 0x08,
0x00, 0x06,
0x04, 0x03,
0x05, 0x03,
0x06, 0x03,
}
parsedExtensionSupportedSignatureAlgorithms := &SupportedSignatureAlgorithms{
SignatureHashAlgorithms: []signaturehash.Algorithm{
{Hash: hash.SHA256, Signature: signature.ECDSA},
{Hash: hash.SHA384, Signature: signature.ECDSA},
{Hash: hash.SHA512, Signature: signature.ECDSA},
},
}
raw, err := parsedExtensionSupportedSignatureAlgorithms.Marshal()
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(raw, rawExtensionSupportedSignatureAlgorithms) {
t.Errorf("extensionSupportedSignatureAlgorithms marshal: got %#v, want %#v", raw, rawExtensionSupportedSignatureAlgorithms)
}
}

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
}

View File

@@ -0,0 +1,20 @@
package extension
import (
"reflect"
"testing"
)
func TestExtensionUseSRTP(t *testing.T) {
rawUseSRTP := []byte{0x00, 0x0e, 0x00, 0x05, 0x00, 0x02, 0x00, 0x01, 0x00}
parsedUseSRTP := &UseSRTP{
ProtectionProfiles: []SRTPProtectionProfile{SRTP_AES128_CM_HMAC_SHA1_80},
}
raw, err := parsedUseSRTP.Marshal()
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(raw, rawUseSRTP) {
t.Errorf("extensionUseSRTP marshal: got %#v, want %#v", raw, rawUseSRTP)
}
}