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,29 @@
package handshake
import "encoding/binary"
func decodeCipherSuiteIDs(buf []byte) ([]uint16, error) {
if len(buf) < 2 {
return nil, errBufferTooSmall
}
cipherSuitesCount := int(binary.BigEndian.Uint16(buf[0:])) / 2
rtrn := make([]uint16, cipherSuitesCount)
for i := 0; i < cipherSuitesCount; i++ {
if len(buf) < (i*2 + 4) {
return nil, errBufferTooSmall
}
rtrn[i] = binary.BigEndian.Uint16(buf[(i*2)+2:])
}
return rtrn, nil
}
func encodeCipherSuiteIDs(cipherSuiteIDs []uint16) []byte {
out := []byte{0x00, 0x00}
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(cipherSuiteIDs)*2))
for _, id := range cipherSuiteIDs {
out = append(out, []byte{0x00, 0x00}...)
binary.BigEndian.PutUint16(out[len(out)-2:], id)
}
return out
}

View File

@@ -0,0 +1,25 @@
package handshake
import (
"errors"
"github.com/pion/dtls/v2/pkg/protocol"
)
// Typed errors
var (
errUnableToMarshalFragmented = &protocol.InternalError{Err: errors.New("unable to marshal fragmented handshakes")} //nolint:goerr113
errHandshakeMessageUnset = &protocol.InternalError{Err: errors.New("handshake message unset, unable to marshal")} //nolint:goerr113
errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
errLengthMismatch = &protocol.InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113
errInvalidClientKeyExchange = &protocol.FatalError{Err: errors.New("unable to determine if ClientKeyExchange is a public key or PSK Identity")} //nolint:goerr113
errInvalidHashAlgorithm = &protocol.FatalError{Err: errors.New("invalid hash algorithm")} //nolint:goerr113
errInvalidSignatureAlgorithm = &protocol.FatalError{Err: errors.New("invalid signature algorithm")} //nolint:goerr113
errCookieTooLong = &protocol.FatalError{Err: errors.New("cookie must not be longer then 255 bytes")} //nolint:goerr113
errInvalidEllipticCurveType = &protocol.FatalError{Err: errors.New("invalid or unknown elliptic curve type")} //nolint:goerr113
errInvalidNamedCurve = &protocol.FatalError{Err: errors.New("invalid named curve")} //nolint:goerr113
errCipherSuiteUnset = &protocol.FatalError{Err: errors.New("server hello can not be created without a cipher suite")} //nolint:goerr113
errCompressionMethodUnset = &protocol.FatalError{Err: errors.New("server hello can not be created without a compression method")} //nolint:goerr113
errInvalidCompressionMethod = &protocol.FatalError{Err: errors.New("invalid or unknown compression method")} //nolint:goerr113
errNotImplemented = &protocol.InternalError{Err: errors.New("feature has not been implemented yet")} //nolint:goerr113
)

View File

@@ -0,0 +1,144 @@
// Package handshake provides the DTLS wire protocol for handshakes
package handshake
import (
"github.com/pion/dtls/v2/internal/util"
"github.com/pion/dtls/v2/pkg/protocol"
)
// Type is the unique identifier for each handshake message
// https://tools.ietf.org/html/rfc5246#section-7.4
type Type uint8
// Types of DTLS Handshake messages we know about
const (
TypeHelloRequest Type = 0
TypeClientHello Type = 1
TypeServerHello Type = 2
TypeHelloVerifyRequest Type = 3
TypeCertificate Type = 11
TypeServerKeyExchange Type = 12
TypeCertificateRequest Type = 13
TypeServerHelloDone Type = 14
TypeCertificateVerify Type = 15
TypeClientKeyExchange Type = 16
TypeFinished Type = 20
)
// String returns the string representation of this type
func (t Type) String() string {
switch t {
case TypeHelloRequest:
return "HelloRequest"
case TypeClientHello:
return "ClientHello"
case TypeServerHello:
return "ServerHello"
case TypeHelloVerifyRequest:
return "HelloVerifyRequest"
case TypeCertificate:
return "TypeCertificate"
case TypeServerKeyExchange:
return "ServerKeyExchange"
case TypeCertificateRequest:
return "CertificateRequest"
case TypeServerHelloDone:
return "ServerHelloDone"
case TypeCertificateVerify:
return "CertificateVerify"
case TypeClientKeyExchange:
return "ClientKeyExchange"
case TypeFinished:
return "Finished"
}
return ""
}
// Message is the body of a Handshake datagram
type Message interface {
Marshal() ([]byte, error)
Unmarshal(data []byte) error
Type() Type
}
// Handshake protocol is responsible for selecting a cipher spec and
// generating a master secret, which together comprise the primary
// cryptographic parameters associated with a secure session. The
// handshake protocol can also optionally authenticate parties who have
// certificates signed by a trusted certificate authority.
// https://tools.ietf.org/html/rfc5246#section-7.3
type Handshake struct {
Header Header
Message Message
}
// ContentType returns what kind of content this message is carying
func (h Handshake) ContentType() protocol.ContentType {
return protocol.ContentTypeHandshake
}
// Marshal encodes a handshake into a binary message
func (h *Handshake) Marshal() ([]byte, error) {
if h.Message == nil {
return nil, errHandshakeMessageUnset
} else if h.Header.FragmentOffset != 0 {
return nil, errUnableToMarshalFragmented
}
msg, err := h.Message.Marshal()
if err != nil {
return nil, err
}
h.Header.Length = uint32(len(msg))
h.Header.FragmentLength = h.Header.Length
h.Header.Type = h.Message.Type()
header, err := h.Header.Marshal()
if err != nil {
return nil, err
}
return append(header, msg...), nil
}
// Unmarshal decodes a handshake from a binary message
func (h *Handshake) Unmarshal(data []byte) error {
if err := h.Header.Unmarshal(data); err != nil {
return err
}
reportedLen := util.BigEndianUint24(data[1:])
if uint32(len(data)-HeaderLength) != reportedLen {
return errLengthMismatch
} else if reportedLen != h.Header.FragmentLength {
return errLengthMismatch
}
switch Type(data[0]) {
case TypeHelloRequest:
return errNotImplemented
case TypeClientHello:
h.Message = &MessageClientHello{}
case TypeHelloVerifyRequest:
h.Message = &MessageHelloVerifyRequest{}
case TypeServerHello:
h.Message = &MessageServerHello{}
case TypeCertificate:
h.Message = &MessageCertificate{}
case TypeServerKeyExchange:
h.Message = &MessageServerKeyExchange{}
case TypeCertificateRequest:
h.Message = &MessageCertificateRequest{}
case TypeServerHelloDone:
h.Message = &MessageServerHelloDone{}
case TypeClientKeyExchange:
h.Message = &MessageClientKeyExchange{}
case TypeFinished:
h.Message = &MessageFinished{}
case TypeCertificateVerify:
h.Message = &MessageCertificateVerify{}
default:
return errNotImplemented
}
return h.Message.Unmarshal(data[HeaderLength:])
}

View File

@@ -0,0 +1,50 @@
package handshake
import (
"encoding/binary"
"github.com/pion/dtls/v2/internal/util"
)
// HeaderLength msg_len for Handshake messages assumes an extra
// 12 bytes for sequence, fragment and version information vs TLS
const HeaderLength = 12
// Header is the static first 12 bytes of each RecordLayer
// of type Handshake. These fields allow us to support message loss, reordering, and
// message fragmentation,
//
// https://tools.ietf.org/html/rfc6347#section-4.2.2
type Header struct {
Type Type
Length uint32 // uint24 in spec
MessageSequence uint16
FragmentOffset uint32 // uint24 in spec
FragmentLength uint32 // uint24 in spec
}
// Marshal encodes the Header
func (h *Header) Marshal() ([]byte, error) {
out := make([]byte, HeaderLength)
out[0] = byte(h.Type)
util.PutBigEndianUint24(out[1:], h.Length)
binary.BigEndian.PutUint16(out[4:], h.MessageSequence)
util.PutBigEndianUint24(out[6:], h.FragmentOffset)
util.PutBigEndianUint24(out[9:], h.FragmentLength)
return out, nil
}
// Unmarshal populates the header from encoded data
func (h *Header) Unmarshal(data []byte) error {
if len(data) < HeaderLength {
return errBufferTooSmall
}
h.Type = Type(data[0])
h.Length = util.BigEndianUint24(data[1:])
h.MessageSequence = binary.BigEndian.Uint16(data[4:])
h.FragmentOffset = util.BigEndianUint24(data[6:])
h.FragmentLength = util.BigEndianUint24(data[9:])
return nil
}

View File

@@ -0,0 +1,66 @@
package handshake
import (
"github.com/pion/dtls/v2/internal/util"
)
// MessageCertificate is a DTLS Handshake Message
// it can contain either a Client or Server Certificate
//
// https://tools.ietf.org/html/rfc5246#section-7.4.2
type MessageCertificate struct {
Certificate [][]byte
}
// Type returns the Handshake Type
func (m MessageCertificate) Type() Type {
return TypeCertificate
}
const (
handshakeMessageCertificateLengthFieldSize = 3
)
// Marshal encodes the Handshake
func (m *MessageCertificate) Marshal() ([]byte, error) {
out := make([]byte, handshakeMessageCertificateLengthFieldSize)
for _, r := range m.Certificate {
// Certificate Length
out = append(out, make([]byte, handshakeMessageCertificateLengthFieldSize)...)
util.PutBigEndianUint24(out[len(out)-handshakeMessageCertificateLengthFieldSize:], uint32(len(r)))
// Certificate body
out = append(out, append([]byte{}, r...)...)
}
// Total Payload Size
util.PutBigEndianUint24(out[0:], uint32(len(out[handshakeMessageCertificateLengthFieldSize:])))
return out, nil
}
// Unmarshal populates the message from encoded data
func (m *MessageCertificate) Unmarshal(data []byte) error {
if len(data) < handshakeMessageCertificateLengthFieldSize {
return errBufferTooSmall
}
if certificateBodyLen := int(util.BigEndianUint24(data)); certificateBodyLen+handshakeMessageCertificateLengthFieldSize != len(data) {
return errLengthMismatch
}
offset := handshakeMessageCertificateLengthFieldSize
for offset < len(data) {
certificateLen := int(util.BigEndianUint24(data[offset:]))
offset += handshakeMessageCertificateLengthFieldSize
if offset+certificateLen > len(data) {
return errLengthMismatch
}
m.Certificate = append(m.Certificate, append([]byte{}, data[offset:offset+certificateLen]...))
offset += certificateLen
}
return nil
}

View File

@@ -0,0 +1,100 @@
package handshake
import (
"encoding/binary"
"github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
"github.com/pion/dtls/v2/pkg/crypto/hash"
"github.com/pion/dtls/v2/pkg/crypto/signature"
"github.com/pion/dtls/v2/pkg/crypto/signaturehash"
)
/*
MessageCertificateRequest is so a non-anonymous server can optionally
request a certificate from the client, if appropriate for the selected cipher
suite. This message, if sent, will immediately follow the ServerKeyExchange
message (if it is sent; otherwise, this message follows the
server's Certificate message).
https://tools.ietf.org/html/rfc5246#section-7.4.4
*/
type MessageCertificateRequest struct {
CertificateTypes []clientcertificate.Type
SignatureHashAlgorithms []signaturehash.Algorithm
}
const (
messageCertificateRequestMinLength = 5
)
// Type returns the Handshake Type
func (m MessageCertificateRequest) Type() Type {
return TypeCertificateRequest
}
// Marshal encodes the Handshake
func (m *MessageCertificateRequest) Marshal() ([]byte, error) {
out := []byte{byte(len(m.CertificateTypes))}
for _, v := range m.CertificateTypes {
out = append(out, byte(v))
}
out = append(out, []byte{0x00, 0x00}...)
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(m.SignatureHashAlgorithms)*2))
for _, v := range m.SignatureHashAlgorithms {
out = append(out, byte(v.Hash))
out = append(out, byte(v.Signature))
}
out = append(out, []byte{0x00, 0x00}...) // Distinguished Names Length
return out, nil
}
// Unmarshal populates the message from encoded data
func (m *MessageCertificateRequest) Unmarshal(data []byte) error {
if len(data) < messageCertificateRequestMinLength {
return errBufferTooSmall
}
offset := 0
certificateTypesLength := int(data[0])
offset++
if (offset + certificateTypesLength) > len(data) {
return errBufferTooSmall
}
for i := 0; i < certificateTypesLength; i++ {
certType := clientcertificate.Type(data[offset+i])
if _, ok := clientcertificate.Types()[certType]; ok {
m.CertificateTypes = append(m.CertificateTypes, certType)
}
}
offset += certificateTypesLength
if len(data) < offset+2 {
return errBufferTooSmall
}
signatureHashAlgorithmsLength := int(binary.BigEndian.Uint16(data[offset:]))
offset += 2
if (offset + signatureHashAlgorithmsLength) > len(data) {
return errBufferTooSmall
}
for i := 0; i < signatureHashAlgorithmsLength; i += 2 {
if len(data) < (offset + i + 2) {
return errBufferTooSmall
}
h := hash.Algorithm(data[offset+i])
s := signature.Algorithm(data[offset+i+1])
if _, ok := hash.Algorithms()[h]; !ok {
continue
} else if _, ok := signature.Algorithms()[s]; !ok {
continue
}
m.SignatureHashAlgorithms = append(m.SignatureHashAlgorithms, signaturehash.Algorithm{Signature: s, Hash: h})
}
return nil
}

View File

@@ -0,0 +1,61 @@
package handshake
import (
"encoding/binary"
"github.com/pion/dtls/v2/pkg/crypto/hash"
"github.com/pion/dtls/v2/pkg/crypto/signature"
)
// MessageCertificateVerify provide explicit verification of a
// client certificate.
//
// https://tools.ietf.org/html/rfc5246#section-7.4.8
type MessageCertificateVerify struct {
HashAlgorithm hash.Algorithm
SignatureAlgorithm signature.Algorithm
Signature []byte
}
const handshakeMessageCertificateVerifyMinLength = 4
// Type returns the Handshake Type
func (m MessageCertificateVerify) Type() Type {
return TypeCertificateVerify
}
// Marshal encodes the Handshake
func (m *MessageCertificateVerify) Marshal() ([]byte, error) {
out := make([]byte, 1+1+2+len(m.Signature))
out[0] = byte(m.HashAlgorithm)
out[1] = byte(m.SignatureAlgorithm)
binary.BigEndian.PutUint16(out[2:], uint16(len(m.Signature)))
copy(out[4:], m.Signature)
return out, nil
}
// Unmarshal populates the message from encoded data
func (m *MessageCertificateVerify) Unmarshal(data []byte) error {
if len(data) < handshakeMessageCertificateVerifyMinLength {
return errBufferTooSmall
}
m.HashAlgorithm = hash.Algorithm(data[0])
if _, ok := hash.Algorithms()[m.HashAlgorithm]; !ok {
return errInvalidHashAlgorithm
}
m.SignatureAlgorithm = signature.Algorithm(data[1])
if _, ok := signature.Algorithms()[m.SignatureAlgorithm]; !ok {
return errInvalidSignatureAlgorithm
}
signatureLength := int(binary.BigEndian.Uint16(data[2:]))
if (signatureLength + 4) != len(data) {
return errBufferTooSmall
}
m.Signature = append([]byte{}, data[4:]...)
return nil
}

View File

@@ -0,0 +1,138 @@
package handshake
import (
"encoding/binary"
"github.com/pion/dtls/v2/pkg/protocol"
"github.com/pion/dtls/v2/pkg/protocol/extension"
)
/*
MessageClientHello is for when a client first connects to a server it is
required to send the client hello as its first message. The client can also send a
client hello in response to a hello request or on its own
initiative in order to renegotiate the security parameters in an
existing connection.
*/
type MessageClientHello struct {
Version protocol.Version
Random Random
Cookie []byte
SessionID []byte
CipherSuiteIDs []uint16
CompressionMethods []*protocol.CompressionMethod
Extensions []extension.Extension
}
const handshakeMessageClientHelloVariableWidthStart = 34
// Type returns the Handshake Type
func (m MessageClientHello) Type() Type {
return TypeClientHello
}
// Marshal encodes the Handshake
func (m *MessageClientHello) Marshal() ([]byte, error) {
if len(m.Cookie) > 255 {
return nil, errCookieTooLong
}
out := make([]byte, handshakeMessageClientHelloVariableWidthStart)
out[0] = m.Version.Major
out[1] = m.Version.Minor
rand := m.Random.MarshalFixed()
copy(out[2:], rand[:])
out = append(out, byte(len(m.SessionID)))
out = append(out, m.SessionID...)
out = append(out, byte(len(m.Cookie)))
out = append(out, m.Cookie...)
out = append(out, encodeCipherSuiteIDs(m.CipherSuiteIDs)...)
out = append(out, protocol.EncodeCompressionMethods(m.CompressionMethods)...)
extensions, err := extension.Marshal(m.Extensions)
if err != nil {
return nil, err
}
return append(out, extensions...), nil
}
// Unmarshal populates the message from encoded data
func (m *MessageClientHello) Unmarshal(data []byte) error {
if len(data) < 2+RandomLength {
return errBufferTooSmall
}
m.Version.Major = data[0]
m.Version.Minor = data[1]
var random [RandomLength]byte
copy(random[:], data[2:])
m.Random.UnmarshalFixed(random)
// rest of packet has variable width sections
currOffset := handshakeMessageClientHelloVariableWidthStart
currOffset++
if len(data) <= currOffset {
return errBufferTooSmall
}
n := int(data[currOffset-1])
if len(data) <= currOffset+n {
return errBufferTooSmall
}
m.SessionID = append([]byte{}, data[currOffset:currOffset+n]...)
currOffset += len(m.SessionID)
currOffset++
if len(data) <= currOffset {
return errBufferTooSmall
}
n = int(data[currOffset-1])
if len(data) <= currOffset+n {
return errBufferTooSmall
}
m.Cookie = append([]byte{}, data[currOffset:currOffset+n]...)
currOffset += len(m.Cookie)
// Cipher Suites
if len(data) < currOffset {
return errBufferTooSmall
}
cipherSuiteIDs, err := decodeCipherSuiteIDs(data[currOffset:])
if err != nil {
return err
}
m.CipherSuiteIDs = cipherSuiteIDs
if len(data) < currOffset+2 {
return errBufferTooSmall
}
currOffset += int(binary.BigEndian.Uint16(data[currOffset:])) + 2
// Compression Methods
if len(data) < currOffset {
return errBufferTooSmall
}
compressionMethods, err := protocol.DecodeCompressionMethods(data[currOffset:])
if err != nil {
return err
}
m.CompressionMethods = compressionMethods
if len(data) < currOffset {
return errBufferTooSmall
}
currOffset += int(data[currOffset]) + 1
// Extensions
extensions, err := extension.Unmarshal(data[currOffset:])
if err != nil {
return err
}
m.Extensions = extensions
return nil
}

View File

@@ -0,0 +1,56 @@
package handshake
import (
"encoding/binary"
)
// MessageClientKeyExchange is a DTLS Handshake Message
// With this message, the premaster secret is set, either by direct
// transmission of the RSA-encrypted secret or by the transmission of
// Diffie-Hellman parameters that will allow each side to agree upon
// the same premaster secret.
//
// https://tools.ietf.org/html/rfc5246#section-7.4.7
type MessageClientKeyExchange struct {
IdentityHint []byte
PublicKey []byte
}
// Type returns the Handshake Type
func (m MessageClientKeyExchange) Type() Type {
return TypeClientKeyExchange
}
// Marshal encodes the Handshake
func (m *MessageClientKeyExchange) Marshal() ([]byte, error) {
switch {
case (m.IdentityHint != nil && m.PublicKey != nil) || (m.IdentityHint == nil && m.PublicKey == nil):
return nil, errInvalidClientKeyExchange
case m.PublicKey != nil:
return append([]byte{byte(len(m.PublicKey))}, m.PublicKey...), nil
default:
out := append([]byte{0x00, 0x00}, m.IdentityHint...)
binary.BigEndian.PutUint16(out, uint16(len(out)-2))
return out, nil
}
}
// Unmarshal populates the message from encoded data
func (m *MessageClientKeyExchange) Unmarshal(data []byte) error {
if len(data) < 2 {
return errBufferTooSmall
}
// If parsed as PSK return early and only populate PSK Identity Hint
if pskLength := binary.BigEndian.Uint16(data); len(data) == int(pskLength+2) {
m.IdentityHint = append([]byte{}, data[2:]...)
return nil
}
if publicKeyLength := int(data[0]); len(data) != publicKeyLength+1 {
return errBufferTooSmall
}
m.PublicKey = append([]byte{}, data[1:]...)
return nil
}

View File

@@ -0,0 +1,27 @@
package handshake
// MessageFinished is a DTLS Handshake Message
// this message is the first one protected with the just
// negotiated algorithms, keys, and secrets. Recipients of Finished
// messages MUST verify that the contents are correct.
//
// https://tools.ietf.org/html/rfc5246#section-7.4.9
type MessageFinished struct {
VerifyData []byte
}
// Type returns the Handshake Type
func (m MessageFinished) Type() Type {
return TypeFinished
}
// Marshal encodes the Handshake
func (m *MessageFinished) Marshal() ([]byte, error) {
return append([]byte{}, m.VerifyData...), nil
}
// Unmarshal populates the message from encoded data
func (m *MessageFinished) Unmarshal(data []byte) error {
m.VerifyData = append([]byte{}, data...)
return nil
}

View File

@@ -0,0 +1,62 @@
package handshake
import (
"github.com/pion/dtls/v2/pkg/protocol"
)
// MessageHelloVerifyRequest is as follows:
//
// struct {
// ProtocolVersion server_version;
// opaque cookie<0..2^8-1>;
// } HelloVerifyRequest;
//
// The HelloVerifyRequest message type is hello_verify_request(3).
//
// When the client sends its ClientHello message to the server, the server
// MAY respond with a HelloVerifyRequest message. This message contains
// a stateless cookie generated using the technique of [PHOTURIS]. The
// client MUST retransmit the ClientHello with the cookie added.
//
// https://tools.ietf.org/html/rfc6347#section-4.2.1
type MessageHelloVerifyRequest struct {
Version protocol.Version
Cookie []byte
}
// Type returns the Handshake Type
func (m MessageHelloVerifyRequest) Type() Type {
return TypeHelloVerifyRequest
}
// Marshal encodes the Handshake
func (m *MessageHelloVerifyRequest) Marshal() ([]byte, error) {
if len(m.Cookie) > 255 {
return nil, errCookieTooLong
}
out := make([]byte, 3+len(m.Cookie))
out[0] = m.Version.Major
out[1] = m.Version.Minor
out[2] = byte(len(m.Cookie))
copy(out[3:], m.Cookie)
return out, nil
}
// Unmarshal populates the message from encoded data
func (m *MessageHelloVerifyRequest) Unmarshal(data []byte) error {
if len(data) < 3 {
return errBufferTooSmall
}
m.Version.Major = data[0]
m.Version.Minor = data[1]
cookieLength := data[2]
if len(data) < (int(cookieLength) + 3) {
return errBufferTooSmall
}
m.Cookie = make([]byte, cookieLength)
copy(m.Cookie, data[3:3+cookieLength])
return nil
}

View File

@@ -0,0 +1,116 @@
package handshake
import (
"encoding/binary"
"github.com/pion/dtls/v2/pkg/protocol"
"github.com/pion/dtls/v2/pkg/protocol/extension"
)
// MessageServerHello is sent in response to a ClientHello
// message when it was able to find an acceptable set of algorithms.
// If it cannot find such a match, it will respond with a handshake
// failure alert.
//
// https://tools.ietf.org/html/rfc5246#section-7.4.1.3
type MessageServerHello struct {
Version protocol.Version
Random Random
SessionID []byte
CipherSuiteID *uint16
CompressionMethod *protocol.CompressionMethod
Extensions []extension.Extension
}
const messageServerHelloVariableWidthStart = 2 + RandomLength
// Type returns the Handshake Type
func (m MessageServerHello) Type() Type {
return TypeServerHello
}
// Marshal encodes the Handshake
func (m *MessageServerHello) Marshal() ([]byte, error) {
if m.CipherSuiteID == nil {
return nil, errCipherSuiteUnset
} else if m.CompressionMethod == nil {
return nil, errCompressionMethodUnset
}
out := make([]byte, messageServerHelloVariableWidthStart)
out[0] = m.Version.Major
out[1] = m.Version.Minor
rand := m.Random.MarshalFixed()
copy(out[2:], rand[:])
out = append(out, byte(len(m.SessionID)))
out = append(out, m.SessionID...)
out = append(out, []byte{0x00, 0x00}...)
binary.BigEndian.PutUint16(out[len(out)-2:], *m.CipherSuiteID)
out = append(out, byte(m.CompressionMethod.ID))
extensions, err := extension.Marshal(m.Extensions)
if err != nil {
return nil, err
}
return append(out, extensions...), nil
}
// Unmarshal populates the message from encoded data
func (m *MessageServerHello) Unmarshal(data []byte) error {
if len(data) < 2+RandomLength {
return errBufferTooSmall
}
m.Version.Major = data[0]
m.Version.Minor = data[1]
var random [RandomLength]byte
copy(random[:], data[2:])
m.Random.UnmarshalFixed(random)
currOffset := messageServerHelloVariableWidthStart
currOffset++
if len(data) <= currOffset {
return errBufferTooSmall
}
n := int(data[currOffset-1])
if len(data) <= currOffset+n {
return errBufferTooSmall
}
m.SessionID = append([]byte{}, data[currOffset:currOffset+n]...)
currOffset += len(m.SessionID)
m.CipherSuiteID = new(uint16)
*m.CipherSuiteID = binary.BigEndian.Uint16(data[currOffset:])
currOffset += 2
if len(data) < currOffset {
return errBufferTooSmall
}
if compressionMethod, ok := protocol.CompressionMethods()[protocol.CompressionMethodID(data[currOffset])]; ok {
m.CompressionMethod = compressionMethod
currOffset++
} else {
return errInvalidCompressionMethod
}
if len(data) <= currOffset {
m.Extensions = []extension.Extension{}
return nil
}
extensions, err := extension.Unmarshal(data[currOffset:])
if err != nil {
return err
}
m.Extensions = extensions
return nil
}

View File

@@ -0,0 +1,21 @@
package handshake
// MessageServerHelloDone is final non-encrypted message from server
// this communicates server has sent all its handshake messages and next
// should be MessageFinished
type MessageServerHelloDone struct{}
// Type returns the Handshake Type
func (m MessageServerHelloDone) Type() Type {
return TypeServerHelloDone
}
// Marshal encodes the Handshake
func (m *MessageServerHelloDone) Marshal() ([]byte, error) {
return []byte{}, nil
}
// Unmarshal populates the message from encoded data
func (m *MessageServerHelloDone) Unmarshal(data []byte) error {
return nil
}

View File

@@ -0,0 +1,119 @@
package handshake
import (
"encoding/binary"
"github.com/pion/dtls/v2/pkg/crypto/elliptic"
"github.com/pion/dtls/v2/pkg/crypto/hash"
"github.com/pion/dtls/v2/pkg/crypto/signature"
)
// MessageServerKeyExchange supports ECDH and PSK
type MessageServerKeyExchange struct {
IdentityHint []byte
EllipticCurveType elliptic.CurveType
NamedCurve elliptic.Curve
PublicKey []byte
HashAlgorithm hash.Algorithm
SignatureAlgorithm signature.Algorithm
Signature []byte
}
// Type returns the Handshake Type
func (m MessageServerKeyExchange) Type() Type {
return TypeServerKeyExchange
}
// Marshal encodes the Handshake
func (m *MessageServerKeyExchange) Marshal() ([]byte, error) {
if m.IdentityHint != nil {
out := append([]byte{0x00, 0x00}, m.IdentityHint...)
binary.BigEndian.PutUint16(out, uint16(len(out)-2))
return out, nil
}
out := []byte{byte(m.EllipticCurveType), 0x00, 0x00}
binary.BigEndian.PutUint16(out[1:], uint16(m.NamedCurve))
out = append(out, byte(len(m.PublicKey)))
out = append(out, m.PublicKey...)
if m.HashAlgorithm == hash.None && m.SignatureAlgorithm == signature.Anonymous && len(m.Signature) == 0 {
return out, nil
}
out = append(out, []byte{byte(m.HashAlgorithm), byte(m.SignatureAlgorithm), 0x00, 0x00}...)
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(m.Signature)))
out = append(out, m.Signature...)
return out, nil
}
// Unmarshal populates the message from encoded data
func (m *MessageServerKeyExchange) Unmarshal(data []byte) error {
if len(data) < 2 {
return errBufferTooSmall
}
// If parsed as PSK return early and only populate PSK Identity Hint
if pskLength := binary.BigEndian.Uint16(data); len(data) == int(pskLength+2) {
m.IdentityHint = append([]byte{}, data[2:]...)
return nil
}
if _, ok := elliptic.CurveTypes()[elliptic.CurveType(data[0])]; ok {
m.EllipticCurveType = elliptic.CurveType(data[0])
} else {
return errInvalidEllipticCurveType
}
if len(data[1:]) < 2 {
return errBufferTooSmall
}
m.NamedCurve = elliptic.Curve(binary.BigEndian.Uint16(data[1:3]))
if _, ok := elliptic.Curves()[m.NamedCurve]; !ok {
return errInvalidNamedCurve
}
if len(data) < 4 {
return errBufferTooSmall
}
publicKeyLength := int(data[3])
offset := 4 + publicKeyLength
if len(data) < offset {
return errBufferTooSmall
}
m.PublicKey = append([]byte{}, data[4:offset]...)
// Anon connection doesn't contains hashAlgorithm, signatureAlgorithm, signature
if len(data) == offset {
return nil
} else if len(data) <= offset {
return errBufferTooSmall
}
m.HashAlgorithm = hash.Algorithm(data[offset])
if _, ok := hash.Algorithms()[m.HashAlgorithm]; !ok {
return errInvalidHashAlgorithm
}
offset++
if len(data) <= offset {
return errBufferTooSmall
}
m.SignatureAlgorithm = signature.Algorithm(data[offset])
if _, ok := signature.Algorithms()[m.SignatureAlgorithm]; !ok {
return errInvalidSignatureAlgorithm
}
offset++
if len(data) < offset+2 {
return errBufferTooSmall
}
signatureLength := int(binary.BigEndian.Uint16(data[offset:]))
offset += 2
if len(data) < offset+signatureLength {
return errBufferTooSmall
}
m.Signature = append([]byte{}, data[offset:offset+signatureLength]...)
return nil
}

View File

@@ -0,0 +1,49 @@
package handshake
import (
"crypto/rand"
"encoding/binary"
"time"
)
// Consts for Random in Handshake
const (
RandomBytesLength = 28
RandomLength = RandomBytesLength + 4
)
// Random value that is used in ClientHello and ServerHello
//
// https://tools.ietf.org/html/rfc4346#section-7.4.1.2
type Random struct {
GMTUnixTime time.Time
RandomBytes [RandomBytesLength]byte
}
// MarshalFixed encodes the Handshake
func (r *Random) MarshalFixed() [RandomLength]byte {
var out [RandomLength]byte
binary.BigEndian.PutUint32(out[0:], uint32(r.GMTUnixTime.Unix()))
copy(out[4:], r.RandomBytes[:])
return out
}
// UnmarshalFixed populates the message from encoded data
func (r *Random) UnmarshalFixed(data [RandomLength]byte) {
r.GMTUnixTime = time.Unix(int64(binary.BigEndian.Uint32(data[0:])), 0)
copy(r.RandomBytes[:], data[4:])
}
// Populate fills the handshakeRandom with random values
// may be called multiple times
func (r *Random) Populate() error {
r.GMTUnixTime = time.Now()
tmp := make([]byte, RandomBytesLength)
_, err := rand.Read(tmp)
copy(r.RandomBytes[:], tmp)
return err
}