github.com/pion/dtls/v2@v2.2.12/pkg/protocol/extension/alpn.go (about) 1 // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly> 2 // SPDX-License-Identifier: MIT 3 4 package extension 5 6 import ( 7 "golang.org/x/crypto/cryptobyte" 8 ) 9 10 // ALPN is a TLS extension for application-layer protocol negotiation within 11 // the TLS handshake. 12 // 13 // https://tools.ietf.org/html/rfc7301 14 type ALPN struct { 15 ProtocolNameList []string 16 } 17 18 // TypeValue returns the extension TypeValue 19 func (a ALPN) TypeValue() TypeValue { 20 return ALPNTypeValue 21 } 22 23 // Marshal encodes the extension 24 func (a *ALPN) Marshal() ([]byte, error) { 25 var b cryptobyte.Builder 26 b.AddUint16(uint16(a.TypeValue())) 27 b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { 28 b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { 29 for _, proto := range a.ProtocolNameList { 30 p := proto // Satisfy range scope lint 31 b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { 32 b.AddBytes([]byte(p)) 33 }) 34 } 35 }) 36 }) 37 return b.Bytes() 38 } 39 40 // Unmarshal populates the extension from encoded data 41 func (a *ALPN) Unmarshal(data []byte) error { 42 val := cryptobyte.String(data) 43 44 var extension uint16 45 val.ReadUint16(&extension) 46 if TypeValue(extension) != a.TypeValue() { 47 return errInvalidExtensionType 48 } 49 50 var extData cryptobyte.String 51 val.ReadUint16LengthPrefixed(&extData) 52 53 var protoList cryptobyte.String 54 if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() { 55 return ErrALPNInvalidFormat 56 } 57 for !protoList.Empty() { 58 var proto cryptobyte.String 59 if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() { 60 return ErrALPNInvalidFormat 61 } 62 a.ProtocolNameList = append(a.ProtocolNameList, string(proto)) 63 } 64 return nil 65 } 66 67 // ALPNProtocolSelection negotiates a shared protocol according to #3.2 of rfc7301 68 func ALPNProtocolSelection(supportedProtocols, peerSupportedProtocols []string) (string, error) { 69 if len(supportedProtocols) == 0 || len(peerSupportedProtocols) == 0 { 70 return "", nil 71 } 72 for _, s := range supportedProtocols { 73 for _, c := range peerSupportedProtocols { 74 if s == c { 75 return s, nil 76 } 77 } 78 } 79 return "", errALPNNoAppProto 80 }