github.com/pion/dtls/v2@v2.2.12/pkg/protocol/handshake/message_client_hello.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  package handshake
     5  
     6  import (
     7  	"encoding/binary"
     8  
     9  	"github.com/pion/dtls/v2/pkg/protocol"
    10  	"github.com/pion/dtls/v2/pkg/protocol/extension"
    11  )
    12  
    13  /*
    14  MessageClientHello is for when a client first connects to a server it is
    15  required to send the client hello as its first message.  The client can also send a
    16  client hello in response to a hello request or on its own
    17  initiative in order to renegotiate the security parameters in an
    18  existing connection.
    19  */
    20  type MessageClientHello struct {
    21  	Version protocol.Version
    22  	Random  Random
    23  	Cookie  []byte
    24  
    25  	SessionID []byte
    26  
    27  	CipherSuiteIDs     []uint16
    28  	CompressionMethods []*protocol.CompressionMethod
    29  	Extensions         []extension.Extension
    30  }
    31  
    32  const handshakeMessageClientHelloVariableWidthStart = 34
    33  
    34  // Type returns the Handshake Type
    35  func (m MessageClientHello) Type() Type {
    36  	return TypeClientHello
    37  }
    38  
    39  // Marshal encodes the Handshake
    40  func (m *MessageClientHello) Marshal() ([]byte, error) {
    41  	if len(m.Cookie) > 255 {
    42  		return nil, errCookieTooLong
    43  	}
    44  
    45  	out := make([]byte, handshakeMessageClientHelloVariableWidthStart)
    46  	out[0] = m.Version.Major
    47  	out[1] = m.Version.Minor
    48  
    49  	rand := m.Random.MarshalFixed()
    50  	copy(out[2:], rand[:])
    51  
    52  	out = append(out, byte(len(m.SessionID)))
    53  	out = append(out, m.SessionID...)
    54  
    55  	out = append(out, byte(len(m.Cookie)))
    56  	out = append(out, m.Cookie...)
    57  	out = append(out, encodeCipherSuiteIDs(m.CipherSuiteIDs)...)
    58  	out = append(out, protocol.EncodeCompressionMethods(m.CompressionMethods)...)
    59  
    60  	extensions, err := extension.Marshal(m.Extensions)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	return append(out, extensions...), nil
    66  }
    67  
    68  // Unmarshal populates the message from encoded data
    69  func (m *MessageClientHello) Unmarshal(data []byte) error {
    70  	if len(data) < 2+RandomLength {
    71  		return errBufferTooSmall
    72  	}
    73  
    74  	m.Version.Major = data[0]
    75  	m.Version.Minor = data[1]
    76  
    77  	var random [RandomLength]byte
    78  	copy(random[:], data[2:])
    79  	m.Random.UnmarshalFixed(random)
    80  
    81  	// rest of packet has variable width sections
    82  	currOffset := handshakeMessageClientHelloVariableWidthStart
    83  
    84  	currOffset++
    85  	if len(data) <= currOffset {
    86  		return errBufferTooSmall
    87  	}
    88  	n := int(data[currOffset-1])
    89  	if len(data) <= currOffset+n {
    90  		return errBufferTooSmall
    91  	}
    92  	m.SessionID = append([]byte{}, data[currOffset:currOffset+n]...)
    93  	currOffset += len(m.SessionID)
    94  
    95  	currOffset++
    96  	if len(data) <= currOffset {
    97  		return errBufferTooSmall
    98  	}
    99  	n = int(data[currOffset-1])
   100  	if len(data) <= currOffset+n {
   101  		return errBufferTooSmall
   102  	}
   103  	m.Cookie = append([]byte{}, data[currOffset:currOffset+n]...)
   104  	currOffset += len(m.Cookie)
   105  
   106  	// Cipher Suites
   107  	if len(data) < currOffset {
   108  		return errBufferTooSmall
   109  	}
   110  	cipherSuiteIDs, err := decodeCipherSuiteIDs(data[currOffset:])
   111  	if err != nil {
   112  		return err
   113  	}
   114  	m.CipherSuiteIDs = cipherSuiteIDs
   115  	if len(data) < currOffset+2 {
   116  		return errBufferTooSmall
   117  	}
   118  	currOffset += int(binary.BigEndian.Uint16(data[currOffset:])) + 2
   119  
   120  	// Compression Methods
   121  	if len(data) < currOffset {
   122  		return errBufferTooSmall
   123  	}
   124  	compressionMethods, err := protocol.DecodeCompressionMethods(data[currOffset:])
   125  	if err != nil {
   126  		return err
   127  	}
   128  	m.CompressionMethods = compressionMethods
   129  	if len(data) < currOffset {
   130  		return errBufferTooSmall
   131  	}
   132  	currOffset += int(data[currOffset]) + 1
   133  
   134  	// Extensions
   135  	extensions, err := extension.Unmarshal(data[currOffset:])
   136  	if err != nil {
   137  		return err
   138  	}
   139  	m.Extensions = extensions
   140  	return nil
   141  }