alpn.go 2.0 KB

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