mirror of
https://github.com/bjdgyc/anylink.git
synced 2025-08-08 11:10:14 +08:00
添加 github.com/pion/dtls 代码
This commit is contained in:
251
dtls-2.0.9/pkg/crypto/ccm/ccm.go
Normal file
251
dtls-2.0.9/pkg/crypto/ccm/ccm.go
Normal file
@@ -0,0 +1,251 @@
|
||||
// Package ccm implements a CCM, Counter with CBC-MAC
|
||||
// as per RFC 3610.
|
||||
//
|
||||
// See https://tools.ietf.org/html/rfc3610
|
||||
//
|
||||
// This code was lifted from https://github.com/bocajim/dtls/blob/a3300364a283fcb490d28a93d7fcfa7ba437fbbe/ccm/ccm.go
|
||||
// and as such was not written by the Pions authors. Like Pions this
|
||||
// code is licensed under MIT.
|
||||
//
|
||||
// A request for including CCM into the Go standard library
|
||||
// can be found as issue #27484 on the https://github.com/golang/go/
|
||||
// repository.
|
||||
package ccm
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math"
|
||||
)
|
||||
|
||||
// ccm represents a Counter with CBC-MAC with a specific key.
|
||||
type ccm struct {
|
||||
b cipher.Block
|
||||
M uint8
|
||||
L uint8
|
||||
}
|
||||
|
||||
const ccmBlockSize = 16
|
||||
|
||||
// CCM is a block cipher in Counter with CBC-MAC mode.
|
||||
// Providing authenticated encryption with associated data via the cipher.AEAD interface.
|
||||
type CCM interface {
|
||||
cipher.AEAD
|
||||
// MaxLength returns the maxium length of plaintext in calls to Seal.
|
||||
// The maximum length of ciphertext in calls to Open is MaxLength()+Overhead().
|
||||
// The maximum length is related to CCM's `L` parameter (15-noncesize) and
|
||||
// is 1<<(8*L) - 1 (but also limited by the maxium size of an int).
|
||||
MaxLength() int
|
||||
}
|
||||
|
||||
var (
|
||||
errInvalidBlockSize = errors.New("ccm: NewCCM requires 128-bit block cipher")
|
||||
errInvalidTagSize = errors.New("ccm: tagsize must be 4, 6, 8, 10, 12, 14, or 16")
|
||||
errInvalidNonceSize = errors.New("ccm: invalid nonce size")
|
||||
)
|
||||
|
||||
// NewCCM returns the given 128-bit block cipher wrapped in CCM.
|
||||
// The tagsize must be an even integer between 4 and 16 inclusive
|
||||
// and is used as CCM's `M` parameter.
|
||||
// The noncesize must be an integer between 7 and 13 inclusive,
|
||||
// 15-noncesize is used as CCM's `L` parameter.
|
||||
func NewCCM(b cipher.Block, tagsize, noncesize int) (CCM, error) {
|
||||
if b.BlockSize() != ccmBlockSize {
|
||||
return nil, errInvalidBlockSize
|
||||
}
|
||||
if tagsize < 4 || tagsize > 16 || tagsize&1 != 0 {
|
||||
return nil, errInvalidTagSize
|
||||
}
|
||||
lensize := 15 - noncesize
|
||||
if lensize < 2 || lensize > 8 {
|
||||
return nil, errInvalidNonceSize
|
||||
}
|
||||
c := &ccm{b: b, M: uint8(tagsize), L: uint8(lensize)}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *ccm) NonceSize() int { return 15 - int(c.L) }
|
||||
func (c *ccm) Overhead() int { return int(c.M) }
|
||||
func (c *ccm) MaxLength() int { return maxlen(c.L, c.Overhead()) }
|
||||
|
||||
func maxlen(l uint8, tagsize int) int {
|
||||
max := (uint64(1) << (8 * l)) - 1
|
||||
if m64 := uint64(math.MaxInt64) - uint64(tagsize); l > 8 || max > m64 {
|
||||
max = m64 // The maximum lentgh on a 64bit arch
|
||||
}
|
||||
if max != uint64(int(max)) {
|
||||
return math.MaxInt32 - tagsize // We have only 32bit int's
|
||||
}
|
||||
return int(max)
|
||||
}
|
||||
|
||||
// MaxNonceLength returns the maximum nonce length for a given plaintext length.
|
||||
// A return value <= 0 indicates that plaintext length is too large for
|
||||
// any nonce length.
|
||||
func MaxNonceLength(pdatalen int) int {
|
||||
const tagsize = 16
|
||||
for L := 2; L <= 8; L++ {
|
||||
if maxlen(uint8(L), tagsize) >= pdatalen {
|
||||
return 15 - L
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *ccm) cbcRound(mac, data []byte) {
|
||||
for i := 0; i < ccmBlockSize; i++ {
|
||||
mac[i] ^= data[i]
|
||||
}
|
||||
c.b.Encrypt(mac, mac)
|
||||
}
|
||||
|
||||
func (c *ccm) cbcData(mac, data []byte) {
|
||||
for len(data) >= ccmBlockSize {
|
||||
c.cbcRound(mac, data[:ccmBlockSize])
|
||||
data = data[ccmBlockSize:]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
var block [ccmBlockSize]byte
|
||||
copy(block[:], data)
|
||||
c.cbcRound(mac, block[:])
|
||||
}
|
||||
}
|
||||
|
||||
var errPlaintextTooLong = errors.New("ccm: plaintext too large")
|
||||
|
||||
func (c *ccm) tag(nonce, plaintext, adata []byte) ([]byte, error) {
|
||||
var mac [ccmBlockSize]byte
|
||||
|
||||
if len(adata) > 0 {
|
||||
mac[0] |= 1 << 6
|
||||
}
|
||||
mac[0] |= (c.M - 2) << 2
|
||||
mac[0] |= c.L - 1
|
||||
if len(nonce) != c.NonceSize() {
|
||||
return nil, errInvalidNonceSize
|
||||
}
|
||||
if len(plaintext) > c.MaxLength() {
|
||||
return nil, errPlaintextTooLong
|
||||
}
|
||||
binary.BigEndian.PutUint64(mac[ccmBlockSize-8:], uint64(len(plaintext)))
|
||||
copy(mac[1:ccmBlockSize-c.L], nonce)
|
||||
c.b.Encrypt(mac[:], mac[:])
|
||||
|
||||
var block [ccmBlockSize]byte
|
||||
if n := uint64(len(adata)); n > 0 {
|
||||
// First adata block includes adata length
|
||||
i := 2
|
||||
if n <= 0xfeff {
|
||||
binary.BigEndian.PutUint16(block[:i], uint16(n))
|
||||
} else {
|
||||
block[0] = 0xfe
|
||||
block[1] = 0xff
|
||||
if n < uint64(1<<32) {
|
||||
i = 2 + 4
|
||||
binary.BigEndian.PutUint32(block[2:i], uint32(n))
|
||||
} else {
|
||||
i = 2 + 8
|
||||
binary.BigEndian.PutUint64(block[2:i], n)
|
||||
}
|
||||
}
|
||||
i = copy(block[i:], adata)
|
||||
c.cbcRound(mac[:], block[:])
|
||||
c.cbcData(mac[:], adata[i:])
|
||||
}
|
||||
|
||||
if len(plaintext) > 0 {
|
||||
c.cbcData(mac[:], plaintext)
|
||||
}
|
||||
|
||||
return mac[:c.M], nil
|
||||
}
|
||||
|
||||
// sliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||
// slice with the contents of the given slice followed by that many bytes and a
|
||||
// second slice that aliases into it and contains only the extra bytes. If the
|
||||
// original slice has sufficient capacity then no allocation is performed.
|
||||
// From crypto/cipher/gcm.go
|
||||
func sliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||
if total := len(in) + n; cap(in) >= total {
|
||||
head = in[:total]
|
||||
} else {
|
||||
head = make([]byte, total)
|
||||
copy(head, in)
|
||||
}
|
||||
tail = head[len(in):]
|
||||
return
|
||||
}
|
||||
|
||||
// Seal encrypts and authenticates plaintext, authenticates the
|
||||
// additional data and appends the result to dst, returning the updated
|
||||
// slice. The nonce must be NonceSize() bytes long and unique for all
|
||||
// time, for a given key.
|
||||
// The plaintext must be no longer than MaxLength() bytes long.
|
||||
//
|
||||
// The plaintext and dst may alias exactly or not at all.
|
||||
func (c *ccm) Seal(dst, nonce, plaintext, adata []byte) []byte {
|
||||
tag, err := c.tag(nonce, plaintext, adata)
|
||||
if err != nil {
|
||||
// The cipher.AEAD interface doesn't allow for an error return.
|
||||
panic(err) // nolint
|
||||
}
|
||||
|
||||
var iv, s0 [ccmBlockSize]byte
|
||||
iv[0] = c.L - 1
|
||||
copy(iv[1:ccmBlockSize-c.L], nonce)
|
||||
c.b.Encrypt(s0[:], iv[:])
|
||||
for i := 0; i < int(c.M); i++ {
|
||||
tag[i] ^= s0[i]
|
||||
}
|
||||
iv[len(iv)-1] |= 1
|
||||
stream := cipher.NewCTR(c.b, iv[:])
|
||||
ret, out := sliceForAppend(dst, len(plaintext)+int(c.M))
|
||||
stream.XORKeyStream(out, plaintext)
|
||||
copy(out[len(plaintext):], tag)
|
||||
return ret
|
||||
}
|
||||
|
||||
var (
|
||||
errOpen = errors.New("ccm: message authentication failed")
|
||||
errCiphertextTooShort = errors.New("ccm: ciphertext too short")
|
||||
errCiphertextTooLong = errors.New("ccm: ciphertext too long")
|
||||
)
|
||||
|
||||
func (c *ccm) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) {
|
||||
if len(ciphertext) < int(c.M) {
|
||||
return nil, errCiphertextTooShort
|
||||
}
|
||||
if len(ciphertext) > c.MaxLength()+c.Overhead() {
|
||||
return nil, errCiphertextTooLong
|
||||
}
|
||||
|
||||
tag := make([]byte, int(c.M))
|
||||
copy(tag, ciphertext[len(ciphertext)-int(c.M):])
|
||||
ciphertextWithoutTag := ciphertext[:len(ciphertext)-int(c.M)]
|
||||
|
||||
var iv, s0 [ccmBlockSize]byte
|
||||
iv[0] = c.L - 1
|
||||
copy(iv[1:ccmBlockSize-c.L], nonce)
|
||||
c.b.Encrypt(s0[:], iv[:])
|
||||
for i := 0; i < int(c.M); i++ {
|
||||
tag[i] ^= s0[i]
|
||||
}
|
||||
iv[len(iv)-1] |= 1
|
||||
stream := cipher.NewCTR(c.b, iv[:])
|
||||
|
||||
// Cannot decrypt directly to dst since we're not supposed to
|
||||
// reveal the plaintext to the caller if authentication fails.
|
||||
plaintext := make([]byte, len(ciphertextWithoutTag))
|
||||
stream.XORKeyStream(plaintext, ciphertextWithoutTag)
|
||||
expectedTag, err := c.tag(nonce, plaintext, adata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if subtle.ConstantTimeCompare(tag, expectedTag) != 1 {
|
||||
return nil, errOpen
|
||||
}
|
||||
return append(dst, plaintext...), nil
|
||||
}
|
419
dtls-2.0.9/pkg/crypto/ccm/ccm_test.go
Normal file
419
dtls-2.0.9/pkg/crypto/ccm/ccm_test.go
Normal file
@@ -0,0 +1,419 @@
|
||||
package ccm
|
||||
|
||||
// Refer to RFC 3610 section 8 for the vectors.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func mustHexDecode(s string) []byte {
|
||||
r, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
var (
|
||||
aesKey1to12 = mustHexDecode("c0c1c2c3c4c5c6c7c8c9cacbcccdcecf") //nolint:gochecknoglobals
|
||||
aesKey13to24 = mustHexDecode("d7828d13b2b0bdc325a76236df93cc6b") //nolint:gochecknoglobals
|
||||
)
|
||||
|
||||
// AESKey: AES Key
|
||||
// CipherText: Authenticated and encrypted output
|
||||
// ClearHeaderOctets: Input with X cleartext header octets
|
||||
// Data: Input with X cleartext header octets
|
||||
// M: length(CBC-MAC)
|
||||
// Nonce: Nonce
|
||||
type vector struct {
|
||||
AESKey []byte
|
||||
CipherText []byte
|
||||
ClearHeaderOctets int
|
||||
Data []byte
|
||||
M int
|
||||
Nonce []byte
|
||||
}
|
||||
|
||||
func TestRFC3610Vectors(t *testing.T) {
|
||||
cases := []vector{
|
||||
// Vectors 1-12
|
||||
{
|
||||
AESKey: aesKey1to12,
|
||||
CipherText: mustHexDecode("0001020304050607588c979a61c663d2f066d0c2c0f989806d5f6b61dac38417e8d12cfdf926e0"),
|
||||
ClearHeaderOctets: 8,
|
||||
Data: mustHexDecode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e"),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("00000003020100a0a1a2a3a4a5"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey1to12,
|
||||
CipherText: mustHexDecode("000102030405060772c91a36e135f8cf291ca894085c87e3cc15c439c9e43a3ba091d56e10400916"),
|
||||
ClearHeaderOctets: 8,
|
||||
Data: mustHexDecode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("00000004030201a0a1a2a3a4a5"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey1to12,
|
||||
CipherText: mustHexDecode("000102030405060751b1e5f44a197d1da46b0f8e2d282ae871e838bb64da8596574adaa76fbd9fb0c5"),
|
||||
ClearHeaderOctets: 8,
|
||||
Data: mustHexDecode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("00000005040302a0a1a2a3a4a5"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey1to12,
|
||||
CipherText: mustHexDecode("000102030405060708090a0ba28c6865939a9a79faaa5c4c2a9d4a91cdac8c96c861b9c9e61ef1"),
|
||||
ClearHeaderOctets: 12,
|
||||
Data: mustHexDecode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e"),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("00000006050403a0a1a2a3a4a5"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey1to12,
|
||||
CipherText: mustHexDecode("000102030405060708090a0bdcf1fb7b5d9e23fb9d4e131253658ad86ebdca3e51e83f077d9c2d93"),
|
||||
ClearHeaderOctets: 12,
|
||||
Data: mustHexDecode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("00000007060504a0a1a2a3a4a5"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey1to12,
|
||||
CipherText: mustHexDecode("000102030405060708090a0b6fc1b011f006568b5171a42d953d469b2570a4bd87405a0443ac91cb94"),
|
||||
ClearHeaderOctets: 12,
|
||||
Data: mustHexDecode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("00000008070605a0a1a2a3a4a5"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey1to12,
|
||||
CipherText: mustHexDecode("00010203040506070135d1b2c95f41d5d1d4fec185d166b8094e999dfed96c048c56602c97acbb7490"),
|
||||
ClearHeaderOctets: 8,
|
||||
Data: mustHexDecode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e"),
|
||||
M: 10,
|
||||
Nonce: mustHexDecode("00000009080706a0a1a2a3a4a5"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey1to12,
|
||||
CipherText: mustHexDecode("00010203040506077b75399ac0831dd2f0bbd75879a2fd8f6cae6b6cd9b7db24c17b4433f434963f34b4"),
|
||||
ClearHeaderOctets: 8,
|
||||
Data: mustHexDecode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"),
|
||||
M: 10,
|
||||
Nonce: mustHexDecode("0000000a090807a0a1a2a3a4a5"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey1to12,
|
||||
CipherText: mustHexDecode("000102030405060782531a60cc24945a4b8279181ab5c84df21ce7f9b73f42e197ea9c07e56b5eb17e5f4e"),
|
||||
ClearHeaderOctets: 8,
|
||||
Data: mustHexDecode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
||||
M: 10,
|
||||
Nonce: mustHexDecode("0000000b0a0908a0a1a2a3a4a5"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey1to12,
|
||||
CipherText: mustHexDecode("000102030405060708090a0b07342594157785152b074098330abb141b947b566aa9406b4d999988dd"),
|
||||
ClearHeaderOctets: 12,
|
||||
Data: mustHexDecode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e"),
|
||||
M: 10,
|
||||
Nonce: mustHexDecode("0000000c0b0a09a0a1a2a3a4a5"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey1to12,
|
||||
CipherText: mustHexDecode("000102030405060708090a0b676bb20380b0e301e8ab79590a396da78b834934f53aa2e9107a8b6c022c"),
|
||||
ClearHeaderOctets: 12,
|
||||
Data: mustHexDecode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"),
|
||||
M: 10,
|
||||
Nonce: mustHexDecode("0000000d0c0b0aa0a1a2a3a4a5"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey1to12,
|
||||
CipherText: mustHexDecode("000102030405060708090a0bc0ffa0d6f05bdb67f24d43a4338d2aa4bed7b20e43cd1aa31662e7ad65d6db"),
|
||||
ClearHeaderOctets: 12,
|
||||
Data: mustHexDecode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
||||
M: 10,
|
||||
Nonce: mustHexDecode("0000000e0d0c0ba0a1a2a3a4a5"),
|
||||
},
|
||||
// Vectors 13-24
|
||||
{
|
||||
AESKey: aesKey13to24,
|
||||
CipherText: mustHexDecode("0be1a88bace018b14cb97f86a2a4689a877947ab8091ef5386a6ffbdd080f8e78cf7cb0cddd7b3"),
|
||||
ClearHeaderOctets: 8,
|
||||
Data: mustHexDecode("0be1a88bace018b108e8cf97d820ea258460e96ad9cf5289054d895ceac47c"),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("00412b4ea9cdbe3c9696766cfa"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey13to24,
|
||||
CipherText: mustHexDecode("63018f76dc8a1bcb4ccb1e7ca981befaa0726c55d378061298c85c92814abc33c52ee81d7d77c08a"),
|
||||
ClearHeaderOctets: 8,
|
||||
Data: mustHexDecode("63018f76dc8a1bcb9020ea6f91bdd85afa0039ba4baff9bfb79c7028949cd0ec"),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("0033568ef7b2633c9696766cfa"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey13to24,
|
||||
CipherText: mustHexDecode("aa6cfa36cae86b40b1d23a2220ddc0ac900d9aa03c61fcf4a559a4417767089708a776796edb723506"),
|
||||
ClearHeaderOctets: 8,
|
||||
Data: mustHexDecode("aa6cfa36cae86b40b916e0eacc1c00d7dcec68ec0b3bbb1a02de8a2d1aa346132e"),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("00103fe41336713c9696766cfa"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey13to24,
|
||||
CipherText: mustHexDecode("d0d0735c531e1becf049c24414d253c3967b70609b7cbb7c499160283245269a6f49975bcadeaf"),
|
||||
ClearHeaderOctets: 12,
|
||||
Data: mustHexDecode("d0d0735c531e1becf049c24412daac5630efa5396f770ce1a66b21f7b2101c"),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("00764c63b8058e3c9696766cfa"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey13to24,
|
||||
CipherText: mustHexDecode("77b60f011c03e1525899bcae5545ff1a085ee2efbf52b2e04bee1e2336c73e3f762c0c7744fe7e3c"),
|
||||
ClearHeaderOctets: 12,
|
||||
Data: mustHexDecode("77b60f011c03e1525899bcaee88b6a46c78d63e52eb8c546efb5de6f75e9cc0d"),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("00f8b678094e3b3c9696766cfa"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey13to24,
|
||||
CipherText: mustHexDecode("cd9044d2b71fdb8120ea60c0009769ecabdf48625594c59251e6035722675e04c847099e5ae0704551"),
|
||||
ClearHeaderOctets: 12,
|
||||
Data: mustHexDecode("cd9044d2b71fdb8120ea60c06435acbafb11a82e2f071d7ca4a5ebd93a803ba87f"),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("00d560912d3f703c9696766cfa"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey13to24,
|
||||
CipherText: mustHexDecode("d85bc7e69f944fb8bc218daa947427b6db386a99ac1aef23ade0b52939cb6a637cf9bec2408897c6ba"),
|
||||
ClearHeaderOctets: 8,
|
||||
Data: mustHexDecode("d85bc7e69f944fb88a19b950bcf71a018e5e6701c91787659809d67dbedd18"),
|
||||
M: 10,
|
||||
Nonce: mustHexDecode("0042fff8f1951c3c9696766cfa"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey13to24,
|
||||
CipherText: mustHexDecode("74a0ebc9069f5b375810e6fd25874022e80361a478e3e9cf484ab04f447efff6f0a477cc2fc9bf548944"),
|
||||
ClearHeaderOctets: 8,
|
||||
Data: mustHexDecode("74a0ebc9069f5b371761433c37c5a35fc1f39f406302eb907c6163be38c98437"),
|
||||
M: 10,
|
||||
Nonce: mustHexDecode("00920f40e56cdc3c9696766cfa"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey13to24,
|
||||
CipherText: mustHexDecode("44a3aa3aae6475caf2beed7bc5098e83feb5b31608f8e29c38819a89c8e776f1544d4151a4ed3a8b87b9ce"),
|
||||
ClearHeaderOctets: 8,
|
||||
Data: mustHexDecode("44a3aa3aae6475caa434a8e58500c6e41530538862d686ea9e81301b5ae4226bfa"),
|
||||
M: 10,
|
||||
Nonce: mustHexDecode("0027ca0c7120bc3c9696766cfa"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey13to24,
|
||||
CipherText: mustHexDecode("ec46bb63b02520c33c49fd7031d750a09da3ed7fddd49a2032aabf17ec8ebf7d22c8088c666be5c197"),
|
||||
ClearHeaderOctets: 12,
|
||||
Data: mustHexDecode("ec46bb63b02520c33c49fd70b96b49e21d621741632875db7f6c9243d2d7c2"),
|
||||
M: 10,
|
||||
Nonce: mustHexDecode("005b8ccbcd9af83c9696766cfa"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey13to24,
|
||||
CipherText: mustHexDecode("47a65ac78b3d594227e85e71e882f1dbd38ce3eda7c23f04dd65071eb41342acdf7e00dccec7ae52987d"),
|
||||
ClearHeaderOctets: 12,
|
||||
Data: mustHexDecode("47a65ac78b3d594227e85e71e2fcfbb880442c731bf95167c8ffd7895e337076"),
|
||||
M: 10,
|
||||
Nonce: mustHexDecode("003ebe94044b9a3c9696766cfa"),
|
||||
},
|
||||
{
|
||||
AESKey: aesKey13to24,
|
||||
CipherText: mustHexDecode("6e37a6ef546d955d34ab6059f32905b88a641b04b9c9ffb58cc390900f3da12ab16dce9e82efa16da62059"),
|
||||
ClearHeaderOctets: 12,
|
||||
Data: mustHexDecode("6e37a6ef546d955d34ab6059abf21c0b02feb88f856df4a37381bce3cc128517d4"),
|
||||
M: 10,
|
||||
Nonce: mustHexDecode("008d493b30ae8b3c9696766cfa"),
|
||||
},
|
||||
}
|
||||
|
||||
if len(cases) != 24 {
|
||||
t.Fatalf("Expected %d test cases, got: %d", 24, len(cases))
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
for idx, c := range cases {
|
||||
c := c
|
||||
t.Run(fmt.Sprintf("packet vector #%d", idx+1), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
blk, err := aes.NewCipher(c.AESKey)
|
||||
if err != nil {
|
||||
t.Fatalf("could not initialize AES block cipher from key: %v", err)
|
||||
}
|
||||
|
||||
lccm, err := NewCCM(blk, c.M, len(c.Nonce))
|
||||
if err != nil {
|
||||
t.Fatalf("could not create CCM: %v", err)
|
||||
}
|
||||
|
||||
t.Run("seal", func(t *testing.T) {
|
||||
var dst []byte
|
||||
dst = lccm.Seal(dst, c.Nonce, c.Data[c.ClearHeaderOctets:], c.Data[:c.ClearHeaderOctets])
|
||||
if !bytes.Equal(c.CipherText[c.ClearHeaderOctets:], dst) {
|
||||
t.Fatalf("ciphertext does not match, wanted %v, got %v",
|
||||
c.CipherText[c.ClearHeaderOctets:], dst)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("open", func(t *testing.T) {
|
||||
var dst []byte
|
||||
dst, err = lccm.Open(dst, c.Nonce, c.CipherText[c.ClearHeaderOctets:], c.CipherText[:c.ClearHeaderOctets])
|
||||
if err != nil {
|
||||
t.Fatalf("failed to unseal: %v", err)
|
||||
}
|
||||
if !bytes.Equal(c.Data[c.ClearHeaderOctets:], dst) {
|
||||
t.Fatalf("plaintext does not match, wanted %v, got %v",
|
||||
c.Data[c.ClearHeaderOctets:], dst)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCCMError(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
vector
|
||||
err error
|
||||
}{
|
||||
"ShortNonceLength": {
|
||||
vector{
|
||||
AESKey: aesKey1to12,
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("a0a1a2a3a4a5"),
|
||||
}, errInvalidNonceSize,
|
||||
},
|
||||
"LongNonceLength": {
|
||||
vector{
|
||||
AESKey: aesKey1to12,
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("0001020304050607080910111213"),
|
||||
}, errInvalidNonceSize,
|
||||
},
|
||||
"ShortTag": {
|
||||
vector{
|
||||
AESKey: aesKey1to12,
|
||||
M: 3,
|
||||
Nonce: mustHexDecode("00010203040506070809101112"),
|
||||
}, errInvalidTagSize,
|
||||
},
|
||||
"LongTag": {
|
||||
vector{
|
||||
AESKey: aesKey1to12,
|
||||
M: 17,
|
||||
Nonce: mustHexDecode("00010203040506070809101112"),
|
||||
}, errInvalidTagSize,
|
||||
},
|
||||
}
|
||||
|
||||
for name, c := range cases {
|
||||
c := c
|
||||
t.Run(name, func(t *testing.T) {
|
||||
blk, err := aes.NewCipher(c.AESKey)
|
||||
if err != nil {
|
||||
t.Fatalf("could not initialize AES block cipher from key: %v", err)
|
||||
}
|
||||
|
||||
if _, err := NewCCM(blk, c.M, len(c.Nonce)); !errors.Is(err, c.err) {
|
||||
t.Fatalf("expected error '%v', got '%v'", c.err, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSealError(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
vector
|
||||
err error
|
||||
}{
|
||||
"InvalidNonceLength": {
|
||||
vector{
|
||||
Data: mustHexDecode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e"),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("00000003020100a0a1a2a3a4"), // short
|
||||
}, errInvalidNonceSize,
|
||||
},
|
||||
"PlaintextTooLong": {
|
||||
vector{
|
||||
Data: make([]byte, 100000),
|
||||
M: 8,
|
||||
Nonce: mustHexDecode("00000003020100a0a1a2a3a4a5"),
|
||||
}, errPlaintextTooLong,
|
||||
},
|
||||
}
|
||||
|
||||
blk, err := aes.NewCipher(aesKey1to12)
|
||||
if err != nil {
|
||||
t.Fatalf("could not initialize AES block cipher from key: %v", err)
|
||||
}
|
||||
|
||||
lccm, err := NewCCM(blk, 8, 13)
|
||||
if err != nil {
|
||||
t.Fatalf("could not create CCM: %v", err)
|
||||
}
|
||||
|
||||
for name, c := range cases {
|
||||
c := c
|
||||
t.Run(name, func(t *testing.T) {
|
||||
defer func() {
|
||||
if err := recover(); !errors.Is(err.(error), c.err) {
|
||||
t.Errorf("expected panic '%v', got '%v'", c.err, err)
|
||||
}
|
||||
}()
|
||||
var dst []byte
|
||||
_ = lccm.Seal(dst, c.Nonce, c.Data[c.ClearHeaderOctets:], c.Data[:c.ClearHeaderOctets])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenError(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
vector
|
||||
err error
|
||||
}{
|
||||
"CiphertextTooShort": {
|
||||
vector{
|
||||
CipherText: make([]byte, 10),
|
||||
ClearHeaderOctets: 8,
|
||||
Nonce: mustHexDecode("00000003020100a0a1a2a3a4a5"),
|
||||
}, errCiphertextTooShort,
|
||||
},
|
||||
"CiphertextTooLong": {
|
||||
vector{
|
||||
CipherText: make([]byte, 100000),
|
||||
ClearHeaderOctets: 8,
|
||||
Nonce: mustHexDecode("00000003020100a0a1a2a3a4a5"),
|
||||
}, errCiphertextTooLong,
|
||||
},
|
||||
}
|
||||
|
||||
blk, err := aes.NewCipher(aesKey1to12)
|
||||
if err != nil {
|
||||
t.Fatalf("could not initialize AES block cipher from key: %v", err)
|
||||
}
|
||||
|
||||
lccm, err := NewCCM(blk, 8, 13)
|
||||
if err != nil {
|
||||
t.Fatalf("could not create CCM: %v", err)
|
||||
}
|
||||
|
||||
for name, c := range cases {
|
||||
c := c
|
||||
t.Run(name, func(t *testing.T) {
|
||||
var dst []byte
|
||||
_, err = lccm.Open(dst, c.Nonce, c.CipherText[c.ClearHeaderOctets:], c.CipherText[:c.ClearHeaderOctets])
|
||||
if !errors.Is(err, c.err) {
|
||||
t.Errorf("expected error '%v', got '%v'", c.err, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
164
dtls-2.0.9/pkg/crypto/ciphersuite/cbc.go
Normal file
164
dtls-2.0.9/pkg/crypto/ciphersuite/cbc.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package ciphersuite
|
||||
|
||||
import ( //nolint:gci
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
|
||||
"github.com/pion/dtls/v2/internal/util"
|
||||
"github.com/pion/dtls/v2/pkg/crypto/prf"
|
||||
"github.com/pion/dtls/v2/pkg/protocol"
|
||||
"github.com/pion/dtls/v2/pkg/protocol/recordlayer"
|
||||
)
|
||||
|
||||
// block ciphers using cipher block chaining.
|
||||
type cbcMode interface {
|
||||
cipher.BlockMode
|
||||
SetIV([]byte)
|
||||
}
|
||||
|
||||
// CBC Provides an API to Encrypt/Decrypt DTLS 1.2 Packets
|
||||
type CBC struct {
|
||||
writeCBC, readCBC cbcMode
|
||||
writeMac, readMac []byte
|
||||
h prf.HashFunc
|
||||
}
|
||||
|
||||
// NewCBC creates a DTLS CBC Cipher
|
||||
func NewCBC(localKey, localWriteIV, localMac, remoteKey, remoteWriteIV, remoteMac []byte, h prf.HashFunc) (*CBC, error) {
|
||||
writeBlock, err := aes.NewCipher(localKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
readBlock, err := aes.NewCipher(remoteKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CBC{
|
||||
writeCBC: cipher.NewCBCEncrypter(writeBlock, localWriteIV).(cbcMode),
|
||||
writeMac: localMac,
|
||||
|
||||
readCBC: cipher.NewCBCDecrypter(readBlock, remoteWriteIV).(cbcMode),
|
||||
readMac: remoteMac,
|
||||
h: h,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Encrypt encrypt a DTLS RecordLayer message
|
||||
func (c *CBC) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
|
||||
payload := raw[recordlayer.HeaderSize:]
|
||||
raw = raw[:recordlayer.HeaderSize]
|
||||
blockSize := c.writeCBC.BlockSize()
|
||||
|
||||
// Generate + Append MAC
|
||||
h := pkt.Header
|
||||
|
||||
MAC, err := c.hmac(h.Epoch, h.SequenceNumber, h.ContentType, h.Version, payload, c.writeMac, c.h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payload = append(payload, MAC...)
|
||||
|
||||
// Generate + Append padding
|
||||
padding := make([]byte, blockSize-len(payload)%blockSize)
|
||||
paddingLen := len(padding)
|
||||
for i := 0; i < paddingLen; i++ {
|
||||
padding[i] = byte(paddingLen - 1)
|
||||
}
|
||||
payload = append(payload, padding...)
|
||||
|
||||
// Generate IV
|
||||
iv := make([]byte, blockSize)
|
||||
if _, err := rand.Read(iv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set IV + Encrypt + Prepend IV
|
||||
c.writeCBC.SetIV(iv)
|
||||
c.writeCBC.CryptBlocks(payload, payload)
|
||||
payload = append(iv, payload...)
|
||||
|
||||
// Prepend unencrypte header with encrypted payload
|
||||
raw = append(raw, payload...)
|
||||
|
||||
// Update recordLayer size to include IV+MAC+Padding
|
||||
binary.BigEndian.PutUint16(raw[recordlayer.HeaderSize-2:], uint16(len(raw)-recordlayer.HeaderSize))
|
||||
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts a DTLS RecordLayer message
|
||||
func (c *CBC) Decrypt(in []byte) ([]byte, error) {
|
||||
body := in[recordlayer.HeaderSize:]
|
||||
blockSize := c.readCBC.BlockSize()
|
||||
mac := c.h()
|
||||
|
||||
var h recordlayer.Header
|
||||
err := h.Unmarshal(in)
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case h.ContentType == protocol.ContentTypeChangeCipherSpec:
|
||||
// Nothing to encrypt with ChangeCipherSpec
|
||||
return in, nil
|
||||
case len(body)%blockSize != 0 || len(body) < blockSize+util.Max(mac.Size()+1, blockSize):
|
||||
return nil, errNotEnoughRoomForNonce
|
||||
}
|
||||
|
||||
// Set + remove per record IV
|
||||
c.readCBC.SetIV(body[:blockSize])
|
||||
body = body[blockSize:]
|
||||
|
||||
// Decrypt
|
||||
c.readCBC.CryptBlocks(body, body)
|
||||
|
||||
// Padding+MAC needs to be checked in constant time
|
||||
// Otherwise we reveal information about the level of correctness
|
||||
paddingLen, paddingGood := examinePadding(body)
|
||||
if paddingGood != 255 {
|
||||
return nil, errInvalidMAC
|
||||
}
|
||||
|
||||
macSize := mac.Size()
|
||||
if len(body) < macSize {
|
||||
return nil, errInvalidMAC
|
||||
}
|
||||
|
||||
dataEnd := len(body) - macSize - paddingLen
|
||||
|
||||
expectedMAC := body[dataEnd : dataEnd+macSize]
|
||||
actualMAC, err := c.hmac(h.Epoch, h.SequenceNumber, h.ContentType, h.Version, body[:dataEnd], c.readMac, c.h)
|
||||
|
||||
// Compute Local MAC and compare
|
||||
if err != nil || !hmac.Equal(actualMAC, expectedMAC) {
|
||||
return nil, errInvalidMAC
|
||||
}
|
||||
|
||||
return append(in[:recordlayer.HeaderSize], body[:dataEnd]...), nil
|
||||
}
|
||||
|
||||
func (c *CBC) hmac(epoch uint16, sequenceNumber uint64, contentType protocol.ContentType, protocolVersion protocol.Version, payload []byte, key []byte, hf func() hash.Hash) ([]byte, error) {
|
||||
h := hmac.New(hf, key)
|
||||
|
||||
msg := make([]byte, 13)
|
||||
|
||||
binary.BigEndian.PutUint16(msg, epoch)
|
||||
util.PutBigEndianUint48(msg[2:], sequenceNumber)
|
||||
msg[8] = byte(contentType)
|
||||
msg[9] = protocolVersion.Major
|
||||
msg[10] = protocolVersion.Minor
|
||||
binary.BigEndian.PutUint16(msg[11:], uint16(len(payload)))
|
||||
|
||||
if _, err := h.Write(msg); err != nil {
|
||||
return nil, err
|
||||
} else if _, err := h.Write(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return h.Sum(nil), nil
|
||||
}
|
104
dtls-2.0.9/pkg/crypto/ciphersuite/ccm.go
Normal file
104
dtls-2.0.9/pkg/crypto/ciphersuite/ccm.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package ciphersuite
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/crypto/ccm"
|
||||
"github.com/pion/dtls/v2/pkg/protocol"
|
||||
"github.com/pion/dtls/v2/pkg/protocol/recordlayer"
|
||||
)
|
||||
|
||||
// CCMTagLen is the length of Authentication Tag
|
||||
type CCMTagLen int
|
||||
|
||||
// CCM Enums
|
||||
const (
|
||||
CCMTagLength8 CCMTagLen = 8
|
||||
CCMTagLength CCMTagLen = 16
|
||||
ccmNonceLength = 12
|
||||
)
|
||||
|
||||
// CCM Provides an API to Encrypt/Decrypt DTLS 1.2 Packets
|
||||
type CCM struct {
|
||||
localCCM, remoteCCM ccm.CCM
|
||||
localWriteIV, remoteWriteIV []byte
|
||||
tagLen CCMTagLen
|
||||
}
|
||||
|
||||
// NewCCM creates a DTLS GCM Cipher
|
||||
func NewCCM(tagLen CCMTagLen, localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*CCM, error) {
|
||||
localBlock, err := aes.NewCipher(localKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localCCM, err := ccm.NewCCM(localBlock, int(tagLen), ccmNonceLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
remoteBlock, err := aes.NewCipher(remoteKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
remoteCCM, err := ccm.NewCCM(remoteBlock, int(tagLen), ccmNonceLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CCM{
|
||||
localCCM: localCCM,
|
||||
localWriteIV: localWriteIV,
|
||||
remoteCCM: remoteCCM,
|
||||
remoteWriteIV: remoteWriteIV,
|
||||
tagLen: tagLen,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Encrypt encrypt a DTLS RecordLayer message
|
||||
func (c *CCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
|
||||
payload := raw[recordlayer.HeaderSize:]
|
||||
raw = raw[:recordlayer.HeaderSize]
|
||||
|
||||
nonce := append(append([]byte{}, c.localWriteIV[:4]...), make([]byte, 8)...)
|
||||
if _, err := rand.Read(nonce[4:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
additionalData := generateAEADAdditionalData(&pkt.Header, len(payload))
|
||||
encryptedPayload := c.localCCM.Seal(nil, nonce, payload, additionalData)
|
||||
|
||||
encryptedPayload = append(nonce[4:], encryptedPayload...)
|
||||
raw = append(raw, encryptedPayload...)
|
||||
|
||||
// Update recordLayer size to include explicit nonce
|
||||
binary.BigEndian.PutUint16(raw[recordlayer.HeaderSize-2:], uint16(len(raw)-recordlayer.HeaderSize))
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts a DTLS RecordLayer message
|
||||
func (c *CCM) Decrypt(in []byte) ([]byte, error) {
|
||||
var h recordlayer.Header
|
||||
err := h.Unmarshal(in)
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case h.ContentType == protocol.ContentTypeChangeCipherSpec:
|
||||
// Nothing to encrypt with ChangeCipherSpec
|
||||
return in, nil
|
||||
case len(in) <= (8 + recordlayer.HeaderSize):
|
||||
return nil, errNotEnoughRoomForNonce
|
||||
}
|
||||
|
||||
nonce := append(append([]byte{}, c.remoteWriteIV[:4]...), in[recordlayer.HeaderSize:recordlayer.HeaderSize+8]...)
|
||||
out := in[recordlayer.HeaderSize+8:]
|
||||
|
||||
additionalData := generateAEADAdditionalData(&h, len(out)-int(c.tagLen))
|
||||
out, err = c.remoteCCM.Open(out[:0], nonce, out, additionalData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", errDecryptPacket, err)
|
||||
}
|
||||
return append(in[:recordlayer.HeaderSize], out...), nil
|
||||
}
|
72
dtls-2.0.9/pkg/crypto/ciphersuite/ciphersuite.go
Normal file
72
dtls-2.0.9/pkg/crypto/ciphersuite/ciphersuite.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Package ciphersuite provides the crypto operations needed for a DTLS CipherSuite
|
||||
package ciphersuite
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/protocol"
|
||||
"github.com/pion/dtls/v2/pkg/protocol/recordlayer"
|
||||
)
|
||||
|
||||
var (
|
||||
errNotEnoughRoomForNonce = &protocol.InternalError{Err: errors.New("buffer not long enough to contain nonce")} //nolint:goerr113
|
||||
errDecryptPacket = &protocol.TemporaryError{Err: errors.New("failed to decrypt packet")} //nolint:goerr113
|
||||
errInvalidMAC = &protocol.TemporaryError{Err: errors.New("invalid mac")} //nolint:goerr113
|
||||
)
|
||||
|
||||
func generateAEADAdditionalData(h *recordlayer.Header, payloadLen int) []byte {
|
||||
var additionalData [13]byte
|
||||
// SequenceNumber MUST be set first
|
||||
// we only want uint48, clobbering an extra 2 (using uint64, Golang doesn't have uint48)
|
||||
binary.BigEndian.PutUint64(additionalData[:], h.SequenceNumber)
|
||||
binary.BigEndian.PutUint16(additionalData[:], h.Epoch)
|
||||
additionalData[8] = byte(h.ContentType)
|
||||
additionalData[9] = h.Version.Major
|
||||
additionalData[10] = h.Version.Minor
|
||||
binary.BigEndian.PutUint16(additionalData[len(additionalData)-2:], uint16(payloadLen))
|
||||
|
||||
return additionalData[:]
|
||||
}
|
||||
|
||||
// examinePadding returns, in constant time, the length of the padding to remove
|
||||
// from the end of payload. It also returns a byte which is equal to 255 if the
|
||||
// padding was valid and 0 otherwise. See RFC 2246, Section 6.2.3.2.
|
||||
//
|
||||
// https://github.com/golang/go/blob/039c2081d1178f90a8fa2f4e6958693129f8de33/src/crypto/tls/conn.go#L245
|
||||
func examinePadding(payload []byte) (toRemove int, good byte) {
|
||||
if len(payload) < 1 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
paddingLen := payload[len(payload)-1]
|
||||
t := uint(len(payload)-1) - uint(paddingLen)
|
||||
// if len(payload) >= (paddingLen - 1) then the MSB of t is zero
|
||||
good = byte(int32(^t) >> 31)
|
||||
|
||||
// The maximum possible padding length plus the actual length field
|
||||
toCheck := 256
|
||||
// The length of the padded data is public, so we can use an if here
|
||||
if toCheck > len(payload) {
|
||||
toCheck = len(payload)
|
||||
}
|
||||
|
||||
for i := 0; i < toCheck; i++ {
|
||||
t := uint(paddingLen) - uint(i)
|
||||
// if i <= paddingLen then the MSB of t is zero
|
||||
mask := byte(int32(^t) >> 31)
|
||||
b := payload[len(payload)-1-i]
|
||||
good &^= mask&paddingLen ^ mask&b
|
||||
}
|
||||
|
||||
// We AND together the bits of good and replicate the result across
|
||||
// all the bits.
|
||||
good &= good << 4
|
||||
good &= good << 2
|
||||
good &= good << 1
|
||||
good = uint8(int8(good) >> 7)
|
||||
|
||||
toRemove = int(paddingLen) + 1
|
||||
|
||||
return toRemove, good
|
||||
}
|
100
dtls-2.0.9/pkg/crypto/ciphersuite/gcm.go
Normal file
100
dtls-2.0.9/pkg/crypto/ciphersuite/gcm.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package ciphersuite
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/protocol"
|
||||
"github.com/pion/dtls/v2/pkg/protocol/recordlayer"
|
||||
)
|
||||
|
||||
const (
|
||||
gcmTagLength = 16
|
||||
gcmNonceLength = 12
|
||||
)
|
||||
|
||||
// GCM Provides an API to Encrypt/Decrypt DTLS 1.2 Packets
|
||||
type GCM struct {
|
||||
localGCM, remoteGCM cipher.AEAD
|
||||
localWriteIV, remoteWriteIV []byte
|
||||
}
|
||||
|
||||
// NewGCM creates a DTLS GCM Cipher
|
||||
func NewGCM(localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*GCM, error) {
|
||||
localBlock, err := aes.NewCipher(localKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localGCM, err := cipher.NewGCM(localBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
remoteBlock, err := aes.NewCipher(remoteKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
remoteGCM, err := cipher.NewGCM(remoteBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &GCM{
|
||||
localGCM: localGCM,
|
||||
localWriteIV: localWriteIV,
|
||||
remoteGCM: remoteGCM,
|
||||
remoteWriteIV: remoteWriteIV,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Encrypt encrypt a DTLS RecordLayer message
|
||||
func (g *GCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
|
||||
payload := raw[recordlayer.HeaderSize:]
|
||||
raw = raw[:recordlayer.HeaderSize]
|
||||
|
||||
nonce := make([]byte, gcmNonceLength)
|
||||
copy(nonce, g.localWriteIV[:4])
|
||||
if _, err := rand.Read(nonce[4:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
additionalData := generateAEADAdditionalData(&pkt.Header, len(payload))
|
||||
encryptedPayload := g.localGCM.Seal(nil, nonce, payload, additionalData)
|
||||
r := make([]byte, len(raw)+len(nonce[4:])+len(encryptedPayload))
|
||||
copy(r, raw)
|
||||
copy(r[len(raw):], nonce[4:])
|
||||
copy(r[len(raw)+len(nonce[4:]):], encryptedPayload)
|
||||
|
||||
// Update recordLayer size to include explicit nonce
|
||||
binary.BigEndian.PutUint16(r[recordlayer.HeaderSize-2:], uint16(len(r)-recordlayer.HeaderSize))
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts a DTLS RecordLayer message
|
||||
func (g *GCM) Decrypt(in []byte) ([]byte, error) {
|
||||
var h recordlayer.Header
|
||||
err := h.Unmarshal(in)
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case h.ContentType == protocol.ContentTypeChangeCipherSpec:
|
||||
// Nothing to encrypt with ChangeCipherSpec
|
||||
return in, nil
|
||||
case len(in) <= (8 + recordlayer.HeaderSize):
|
||||
return nil, errNotEnoughRoomForNonce
|
||||
}
|
||||
|
||||
nonce := make([]byte, 0, gcmNonceLength)
|
||||
nonce = append(append(nonce, g.remoteWriteIV[:4]...), in[recordlayer.HeaderSize:recordlayer.HeaderSize+8]...)
|
||||
out := in[recordlayer.HeaderSize+8:]
|
||||
|
||||
additionalData := generateAEADAdditionalData(&h, len(out)-gcmTagLength)
|
||||
out, err = g.remoteGCM.Open(out[:0], nonce, out, additionalData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", errDecryptPacket, err)
|
||||
}
|
||||
return append(in[:recordlayer.HeaderSize], out...), nil
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
// Package clientcertificate provides all the support Client Certificate types
|
||||
package clientcertificate
|
||||
|
||||
// Type is used to communicate what
|
||||
// type of certificate is being transported
|
||||
//
|
||||
//https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-2
|
||||
type Type byte
|
||||
|
||||
// ClientCertificateType enums
|
||||
const (
|
||||
RSASign Type = 1
|
||||
ECDSASign Type = 64
|
||||
)
|
||||
|
||||
// Types returns all valid ClientCertificate Types
|
||||
func Types() map[Type]bool {
|
||||
return map[Type]bool{
|
||||
RSASign: true,
|
||||
ECDSASign: true,
|
||||
}
|
||||
}
|
99
dtls-2.0.9/pkg/crypto/elliptic/elliptic.go
Normal file
99
dtls-2.0.9/pkg/crypto/elliptic/elliptic.go
Normal file
@@ -0,0 +1,99 @@
|
||||
// Package elliptic provides elliptic curve cryptography for DTLS
|
||||
package elliptic
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
var errInvalidNamedCurve = errors.New("invalid named curve")
|
||||
|
||||
// CurvePointFormat is used to represent the IANA registered curve points
|
||||
//
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9
|
||||
type CurvePointFormat byte
|
||||
|
||||
// CurvePointFormat enums
|
||||
const (
|
||||
CurvePointFormatUncompressed CurvePointFormat = 0
|
||||
)
|
||||
|
||||
// Keypair is a Curve with a Private/Public Keypair
|
||||
type Keypair struct {
|
||||
Curve Curve
|
||||
PublicKey []byte
|
||||
PrivateKey []byte
|
||||
}
|
||||
|
||||
// CurveType is used to represent the IANA registered curve types for TLS
|
||||
//
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-10
|
||||
type CurveType byte
|
||||
|
||||
// CurveType enums
|
||||
const (
|
||||
CurveTypeNamedCurve CurveType = 0x03
|
||||
)
|
||||
|
||||
// CurveTypes returns all known curves
|
||||
func CurveTypes() map[CurveType]struct{} {
|
||||
return map[CurveType]struct{}{
|
||||
CurveTypeNamedCurve: {},
|
||||
}
|
||||
}
|
||||
|
||||
// Curve is used to represent the IANA registered curves for TLS
|
||||
//
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8
|
||||
type Curve uint16
|
||||
|
||||
// Curve enums
|
||||
const (
|
||||
P256 Curve = 0x0017
|
||||
P384 Curve = 0x0018
|
||||
X25519 Curve = 0x001d
|
||||
)
|
||||
|
||||
// Curves returns all curves we implement
|
||||
func Curves() map[Curve]bool {
|
||||
return map[Curve]bool{
|
||||
X25519: true,
|
||||
P256: true,
|
||||
P384: true,
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateKeypair generates a keypair for the given Curve
|
||||
func GenerateKeypair(c Curve) (*Keypair, error) {
|
||||
switch c { //nolint:golint
|
||||
case X25519:
|
||||
tmp := make([]byte, 32)
|
||||
if _, err := rand.Read(tmp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var public, private [32]byte
|
||||
copy(private[:], tmp)
|
||||
|
||||
curve25519.ScalarBaseMult(&public, &private)
|
||||
return &Keypair{X25519, public[:], private[:]}, nil
|
||||
case P256:
|
||||
return ellipticCurveKeypair(P256, elliptic.P256(), elliptic.P256())
|
||||
case P384:
|
||||
return ellipticCurveKeypair(P384, elliptic.P384(), elliptic.P384())
|
||||
default:
|
||||
return nil, errInvalidNamedCurve
|
||||
}
|
||||
}
|
||||
|
||||
func ellipticCurveKeypair(nc Curve, c1, c2 elliptic.Curve) (*Keypair, error) {
|
||||
privateKey, x, y, err := elliptic.GenerateKey(c1, rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Keypair{nc, elliptic.Marshal(c2, x, y), privateKey}, nil
|
||||
}
|
50
dtls-2.0.9/pkg/crypto/fingerprint/fingerprint.go
Normal file
50
dtls-2.0.9/pkg/crypto/fingerprint/fingerprint.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Package fingerprint provides a helper to create fingerprint string from certificate
|
||||
package fingerprint
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
errHashUnavailable = errors.New("fingerprint: hash algorithm is not linked into the binary")
|
||||
errInvalidFingerprintLength = errors.New("fingerprint: invalid fingerprint length")
|
||||
)
|
||||
|
||||
// Fingerprint creates a fingerprint for a certificate using the specified hash algorithm
|
||||
func Fingerprint(cert *x509.Certificate, algo crypto.Hash) (string, error) {
|
||||
if !algo.Available() {
|
||||
return "", errHashUnavailable
|
||||
}
|
||||
h := algo.New()
|
||||
for i := 0; i < len(cert.Raw); {
|
||||
n, _ := h.Write(cert.Raw[i:])
|
||||
// Hash.Writer is specified to be never returning an error.
|
||||
// https://golang.org/pkg/hash/#Hash
|
||||
i += n
|
||||
}
|
||||
digest := []byte(fmt.Sprintf("%x", h.Sum(nil)))
|
||||
|
||||
digestlen := len(digest)
|
||||
if digestlen == 0 {
|
||||
return "", nil
|
||||
}
|
||||
if digestlen%2 != 0 {
|
||||
return "", errInvalidFingerprintLength
|
||||
}
|
||||
res := make([]byte, digestlen>>1+digestlen-1)
|
||||
|
||||
pos := 0
|
||||
for i, c := range digest {
|
||||
res[pos] = c
|
||||
pos++
|
||||
if (i)%2 != 0 && i < digestlen-1 {
|
||||
res[pos] = byte(':')
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
return string(res), nil
|
||||
}
|
52
dtls-2.0.9/pkg/crypto/fingerprint/fingerprint_test.go
Normal file
52
dtls-2.0.9/pkg/crypto/fingerprint/fingerprint_test.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package fingerprint
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var errInvalidHashID = errors.New("invalid hash ID")
|
||||
|
||||
func TestFingerprint(t *testing.T) {
|
||||
rawCertificate := []byte{
|
||||
0x30, 0x82, 0x01, 0x98, 0x30, 0x82, 0x01, 0x3d, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x11, 0x00, 0xa9, 0x91, 0x76, 0x0a, 0xcd, 0x97, 0x4c, 0x36, 0xba,
|
||||
0xc9, 0xc2, 0x66, 0x91, 0x47, 0x6c, 0xac, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x2b, 0x31, 0x29, 0x30, 0x27,
|
||||
0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
|
||||
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x31, 0x31, 0x30, 0x30,
|
||||
0x39, 0x30, 0x34, 0x32, 0x33, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, 0x31, 0x30, 0x30, 0x39, 0x30, 0x34, 0x32, 0x33, 0x5a, 0x30, 0x2b, 0x31, 0x29,
|
||||
0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
|
||||
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48,
|
||||
0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x9c, 0x12, 0x8e, 0xb5, 0x21, 0x23, 0x9f,
|
||||
0x35, 0x5d, 0x39, 0x64, 0xc3, 0x75, 0x81, 0xa4, 0xc8, 0xc8, 0x08, 0x8a, 0xa8, 0x42, 0x30, 0x30, 0x65, 0xb8, 0xb1, 0x3e, 0x4a, 0x51, 0x86, 0xeb, 0xad,
|
||||
0x03, 0x02, 0x35, 0x83, 0xc4, 0x19, 0x3a, 0x5b, 0x79, 0x83, 0xec, 0x59, 0x0e, 0x4f, 0x99, 0xb1, 0xd2, 0xf0, 0x50, 0xfa, 0xb8, 0x5f, 0xfc, 0x88, 0xf3,
|
||||
0x15, 0xed, 0xb8, 0x14, 0xf0, 0xba, 0xcd, 0xa3, 0x42, 0x30, 0x40, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
|
||||
0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08,
|
||||
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff,
|
||||
0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x49, 0x00, 0x30, 0x46, 0x02, 0x21, 0x00, 0xcd, 0x44, 0xb1, 0xf2, 0x09,
|
||||
0xe5, 0xf1, 0xf4, 0xc9, 0x26, 0x95, 0x9a, 0x2d, 0x6d, 0xf3, 0x0c, 0xb8, 0xeb, 0x27, 0x2d, 0x81, 0x19, 0xe9, 0x51, 0xf7, 0xad, 0x64, 0x7d, 0x42, 0x32,
|
||||
0x9e, 0xf8, 0x02, 0x21, 0x00, 0xee, 0xad, 0x96, 0x41, 0xf1, 0x12, 0xd0, 0x6b, 0xcd, 0x09, 0xf0, 0x3c, 0x67, 0xb3, 0xdd, 0xed, 0x0a, 0xf1, 0xd8, 0x41,
|
||||
0x4f, 0x61, 0xfd, 0x53, 0x1d, 0xf5, 0x27, 0xbe, 0x6d, 0x0b, 0xe2, 0x0d,
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(rawCertificate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
const expectedSHA256 = "60:ef:f5:79:ad:8d:3e:d7:e8:4d:5a:5a:d6:1e:71:2d:47:52:a5:cb:df:34:37:87:10:a5:4e:d7:2a:2c:37:34"
|
||||
actualSHA256, err := Fingerprint(cert, crypto.SHA256)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if actualSHA256 != expectedSHA256 {
|
||||
t.Fatalf("Fingerprint SHA256 mismatch expected(%s) actual(%s)", expectedSHA256, actualSHA256)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFingerprint_UnavailableHash(t *testing.T) {
|
||||
_, err := Fingerprint(&x509.Certificate{}, crypto.Hash(0xFFFFFFFF))
|
||||
if !errors.Is(err, errHashUnavailable) {
|
||||
t.Errorf("%w: Expected error '%v' for invalid hash ID, got '%v'", errInvalidHashID, errHashUnavailable, err)
|
||||
}
|
||||
}
|
37
dtls-2.0.9/pkg/crypto/fingerprint/hash.go
Normal file
37
dtls-2.0.9/pkg/crypto/fingerprint/hash.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package fingerprint
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var errInvalidHashAlgorithm = errors.New("fingerprint: invalid hash algorithm")
|
||||
|
||||
func nameToHash() map[string]crypto.Hash {
|
||||
return map[string]crypto.Hash{
|
||||
"md5": crypto.MD5, // [RFC3279]
|
||||
"sha-1": crypto.SHA1, // [RFC3279]
|
||||
"sha-224": crypto.SHA224, // [RFC4055]
|
||||
"sha-256": crypto.SHA256, // [RFC4055]
|
||||
"sha-384": crypto.SHA384, // [RFC4055]
|
||||
"sha-512": crypto.SHA512, // [RFC4055]
|
||||
}
|
||||
}
|
||||
|
||||
// HashFromString allows looking up a hash algorithm by it's string representation
|
||||
func HashFromString(s string) (crypto.Hash, error) {
|
||||
if h, ok := nameToHash()[s]; ok {
|
||||
return h, nil
|
||||
}
|
||||
return 0, errInvalidHashAlgorithm
|
||||
}
|
||||
|
||||
// StringFromHash allows looking up a string representation of the crypto.Hash.
|
||||
func StringFromHash(hash crypto.Hash) (string, error) {
|
||||
for s, h := range nameToHash() {
|
||||
if h == hash {
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
return "", errInvalidHashAlgorithm
|
||||
}
|
41
dtls-2.0.9/pkg/crypto/fingerprint/hash_test.go
Normal file
41
dtls-2.0.9/pkg/crypto/fingerprint/hash_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package fingerprint
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHashFromString(t *testing.T) {
|
||||
t.Run("InvalidHashAlgorithm", func(t *testing.T) {
|
||||
_, err := HashFromString("invalid-hash-algorithm")
|
||||
if !errors.Is(err, errInvalidHashAlgorithm) {
|
||||
t.Errorf("Expected error '%v' for invalid hash name, got '%v'", errInvalidHashAlgorithm, err)
|
||||
}
|
||||
})
|
||||
t.Run("ValidHashAlgorithm", func(t *testing.T) {
|
||||
h, err := HashFromString("sha-512")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error for valid hash name, got '%v'", err)
|
||||
}
|
||||
if h != crypto.SHA512 {
|
||||
t.Errorf("Expected hash ID of %d, got %d", int(crypto.SHA512), int(h))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringFromHash_Roundtrip(t *testing.T) {
|
||||
for _, h := range nameToHash() {
|
||||
s, err := StringFromHash(h)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error for valid hash algorithm, got '%v'", err)
|
||||
}
|
||||
h2, err := HashFromString(s)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error for valid hash name, got '%v'", err)
|
||||
}
|
||||
if h != h2 {
|
||||
t.Errorf("Hash value doesn't match, expected: 0x%x, got 0x%x", h, h2)
|
||||
}
|
||||
}
|
||||
}
|
126
dtls-2.0.9/pkg/crypto/hash/hash.go
Normal file
126
dtls-2.0.9/pkg/crypto/hash/hash.go
Normal file
@@ -0,0 +1,126 @@
|
||||
// Package hash provides TLS HashAlgorithm as defined in TLS 1.2
|
||||
package hash
|
||||
|
||||
import ( //nolint:gci
|
||||
"crypto"
|
||||
"crypto/md5" //nolint:gosec
|
||||
"crypto/sha1" //nolint:gosec
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
)
|
||||
|
||||
// Algorithm is used to indicate the hash algorithm used
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18
|
||||
type Algorithm uint16
|
||||
|
||||
// Supported hash algorithms
|
||||
const (
|
||||
None Algorithm = 0 // Blacklisted
|
||||
MD5 Algorithm = 1 // Blacklisted
|
||||
SHA1 Algorithm = 2 // Blacklisted
|
||||
SHA224 Algorithm = 3
|
||||
SHA256 Algorithm = 4
|
||||
SHA384 Algorithm = 5
|
||||
SHA512 Algorithm = 6
|
||||
Ed25519 Algorithm = 8
|
||||
)
|
||||
|
||||
// String makes hashAlgorithm printable
|
||||
func (a Algorithm) String() string {
|
||||
switch a {
|
||||
case None:
|
||||
return "none"
|
||||
case MD5:
|
||||
return "md5" // [RFC3279]
|
||||
case SHA1:
|
||||
return "sha-1" // [RFC3279]
|
||||
case SHA224:
|
||||
return "sha-224" // [RFC4055]
|
||||
case SHA256:
|
||||
return "sha-256" // [RFC4055]
|
||||
case SHA384:
|
||||
return "sha-384" // [RFC4055]
|
||||
case SHA512:
|
||||
return "sha-512" // [RFC4055]
|
||||
case Ed25519:
|
||||
return "null"
|
||||
default:
|
||||
return "unknown or unsupported hash algorithm"
|
||||
}
|
||||
}
|
||||
|
||||
// Digest performs a digest on the passed value
|
||||
func (a Algorithm) Digest(b []byte) []byte {
|
||||
switch a {
|
||||
case None:
|
||||
return nil
|
||||
case MD5:
|
||||
hash := md5.Sum(b) // #nosec
|
||||
return hash[:]
|
||||
case SHA1:
|
||||
hash := sha1.Sum(b) // #nosec
|
||||
return hash[:]
|
||||
case SHA224:
|
||||
hash := sha256.Sum224(b)
|
||||
return hash[:]
|
||||
case SHA256:
|
||||
hash := sha256.Sum256(b)
|
||||
return hash[:]
|
||||
case SHA384:
|
||||
hash := sha512.Sum384(b)
|
||||
return hash[:]
|
||||
case SHA512:
|
||||
hash := sha512.Sum512(b)
|
||||
return hash[:]
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Insecure returns if the given HashAlgorithm is considered secure in DTLS 1.2
|
||||
func (a Algorithm) Insecure() bool {
|
||||
switch a {
|
||||
case None, MD5, SHA1:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// CryptoHash returns the crypto.Hash implementation for the given HashAlgorithm
|
||||
func (a Algorithm) CryptoHash() crypto.Hash {
|
||||
switch a {
|
||||
case None:
|
||||
return crypto.Hash(0)
|
||||
case MD5:
|
||||
return crypto.MD5
|
||||
case SHA1:
|
||||
return crypto.SHA1
|
||||
case SHA224:
|
||||
return crypto.SHA224
|
||||
case SHA256:
|
||||
return crypto.SHA256
|
||||
case SHA384:
|
||||
return crypto.SHA384
|
||||
case SHA512:
|
||||
return crypto.SHA512
|
||||
case Ed25519:
|
||||
return crypto.Hash(0)
|
||||
default:
|
||||
return crypto.Hash(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Algorithms returns all the supported Hash Algorithms
|
||||
func Algorithms() map[Algorithm]struct{} {
|
||||
return map[Algorithm]struct{}{
|
||||
None: {},
|
||||
MD5: {},
|
||||
SHA1: {},
|
||||
SHA224: {},
|
||||
SHA256: {},
|
||||
SHA384: {},
|
||||
SHA512: {},
|
||||
Ed25519: {},
|
||||
}
|
||||
}
|
25
dtls-2.0.9/pkg/crypto/hash/hash_test.go
Normal file
25
dtls-2.0.9/pkg/crypto/hash/hash_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package hash
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/crypto/fingerprint"
|
||||
)
|
||||
|
||||
func TestHashAlgorithm_StringRoundtrip(t *testing.T) {
|
||||
for algo := range Algorithms() {
|
||||
if algo == Ed25519 || algo == None {
|
||||
continue
|
||||
}
|
||||
|
||||
str := algo.String()
|
||||
hash1 := algo.CryptoHash()
|
||||
hash2, err := fingerprint.HashFromString(str)
|
||||
if err != nil {
|
||||
t.Fatalf("fingerprint.HashFromString failed: %v", err)
|
||||
}
|
||||
if hash1 != hash2 {
|
||||
t.Errorf("Hash algorithm mismatch, input: %d, after roundtrip: %d", int(hash1), int(hash2))
|
||||
}
|
||||
}
|
||||
}
|
224
dtls-2.0.9/pkg/crypto/prf/prf.go
Normal file
224
dtls-2.0.9/pkg/crypto/prf/prf.go
Normal file
@@ -0,0 +1,224 @@
|
||||
// Package prf implements TLS 1.2 Pseudorandom functions
|
||||
package prf
|
||||
|
||||
import ( //nolint:gci
|
||||
ellipticStdlib "crypto/elliptic"
|
||||
"crypto/hmac"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"math"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/crypto/elliptic"
|
||||
"github.com/pion/dtls/v2/pkg/protocol"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
const (
|
||||
masterSecretLabel = "master secret"
|
||||
extendedMasterSecretLabel = "extended master secret"
|
||||
keyExpansionLabel = "key expansion"
|
||||
verifyDataClientLabel = "client finished"
|
||||
verifyDataServerLabel = "server finished"
|
||||
)
|
||||
|
||||
// HashFunc allows callers to decide what hash is used in PRF
|
||||
type HashFunc func() hash.Hash
|
||||
|
||||
// EncryptionKeys is all the state needed for a TLS CipherSuite
|
||||
type EncryptionKeys struct {
|
||||
MasterSecret []byte
|
||||
ClientMACKey []byte
|
||||
ServerMACKey []byte
|
||||
ClientWriteKey []byte
|
||||
ServerWriteKey []byte
|
||||
ClientWriteIV []byte
|
||||
ServerWriteIV []byte
|
||||
}
|
||||
|
||||
var errInvalidNamedCurve = &protocol.FatalError{Err: errors.New("invalid named curve")} //nolint:goerr113
|
||||
|
||||
func (e *EncryptionKeys) String() string {
|
||||
return fmt.Sprintf(`encryptionKeys:
|
||||
- masterSecret: %#v
|
||||
- clientMACKey: %#v
|
||||
- serverMACKey: %#v
|
||||
- clientWriteKey: %#v
|
||||
- serverWriteKey: %#v
|
||||
- clientWriteIV: %#v
|
||||
- serverWriteIV: %#v
|
||||
`,
|
||||
e.MasterSecret,
|
||||
e.ClientMACKey,
|
||||
e.ServerMACKey,
|
||||
e.ClientWriteKey,
|
||||
e.ServerWriteKey,
|
||||
e.ClientWriteIV,
|
||||
e.ServerWriteIV)
|
||||
}
|
||||
|
||||
// PSKPreMasterSecret generates the PSK Premaster Secret
|
||||
// The premaster secret is formed as follows: if the PSK is N octets
|
||||
// long, concatenate a uint16 with the value N, N zero octets, a second
|
||||
// uint16 with the value N, and the PSK itself.
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc4279#section-2
|
||||
func PSKPreMasterSecret(psk []byte) []byte {
|
||||
pskLen := uint16(len(psk))
|
||||
|
||||
out := append(make([]byte, 2+pskLen+2), psk...)
|
||||
binary.BigEndian.PutUint16(out, pskLen)
|
||||
binary.BigEndian.PutUint16(out[2+pskLen:], pskLen)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// PreMasterSecret implements TLS 1.2 Premaster Secret generation given a keypair and a curve
|
||||
func PreMasterSecret(publicKey, privateKey []byte, curve elliptic.Curve) ([]byte, error) {
|
||||
switch curve {
|
||||
case elliptic.X25519:
|
||||
return curve25519.X25519(privateKey, publicKey)
|
||||
case elliptic.P256:
|
||||
return ellipticCurvePreMasterSecret(publicKey, privateKey, ellipticStdlib.P256(), ellipticStdlib.P256())
|
||||
case elliptic.P384:
|
||||
return ellipticCurvePreMasterSecret(publicKey, privateKey, ellipticStdlib.P384(), ellipticStdlib.P384())
|
||||
default:
|
||||
return nil, errInvalidNamedCurve
|
||||
}
|
||||
}
|
||||
|
||||
func ellipticCurvePreMasterSecret(publicKey, privateKey []byte, c1, c2 ellipticStdlib.Curve) ([]byte, error) {
|
||||
x, y := ellipticStdlib.Unmarshal(c1, publicKey)
|
||||
if x == nil || y == nil {
|
||||
return nil, errInvalidNamedCurve
|
||||
}
|
||||
|
||||
result, _ := c2.ScalarMult(x, y, privateKey)
|
||||
preMasterSecret := make([]byte, (c2.Params().BitSize+7)>>3)
|
||||
resultBytes := result.Bytes()
|
||||
copy(preMasterSecret[len(preMasterSecret)-len(resultBytes):], resultBytes)
|
||||
return preMasterSecret, nil
|
||||
}
|
||||
|
||||
// PHash is PRF is the SHA-256 hash function is used for all cipher suites
|
||||
// defined in this TLS 1.2 document and in TLS documents published prior to this
|
||||
// document when TLS 1.2 is negotiated. New cipher suites MUST explicitly
|
||||
// specify a PRF and, in general, SHOULD use the TLS PRF with SHA-256 or a
|
||||
// stronger standard hash function.
|
||||
//
|
||||
// P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
|
||||
// HMAC_hash(secret, A(2) + seed) +
|
||||
// HMAC_hash(secret, A(3) + seed) + ...
|
||||
//
|
||||
// A() is defined as:
|
||||
//
|
||||
// A(0) = seed
|
||||
// A(i) = HMAC_hash(secret, A(i-1))
|
||||
//
|
||||
// P_hash can be iterated as many times as necessary to produce the
|
||||
// required quantity of data. For example, if P_SHA256 is being used to
|
||||
// create 80 bytes of data, it will have to be iterated three times
|
||||
// (through A(3)), creating 96 bytes of output data; the last 16 bytes
|
||||
// of the final iteration will then be discarded, leaving 80 bytes of
|
||||
// output data.
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc4346w
|
||||
func PHash(secret, seed []byte, requestedLength int, h HashFunc) ([]byte, error) {
|
||||
hmacSHA256 := func(key, data []byte) ([]byte, error) {
|
||||
mac := hmac.New(h, key)
|
||||
if _, err := mac.Write(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mac.Sum(nil), nil
|
||||
}
|
||||
|
||||
var err error
|
||||
lastRound := seed
|
||||
out := []byte{}
|
||||
|
||||
iterations := int(math.Ceil(float64(requestedLength) / float64(h().Size())))
|
||||
for i := 0; i < iterations; i++ {
|
||||
lastRound, err = hmacSHA256(secret, lastRound)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withSecret, err := hmacSHA256(secret, append(lastRound, seed...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, withSecret...)
|
||||
}
|
||||
|
||||
return out[:requestedLength], nil
|
||||
}
|
||||
|
||||
// ExtendedMasterSecret generates a Extended MasterSecret as defined in
|
||||
// https://tools.ietf.org/html/rfc7627
|
||||
func ExtendedMasterSecret(preMasterSecret, sessionHash []byte, h HashFunc) ([]byte, error) {
|
||||
seed := append([]byte(extendedMasterSecretLabel), sessionHash...)
|
||||
return PHash(preMasterSecret, seed, 48, h)
|
||||
}
|
||||
|
||||
// MasterSecret generates a TLS 1.2 MasterSecret
|
||||
func MasterSecret(preMasterSecret, clientRandom, serverRandom []byte, h HashFunc) ([]byte, error) {
|
||||
seed := append(append([]byte(masterSecretLabel), clientRandom...), serverRandom...)
|
||||
return PHash(preMasterSecret, seed, 48, h)
|
||||
}
|
||||
|
||||
// GenerateEncryptionKeys is the final step TLS 1.2 PRF. Given all state generated so far generates
|
||||
// the final keys need for encryption
|
||||
func GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int, h HashFunc) (*EncryptionKeys, error) {
|
||||
seed := append(append([]byte(keyExpansionLabel), serverRandom...), clientRandom...)
|
||||
keyMaterial, err := PHash(masterSecret, seed, (2*macLen)+(2*keyLen)+(2*ivLen), h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientMACKey := keyMaterial[:macLen]
|
||||
keyMaterial = keyMaterial[macLen:]
|
||||
|
||||
serverMACKey := keyMaterial[:macLen]
|
||||
keyMaterial = keyMaterial[macLen:]
|
||||
|
||||
clientWriteKey := keyMaterial[:keyLen]
|
||||
keyMaterial = keyMaterial[keyLen:]
|
||||
|
||||
serverWriteKey := keyMaterial[:keyLen]
|
||||
keyMaterial = keyMaterial[keyLen:]
|
||||
|
||||
clientWriteIV := keyMaterial[:ivLen]
|
||||
keyMaterial = keyMaterial[ivLen:]
|
||||
|
||||
serverWriteIV := keyMaterial[:ivLen]
|
||||
|
||||
return &EncryptionKeys{
|
||||
MasterSecret: masterSecret,
|
||||
ClientMACKey: clientMACKey,
|
||||
ServerMACKey: serverMACKey,
|
||||
ClientWriteKey: clientWriteKey,
|
||||
ServerWriteKey: serverWriteKey,
|
||||
ClientWriteIV: clientWriteIV,
|
||||
ServerWriteIV: serverWriteIV,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func prfVerifyData(masterSecret, handshakeBodies []byte, label string, hashFunc HashFunc) ([]byte, error) {
|
||||
h := hashFunc()
|
||||
if _, err := h.Write(handshakeBodies); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seed := append([]byte(label), h.Sum(nil)...)
|
||||
return PHash(masterSecret, seed, 12, hashFunc)
|
||||
}
|
||||
|
||||
// VerifyDataClient is caled on the Client Side to either verify or generate the VerifyData message
|
||||
func VerifyDataClient(masterSecret, handshakeBodies []byte, h HashFunc) ([]byte, error) {
|
||||
return prfVerifyData(masterSecret, handshakeBodies, verifyDataClientLabel, h)
|
||||
}
|
||||
|
||||
// VerifyDataServer is caled on the Server Side to either verify or generate the VerifyData message
|
||||
func VerifyDataServer(masterSecret, handshakeBodies []byte, h HashFunc) ([]byte, error) {
|
||||
return prfVerifyData(masterSecret, handshakeBodies, verifyDataServerLabel, h)
|
||||
}
|
80
dtls-2.0.9/pkg/crypto/prf/prf_test.go
Normal file
80
dtls-2.0.9/pkg/crypto/prf/prf_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package prf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/crypto/elliptic"
|
||||
)
|
||||
|
||||
func TestPreMasterSecret(t *testing.T) {
|
||||
privateKey := []byte{0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f}
|
||||
publicKey := []byte{0x9f, 0xd7, 0xad, 0x6d, 0xcf, 0xf4, 0x29, 0x8d, 0xd3, 0xf9, 0x6d, 0x5b, 0x1b, 0x2a, 0xf9, 0x10, 0xa0, 0x53, 0x5b, 0x14, 0x88, 0xd7, 0xf8, 0xfa, 0xbb, 0x34, 0x9a, 0x98, 0x28, 0x80, 0xb6, 0x15}
|
||||
expectedPreMasterSecret := []byte{0xdf, 0x4a, 0x29, 0x1b, 0xaa, 0x1e, 0xb7, 0xcf, 0xa6, 0x93, 0x4b, 0x29, 0xb4, 0x74, 0xba, 0xad, 0x26, 0x97, 0xe2, 0x9f, 0x1f, 0x92, 0x0d, 0xcc, 0x77, 0xc8, 0xa0, 0xa0, 0x88, 0x44, 0x76, 0x24}
|
||||
|
||||
preMasterSecret, err := PreMasterSecret(publicKey, privateKey, elliptic.X25519)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !bytes.Equal(expectedPreMasterSecret, preMasterSecret) {
|
||||
t.Fatalf("PremasterSecret exp: % 02x actual: % 02x", expectedPreMasterSecret, preMasterSecret)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMasterSecret(t *testing.T) {
|
||||
preMasterSecret := []byte{0xdf, 0x4a, 0x29, 0x1b, 0xaa, 0x1e, 0xb7, 0xcf, 0xa6, 0x93, 0x4b, 0x29, 0xb4, 0x74, 0xba, 0xad, 0x26, 0x97, 0xe2, 0x9f, 0x1f, 0x92, 0x0d, 0xcc, 0x77, 0xc8, 0xa0, 0xa0, 0x88, 0x44, 0x76, 0x24}
|
||||
clientRandom := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f}
|
||||
serverRandom := []byte{0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f}
|
||||
expectedMasterSecret := []byte{0x91, 0x6a, 0xbf, 0x9d, 0xa5, 0x59, 0x73, 0xe1, 0x36, 0x14, 0xae, 0x0a, 0x3f, 0x5d, 0x3f, 0x37, 0xb0, 0x23, 0xba, 0x12, 0x9a, 0xee, 0x02, 0xcc, 0x91, 0x34, 0x33, 0x81, 0x27, 0xcd, 0x70, 0x49, 0x78, 0x1c, 0x8e, 0x19, 0xfc, 0x1e, 0xb2, 0xa7, 0x38, 0x7a, 0xc0, 0x6a, 0xe2, 0x37, 0x34, 0x4c}
|
||||
|
||||
masterSecret, err := MasterSecret(preMasterSecret, clientRandom, serverRandom, sha256.New)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !bytes.Equal(expectedMasterSecret, masterSecret) {
|
||||
t.Fatalf("masterSecret exp: % 02x actual: % 02x", expectedMasterSecret, masterSecret)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryptionKeys(t *testing.T) {
|
||||
clientRandom := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f}
|
||||
serverRandom := []byte{0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f}
|
||||
masterSecret := []byte{0x91, 0x6a, 0xbf, 0x9d, 0xa5, 0x59, 0x73, 0xe1, 0x36, 0x14, 0xae, 0x0a, 0x3f, 0x5d, 0x3f, 0x37, 0xb0, 0x23, 0xba, 0x12, 0x9a, 0xee, 0x02, 0xcc, 0x91, 0x34, 0x33, 0x81, 0x27, 0xcd, 0x70, 0x49, 0x78, 0x1c, 0x8e, 0x19, 0xfc, 0x1e, 0xb2, 0xa7, 0x38, 0x7a, 0xc0, 0x6a, 0xe2, 0x37, 0x34, 0x4c}
|
||||
|
||||
expectedEncryptionKeys := &EncryptionKeys{
|
||||
MasterSecret: masterSecret,
|
||||
ClientMACKey: []byte{},
|
||||
ServerMACKey: []byte{},
|
||||
ClientWriteKey: []byte{0x1b, 0x7d, 0x11, 0x7c, 0x7d, 0x5f, 0x69, 0x0b, 0xc2, 0x63, 0xca, 0xe8, 0xef, 0x60, 0xaf, 0x0f},
|
||||
ServerWriteKey: []byte{0x18, 0x78, 0xac, 0xc2, 0x2a, 0xd8, 0xbd, 0xd8, 0xc6, 0x01, 0xa6, 0x17, 0x12, 0x6f, 0x63, 0x54},
|
||||
ClientWriteIV: []byte{0x0e, 0xb2, 0x09, 0x06},
|
||||
ServerWriteIV: []byte{0xf7, 0x81, 0xfa, 0xd2},
|
||||
}
|
||||
keys, err := GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, 0, 16, 4, sha256.New)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !reflect.DeepEqual(expectedEncryptionKeys, keys) {
|
||||
t.Fatalf("masterSecret exp: %q actual: %q", expectedEncryptionKeys, keys)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyData(t *testing.T) {
|
||||
clientHello := []byte{0x01, 0x00, 0x00, 0xa1, 0x03, 0x03, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x00, 0x20, 0xcc, 0xa8, 0xcc, 0xa9, 0xc0, 0x2f, 0xc0, 0x30, 0xc0, 0x2b, 0xc0, 0x2c, 0xc0, 0x13, 0xc0, 0x09, 0xc0, 0x14, 0xc0, 0x0a, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0xc0, 0x12, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x18, 0x00, 0x16, 0x00, 0x00, 0x13, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x75, 0x6c, 0x66, 0x68, 0x65, 0x69, 0x6d, 0x2e, 0x6e, 0x65, 0x74, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x12, 0x00, 0x10, 0x04, 0x01, 0x04, 0x03, 0x05, 0x01, 0x05, 0x03, 0x06, 0x01, 0x06, 0x03, 0x02, 0x01, 0x02, 0x03, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00, 0x00}
|
||||
serverHello := []byte{0x02, 0x00, 0x00, 0x2d, 0x03, 0x03, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x00, 0xc0, 0x13, 0x00, 0x00, 0x05, 0xff, 0x01, 0x00, 0x01, 0x00}
|
||||
serverCertificate := []byte{0x0b, 0x00, 0x03, 0x2b, 0x00, 0x03, 0x28, 0x00, 0x03, 0x25, 0x30, 0x82, 0x03, 0x21, 0x30, 0x82, 0x02, 0x09, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x15, 0x5a, 0x92, 0xad, 0xc2, 0x04, 0x8f, 0x90, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x22, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x31, 0x30, 0x30, 0x35, 0x30, 0x31, 0x33, 0x38, 0x31, 0x37, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x30, 0x30, 0x35, 0x30, 0x31, 0x33, 0x38, 0x31, 0x37, 0x5a, 0x30, 0x2b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x13, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x75, 0x6c, 0x66, 0x68, 0x65, 0x69, 0x6d, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc4, 0x80, 0x36, 0x06, 0xba, 0xe7, 0x47, 0x6b, 0x08, 0x94, 0x04, 0xec, 0xa7, 0xb6, 0x91, 0x04, 0x3f, 0xf7, 0x92, 0xbc, 0x19, 0xee, 0xfb, 0x7d, 0x74, 0xd7, 0xa8, 0x0d, 0x00, 0x1e, 0x7b, 0x4b, 0x3a, 0x4a, 0xe6, 0x0f, 0xe8, 0xc0, 0x71, 0xfc, 0x73, 0xe7, 0x02, 0x4c, 0x0d, 0xbc, 0xf4, 0xbd, 0xd1, 0x1d, 0x39, 0x6b, 0xba, 0x70, 0x46, 0x4a, 0x13, 0xe9, 0x4a, 0xf8, 0x3d, 0xf3, 0xe1, 0x09, 0x59, 0x54, 0x7b, 0xc9, 0x55, 0xfb, 0x41, 0x2d, 0xa3, 0x76, 0x52, 0x11, 0xe1, 0xf3, 0xdc, 0x77, 0x6c, 0xaa, 0x53, 0x37, 0x6e, 0xca, 0x3a, 0xec, 0xbe, 0xc3, 0xaa, 0xb7, 0x3b, 0x31, 0xd5, 0x6c, 0xb6, 0x52, 0x9c, 0x80, 0x98, 0xbc, 0xc9, 0xe0, 0x28, 0x18, 0xe2, 0x0b, 0xf7, 0xf8, 0xa0, 0x3a, 0xfd, 0x17, 0x04, 0x50, 0x9e, 0xce, 0x79, 0xbd, 0x9f, 0x39, 0xf1, 0xea, 0x69, 0xec, 0x47, 0x97, 0x2e, 0x83, 0x0f, 0xb5, 0xca, 0x95, 0xde, 0x95, 0xa1, 0xe6, 0x04, 0x22, 0xd5, 0xee, 0xbe, 0x52, 0x79, 0x54, 0xa1, 0xe7, 0xbf, 0x8a, 0x86, 0xf6, 0x46, 0x6d, 0x0d, 0x9f, 0x16, 0x95, 0x1a, 0x4c, 0xf7, 0xa0, 0x46, 0x92, 0x59, 0x5c, 0x13, 0x52, 0xf2, 0x54, 0x9e, 0x5a, 0xfb, 0x4e, 0xbf, 0xd7, 0x7a, 0x37, 0x95, 0x01, 0x44, 0xe4, 0xc0, 0x26, 0x87, 0x4c, 0x65, 0x3e, 0x40, 0x7d, 0x7d, 0x23, 0x07, 0x44, 0x01, 0xf4, 0x84, 0xff, 0xd0, 0x8f, 0x7a, 0x1f, 0xa0, 0x52, 0x10, 0xd1, 0xf4, 0xf0, 0xd5, 0xce, 0x79, 0x70, 0x29, 0x32, 0xe2, 0xca, 0xbe, 0x70, 0x1f, 0xdf, 0xad, 0x6b, 0x4b, 0xb7, 0x11, 0x01, 0xf4, 0x4b, 0xad, 0x66, 0x6a, 0x11, 0x13, 0x0f, 0xe2, 0xee, 0x82, 0x9e, 0x4d, 0x02, 0x9d, 0xc9, 0x1c, 0xdd, 0x67, 0x16, 0xdb, 0xb9, 0x06, 0x18, 0x86, 0xed, 0xc1, 0xba, 0x94, 0x21, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x52, 0x30, 0x50, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x89, 0x4f, 0xde, 0x5b, 0xcc, 0x69, 0xe2, 0x52, 0xcf, 0x3e, 0xa3, 0x00, 0xdf, 0xb1, 0x97, 0xb8, 0x1d, 0xe1, 0xc1, 0x46, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x59, 0x16, 0x45, 0xa6, 0x9a, 0x2e, 0x37, 0x79, 0xe4, 0xf6, 0xdd, 0x27, 0x1a, 0xba, 0x1c, 0x0b, 0xfd, 0x6c, 0xd7, 0x55, 0x99, 0xb5, 0xe7, 0xc3, 0x6e, 0x53, 0x3e, 0xff, 0x36, 0x59, 0x08, 0x43, 0x24, 0xc9, 0xe7, 0xa5, 0x04, 0x07, 0x9d, 0x39, 0xe0, 0xd4, 0x29, 0x87, 0xff, 0xe3, 0xeb, 0xdd, 0x09, 0xc1, 0xcf, 0x1d, 0x91, 0x44, 0x55, 0x87, 0x0b, 0x57, 0x1d, 0xd1, 0x9b, 0xdf, 0x1d, 0x24, 0xf8, 0xbb, 0x9a, 0x11, 0xfe, 0x80, 0xfd, 0x59, 0x2b, 0xa0, 0x39, 0x8c, 0xde, 0x11, 0xe2, 0x65, 0x1e, 0x61, 0x8c, 0xe5, 0x98, 0xfa, 0x96, 0xe5, 0x37, 0x2e, 0xef, 0x3d, 0x24, 0x8a, 0xfd, 0xe1, 0x74, 0x63, 0xeb, 0xbf, 0xab, 0xb8, 0xe4, 0xd1, 0xab, 0x50, 0x2a, 0x54, 0xec, 0x00, 0x64, 0xe9, 0x2f, 0x78, 0x19, 0x66, 0x0d, 0x3f, 0x27, 0xcf, 0x20, 0x9e, 0x66, 0x7f, 0xce, 0x5a, 0xe2, 0xe4, 0xac, 0x99, 0xc7, 0xc9, 0x38, 0x18, 0xf8, 0xb2, 0x51, 0x07, 0x22, 0xdf, 0xed, 0x97, 0xf3, 0x2e, 0x3e, 0x93, 0x49, 0xd4, 0xc6, 0x6c, 0x9e, 0xa6, 0x39, 0x6d, 0x74, 0x44, 0x62, 0xa0, 0x6b, 0x42, 0xc6, 0xd5, 0xba, 0x68, 0x8e, 0xac, 0x3a, 0x01, 0x7b, 0xdd, 0xfc, 0x8e, 0x2c, 0xfc, 0xad, 0x27, 0xcb, 0x69, 0xd3, 0xcc, 0xdc, 0xa2, 0x80, 0x41, 0x44, 0x65, 0xd3, 0xae, 0x34, 0x8c, 0xe0, 0xf3, 0x4a, 0xb2, 0xfb, 0x9c, 0x61, 0x83, 0x71, 0x31, 0x2b, 0x19, 0x10, 0x41, 0x64, 0x1c, 0x23, 0x7f, 0x11, 0xa5, 0xd6, 0x5c, 0x84, 0x4f, 0x04, 0x04, 0x84, 0x99, 0x38, 0x71, 0x2b, 0x95, 0x9e, 0xd6, 0x85, 0xbc, 0x5c, 0x5d, 0xd6, 0x45, 0xed, 0x19, 0x90, 0x94, 0x73, 0x40, 0x29, 0x26, 0xdc, 0xb4, 0x0e, 0x34, 0x69, 0xa1, 0x59, 0x41, 0xe8, 0xe2, 0xcc, 0xa8, 0x4b, 0xb6, 0x08, 0x46, 0x36, 0xa0}
|
||||
serverKeyExchange := []byte{0x0c, 0x00, 0x01, 0x28, 0x03, 0x00, 0x1d, 0x20, 0x9f, 0xd7, 0xad, 0x6d, 0xcf, 0xf4, 0x29, 0x8d, 0xd3, 0xf9, 0x6d, 0x5b, 0x1b, 0x2a, 0xf9, 0x10, 0xa0, 0x53, 0x5b, 0x14, 0x88, 0xd7, 0xf8, 0xfa, 0xbb, 0x34, 0x9a, 0x98, 0x28, 0x80, 0xb6, 0x15, 0x04, 0x01, 0x01, 0x00, 0x04, 0x02, 0xb6, 0x61, 0xf7, 0xc1, 0x91, 0xee, 0x59, 0xbe, 0x45, 0x37, 0x66, 0x39, 0xbd, 0xc3, 0xd4, 0xbb, 0x81, 0xe1, 0x15, 0xca, 0x73, 0xc8, 0x34, 0x8b, 0x52, 0x5b, 0x0d, 0x23, 0x38, 0xaa, 0x14, 0x46, 0x67, 0xed, 0x94, 0x31, 0x02, 0x14, 0x12, 0xcd, 0x9b, 0x84, 0x4c, 0xba, 0x29, 0x93, 0x4a, 0xaa, 0xcc, 0xe8, 0x73, 0x41, 0x4e, 0xc1, 0x1c, 0xb0, 0x2e, 0x27, 0x2d, 0x0a, 0xd8, 0x1f, 0x76, 0x7d, 0x33, 0x07, 0x67, 0x21, 0xf1, 0x3b, 0xf3, 0x60, 0x20, 0xcf, 0x0b, 0x1f, 0xd0, 0xec, 0xb0, 0x78, 0xde, 0x11, 0x28, 0xbe, 0xba, 0x09, 0x49, 0xeb, 0xec, 0xe1, 0xa1, 0xf9, 0x6e, 0x20, 0x9d, 0xc3, 0x6e, 0x4f, 0xff, 0xd3, 0x6b, 0x67, 0x3a, 0x7d, 0xdc, 0x15, 0x97, 0xad, 0x44, 0x08, 0xe4, 0x85, 0xc4, 0xad, 0xb2, 0xc8, 0x73, 0x84, 0x12, 0x49, 0x37, 0x25, 0x23, 0x80, 0x9e, 0x43, 0x12, 0xd0, 0xc7, 0xb3, 0x52, 0x2e, 0xf9, 0x83, 0xca, 0xc1, 0xe0, 0x39, 0x35, 0xff, 0x13, 0xa8, 0xe9, 0x6b, 0xa6, 0x81, 0xa6, 0x2e, 0x40, 0xd3, 0xe7, 0x0a, 0x7f, 0xf3, 0x58, 0x66, 0xd3, 0xd9, 0x99, 0x3f, 0x9e, 0x26, 0xa6, 0x34, 0xc8, 0x1b, 0x4e, 0x71, 0x38, 0x0f, 0xcd, 0xd6, 0xf4, 0xe8, 0x35, 0xf7, 0x5a, 0x64, 0x09, 0xc7, 0xdc, 0x2c, 0x07, 0x41, 0x0e, 0x6f, 0x87, 0x85, 0x8c, 0x7b, 0x94, 0xc0, 0x1c, 0x2e, 0x32, 0xf2, 0x91, 0x76, 0x9e, 0xac, 0xca, 0x71, 0x64, 0x3b, 0x8b, 0x98, 0xa9, 0x63, 0xdf, 0x0a, 0x32, 0x9b, 0xea, 0x4e, 0xd6, 0x39, 0x7e, 0x8c, 0xd0, 0x1a, 0x11, 0x0a, 0xb3, 0x61, 0xac, 0x5b, 0xad, 0x1c, 0xcd, 0x84, 0x0a, 0x6c, 0x8a, 0x6e, 0xaa, 0x00, 0x1a, 0x9d, 0x7d, 0x87, 0xdc, 0x33, 0x18, 0x64, 0x35, 0x71, 0x22, 0x6c, 0x4d, 0xd2, 0xc2, 0xac, 0x41, 0xfb}
|
||||
serverHelloDone := []byte{0x0e, 0x00, 0x00, 0x00}
|
||||
clientKeyExchange := []byte{0x10, 0x00, 0x00, 0x21, 0x20, 0x35, 0x80, 0x72, 0xd6, 0x36, 0x58, 0x80, 0xd1, 0xae, 0xea, 0x32, 0x9a, 0xdf, 0x91, 0x21, 0x38, 0x38, 0x51, 0xed, 0x21, 0xa2, 0x8e, 0x3b, 0x75, 0xe9, 0x65, 0xd0, 0xd2, 0xcd, 0x16, 0x62, 0x54}
|
||||
|
||||
finalMsg := append(append(append(append(append(clientHello, serverHello...), serverCertificate...), serverKeyExchange...), serverHelloDone...), clientKeyExchange...)
|
||||
masterSecret := []byte{0x91, 0x6a, 0xbf, 0x9d, 0xa5, 0x59, 0x73, 0xe1, 0x36, 0x14, 0xae, 0x0a, 0x3f, 0x5d, 0x3f, 0x37, 0xb0, 0x23, 0xba, 0x12, 0x9a, 0xee, 0x02, 0xcc, 0x91, 0x34, 0x33, 0x81, 0x27, 0xcd, 0x70, 0x49, 0x78, 0x1c, 0x8e, 0x19, 0xfc, 0x1e, 0xb2, 0xa7, 0x38, 0x7a, 0xc0, 0x6a, 0xe2, 0x37, 0x34, 0x4c}
|
||||
|
||||
expectedVerifyData := []byte{0xcf, 0x91, 0x96, 0x26, 0xf1, 0x36, 0x0c, 0x53, 0x6a, 0xaa, 0xd7, 0x3a}
|
||||
verifyData, err := VerifyDataClient(masterSecret, finalMsg, sha256.New)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !bytes.Equal(expectedVerifyData, verifyData) {
|
||||
t.Fatalf("verifyData exp: %q actual: %q", expectedVerifyData, verifyData)
|
||||
}
|
||||
}
|
97
dtls-2.0.9/pkg/crypto/selfsign/selfsign.go
Normal file
97
dtls-2.0.9/pkg/crypto/selfsign/selfsign.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// Package selfsign is a test helper that generates self signed certificate.
|
||||
package selfsign
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
var errInvalidPrivateKey = errors.New("selfsign: invalid private key type")
|
||||
|
||||
// GenerateSelfSigned creates a self-signed certificate
|
||||
func GenerateSelfSigned() (tls.Certificate, error) {
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
|
||||
return SelfSign(priv)
|
||||
}
|
||||
|
||||
// GenerateSelfSignedWithDNS creates a self-signed certificate
|
||||
func GenerateSelfSignedWithDNS(cn string, sans ...string) (tls.Certificate, error) {
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
|
||||
return WithDNS(priv, cn, sans...)
|
||||
}
|
||||
|
||||
// SelfSign creates a self-signed certificate from a elliptic curve key
|
||||
func SelfSign(key crypto.PrivateKey) (tls.Certificate, error) {
|
||||
return WithDNS(key, hex.EncodeToString(make([]byte, 16)))
|
||||
}
|
||||
|
||||
// WithDNS creates a self-signed certificate from a elliptic curve key
|
||||
func WithDNS(key crypto.PrivateKey, cn string, sans ...string) (tls.Certificate, error) {
|
||||
var (
|
||||
pubKey crypto.PublicKey
|
||||
maxBigInt = new(big.Int) // Max random value, a 130-bits integer, i.e 2^130 - 1
|
||||
)
|
||||
|
||||
switch k := key.(type) {
|
||||
case ed25519.PrivateKey:
|
||||
pubKey = k.Public()
|
||||
case *ecdsa.PrivateKey:
|
||||
pubKey = k.Public()
|
||||
default:
|
||||
return tls.Certificate{}, errInvalidPrivateKey
|
||||
}
|
||||
|
||||
/* #nosec */
|
||||
maxBigInt.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(maxBigInt, big.NewInt(1))
|
||||
/* #nosec */
|
||||
serialNumber, err := rand.Int(rand.Reader, maxBigInt)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
|
||||
names := []string{cn}
|
||||
names = append(names, sans...)
|
||||
|
||||
template := x509.Certificate{
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||
x509.ExtKeyUsageClientAuth,
|
||||
x509.ExtKeyUsageServerAuth,
|
||||
},
|
||||
BasicConstraintsValid: true,
|
||||
NotBefore: time.Now(),
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
NotAfter: time.Now().AddDate(0, 1, 0),
|
||||
SerialNumber: serialNumber,
|
||||
Version: 2,
|
||||
IsCA: true,
|
||||
DNSNames: names,
|
||||
}
|
||||
|
||||
raw, err := x509.CreateCertificate(rand.Reader, &template, &template, pubKey, key)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
|
||||
return tls.Certificate{
|
||||
Certificate: [][]byte{raw},
|
||||
PrivateKey: key,
|
||||
Leaf: &template,
|
||||
}, nil
|
||||
}
|
24
dtls-2.0.9/pkg/crypto/signature/signature.go
Normal file
24
dtls-2.0.9/pkg/crypto/signature/signature.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Package signature provides our implemented Signature Algorithms
|
||||
package signature
|
||||
|
||||
// Algorithm as defined in TLS 1.2
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16
|
||||
type Algorithm uint16
|
||||
|
||||
// SignatureAlgorithm enums
|
||||
const (
|
||||
Anonymous Algorithm = 0
|
||||
RSA Algorithm = 1
|
||||
ECDSA Algorithm = 3
|
||||
Ed25519 Algorithm = 7
|
||||
)
|
||||
|
||||
// Algorithms returns all implemented Signature Algorithms
|
||||
func Algorithms() map[Algorithm]struct{} {
|
||||
return map[Algorithm]struct{}{
|
||||
Anonymous: {},
|
||||
RSA: {},
|
||||
ECDSA: {},
|
||||
Ed25519: {},
|
||||
}
|
||||
}
|
9
dtls-2.0.9/pkg/crypto/signaturehash/errors.go
Normal file
9
dtls-2.0.9/pkg/crypto/signaturehash/errors.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package signaturehash
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
errNoAvailableSignatureSchemes = errors.New("connection can not be created, no SignatureScheme satisfy this Config")
|
||||
errInvalidSignatureAlgorithm = errors.New("invalid signature algorithm")
|
||||
errInvalidHashAlgorithm = errors.New("invalid hash algorithm")
|
||||
)
|
93
dtls-2.0.9/pkg/crypto/signaturehash/signaturehash.go
Normal file
93
dtls-2.0.9/pkg/crypto/signaturehash/signaturehash.go
Normal file
@@ -0,0 +1,93 @@
|
||||
// Package signaturehash provides the SignatureHashAlgorithm as defined in TLS 1.2
|
||||
package signaturehash
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/crypto/hash"
|
||||
"github.com/pion/dtls/v2/pkg/crypto/signature"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Algorithm is a signature/hash algorithm pairs which may be used in
|
||||
// digital signatures.
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
|
||||
type Algorithm struct {
|
||||
Hash hash.Algorithm
|
||||
Signature signature.Algorithm
|
||||
}
|
||||
|
||||
// Algorithms are all the know SignatureHash Algorithms
|
||||
func Algorithms() []Algorithm {
|
||||
return []Algorithm{
|
||||
{hash.SHA256, signature.ECDSA},
|
||||
{hash.SHA384, signature.ECDSA},
|
||||
{hash.SHA512, signature.ECDSA},
|
||||
{hash.SHA256, signature.RSA},
|
||||
{hash.SHA384, signature.RSA},
|
||||
{hash.SHA512, signature.RSA},
|
||||
{hash.Ed25519, signature.Ed25519},
|
||||
}
|
||||
}
|
||||
|
||||
// SelectSignatureScheme returns most preferred and compatible scheme.
|
||||
func SelectSignatureScheme(sigs []Algorithm, privateKey crypto.PrivateKey) (Algorithm, error) {
|
||||
for _, ss := range sigs {
|
||||
if ss.isCompatible(privateKey) {
|
||||
return ss, nil
|
||||
}
|
||||
}
|
||||
return Algorithm{}, errNoAvailableSignatureSchemes
|
||||
}
|
||||
|
||||
// isCompatible checks that given private key is compatible with the signature scheme.
|
||||
func (a *Algorithm) isCompatible(privateKey crypto.PrivateKey) bool {
|
||||
switch privateKey.(type) {
|
||||
case ed25519.PrivateKey:
|
||||
return a.Signature == signature.Ed25519
|
||||
case *ecdsa.PrivateKey:
|
||||
return a.Signature == signature.ECDSA
|
||||
case *rsa.PrivateKey:
|
||||
return a.Signature == signature.RSA
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ParseSignatureSchemes translates []tls.SignatureScheme to []signatureHashAlgorithm.
|
||||
// It returns default signature scheme list if no SignatureScheme is passed.
|
||||
func ParseSignatureSchemes(sigs []tls.SignatureScheme, insecureHashes bool) ([]Algorithm, error) {
|
||||
if len(sigs) == 0 {
|
||||
return Algorithms(), nil
|
||||
}
|
||||
out := []Algorithm{}
|
||||
for _, ss := range sigs {
|
||||
sig := signature.Algorithm(ss & 0xFF)
|
||||
if _, ok := signature.Algorithms()[sig]; !ok {
|
||||
return nil,
|
||||
xerrors.Errorf("SignatureScheme %04x: %w", ss, errInvalidSignatureAlgorithm)
|
||||
}
|
||||
h := hash.Algorithm(ss >> 8)
|
||||
if _, ok := hash.Algorithms()[h]; !ok || (ok && h == hash.None) {
|
||||
return nil, xerrors.Errorf("SignatureScheme %04x: %w", ss, errInvalidHashAlgorithm)
|
||||
}
|
||||
if h.Insecure() && !insecureHashes {
|
||||
continue
|
||||
}
|
||||
out = append(out, Algorithm{
|
||||
Hash: h,
|
||||
Signature: sig,
|
||||
})
|
||||
}
|
||||
|
||||
if len(out) == 0 {
|
||||
return nil, errNoAvailableSignatureSchemes
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
// +build go1.13
|
||||
|
||||
package signaturehash
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/crypto/hash"
|
||||
"github.com/pion/dtls/v2/pkg/crypto/signature"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func TestParseSignatureSchemes_Ed25519(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
input []tls.SignatureScheme
|
||||
expected []Algorithm
|
||||
err error
|
||||
insecureHashes bool
|
||||
}{
|
||||
"Translate": {
|
||||
input: []tls.SignatureScheme{
|
||||
tls.Ed25519,
|
||||
},
|
||||
expected: []Algorithm{
|
||||
{hash.Ed25519, signature.Ed25519},
|
||||
},
|
||||
err: nil,
|
||||
insecureHashes: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, testCase := range cases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
output, err := ParseSignatureSchemes(testCase.input, testCase.insecureHashes)
|
||||
if testCase.err != nil && !xerrors.Is(err, testCase.err) {
|
||||
t.Fatalf("Expected error: %v, got: %v", testCase.err, err)
|
||||
}
|
||||
if !reflect.DeepEqual(testCase.expected, output) {
|
||||
t.Errorf("Expected signatureHashAlgorithm:\n%+v\ngot:\n%+v", testCase.expected, output)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
102
dtls-2.0.9/pkg/crypto/signaturehash/signaturehash_test.go
Normal file
102
dtls-2.0.9/pkg/crypto/signaturehash/signaturehash_test.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package signaturehash
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/crypto/hash"
|
||||
"github.com/pion/dtls/v2/pkg/crypto/signature"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func TestParseSignatureSchemes(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
input []tls.SignatureScheme
|
||||
expected []Algorithm
|
||||
err error
|
||||
insecureHashes bool
|
||||
}{
|
||||
"Translate": {
|
||||
input: []tls.SignatureScheme{
|
||||
tls.ECDSAWithP256AndSHA256,
|
||||
tls.ECDSAWithP384AndSHA384,
|
||||
tls.ECDSAWithP521AndSHA512,
|
||||
tls.PKCS1WithSHA256,
|
||||
tls.PKCS1WithSHA384,
|
||||
tls.PKCS1WithSHA512,
|
||||
},
|
||||
expected: []Algorithm{
|
||||
{hash.SHA256, signature.ECDSA},
|
||||
{hash.SHA384, signature.ECDSA},
|
||||
{hash.SHA512, signature.ECDSA},
|
||||
{hash.SHA256, signature.RSA},
|
||||
{hash.SHA384, signature.RSA},
|
||||
{hash.SHA512, signature.RSA},
|
||||
},
|
||||
insecureHashes: false,
|
||||
err: nil,
|
||||
},
|
||||
"InvalidSignatureAlgorithm": {
|
||||
input: []tls.SignatureScheme{
|
||||
tls.ECDSAWithP256AndSHA256, // Valid
|
||||
0x04FF, // Invalid: unknown signature with SHA-256
|
||||
},
|
||||
expected: nil,
|
||||
insecureHashes: false,
|
||||
err: errInvalidSignatureAlgorithm,
|
||||
},
|
||||
"InvalidHashAlgorithm": {
|
||||
input: []tls.SignatureScheme{
|
||||
tls.ECDSAWithP256AndSHA256, // Valid
|
||||
0x0003, // Invalid: ECDSA with None
|
||||
},
|
||||
expected: nil,
|
||||
insecureHashes: false,
|
||||
err: errInvalidHashAlgorithm,
|
||||
},
|
||||
"InsecureHashAlgorithmDenied": {
|
||||
input: []tls.SignatureScheme{
|
||||
tls.ECDSAWithP256AndSHA256, // Valid
|
||||
tls.ECDSAWithSHA1, // Insecure
|
||||
},
|
||||
expected: []Algorithm{
|
||||
{hash.SHA256, signature.ECDSA},
|
||||
},
|
||||
insecureHashes: false,
|
||||
err: nil,
|
||||
},
|
||||
"InsecureHashAlgorithmAllowed": {
|
||||
input: []tls.SignatureScheme{
|
||||
tls.ECDSAWithP256AndSHA256, // Valid
|
||||
tls.ECDSAWithSHA1, // Insecure
|
||||
},
|
||||
expected: []Algorithm{
|
||||
{hash.SHA256, signature.ECDSA},
|
||||
{hash.SHA1, signature.ECDSA},
|
||||
},
|
||||
insecureHashes: true,
|
||||
err: nil,
|
||||
},
|
||||
"OnlyInsecureHashAlgorithm": {
|
||||
input: []tls.SignatureScheme{
|
||||
tls.ECDSAWithSHA1, // Insecure
|
||||
},
|
||||
insecureHashes: false,
|
||||
err: errNoAvailableSignatureSchemes,
|
||||
},
|
||||
}
|
||||
|
||||
for name, testCase := range cases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
output, err := ParseSignatureSchemes(testCase.input, testCase.insecureHashes)
|
||||
if testCase.err != nil && !xerrors.Is(err, testCase.err) {
|
||||
t.Fatalf("Expected error: %v, got: %v", testCase.err, err)
|
||||
}
|
||||
if !reflect.DeepEqual(testCase.expected, output) {
|
||||
t.Errorf("Expected signatureHashAlgorithm:\n%+v\ngot:\n%+v", testCase.expected, output)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
160
dtls-2.0.9/pkg/protocol/alert/alert.go
Normal file
160
dtls-2.0.9/pkg/protocol/alert/alert.go
Normal file
@@ -0,0 +1,160 @@
|
||||
// Package alert implements TLS alert protocol https://tools.ietf.org/html/rfc5246#section-7.2
|
||||
package alert
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/protocol"
|
||||
)
|
||||
|
||||
var errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
|
||||
|
||||
// Level is the level of the TLS Alert
|
||||
type Level byte
|
||||
|
||||
// Level enums
|
||||
const (
|
||||
Warning Level = 1
|
||||
Fatal Level = 2
|
||||
)
|
||||
|
||||
func (l Level) String() string {
|
||||
switch l {
|
||||
case Warning:
|
||||
return "Warning"
|
||||
case Fatal:
|
||||
return "Fatal"
|
||||
default:
|
||||
return "Invalid alert level"
|
||||
}
|
||||
}
|
||||
|
||||
// Description is the extended info of the TLS Alert
|
||||
type Description byte
|
||||
|
||||
// Description enums
|
||||
const (
|
||||
CloseNotify Description = 0
|
||||
UnexpectedMessage Description = 10
|
||||
BadRecordMac Description = 20
|
||||
DecryptionFailed Description = 21
|
||||
RecordOverflow Description = 22
|
||||
DecompressionFailure Description = 30
|
||||
HandshakeFailure Description = 40
|
||||
NoCertificate Description = 41
|
||||
BadCertificate Description = 42
|
||||
UnsupportedCertificate Description = 43
|
||||
CertificateRevoked Description = 44
|
||||
CertificateExpired Description = 45
|
||||
CertificateUnknown Description = 46
|
||||
IllegalParameter Description = 47
|
||||
UnknownCA Description = 48
|
||||
AccessDenied Description = 49
|
||||
DecodeError Description = 50
|
||||
DecryptError Description = 51
|
||||
ExportRestriction Description = 60
|
||||
ProtocolVersion Description = 70
|
||||
InsufficientSecurity Description = 71
|
||||
InternalError Description = 80
|
||||
UserCanceled Description = 90
|
||||
NoRenegotiation Description = 100
|
||||
UnsupportedExtension Description = 110
|
||||
)
|
||||
|
||||
func (d Description) String() string {
|
||||
switch d {
|
||||
case CloseNotify:
|
||||
return "CloseNotify"
|
||||
case UnexpectedMessage:
|
||||
return "UnexpectedMessage"
|
||||
case BadRecordMac:
|
||||
return "BadRecordMac"
|
||||
case DecryptionFailed:
|
||||
return "DecryptionFailed"
|
||||
case RecordOverflow:
|
||||
return "RecordOverflow"
|
||||
case DecompressionFailure:
|
||||
return "DecompressionFailure"
|
||||
case HandshakeFailure:
|
||||
return "HandshakeFailure"
|
||||
case NoCertificate:
|
||||
return "NoCertificate"
|
||||
case BadCertificate:
|
||||
return "BadCertificate"
|
||||
case UnsupportedCertificate:
|
||||
return "UnsupportedCertificate"
|
||||
case CertificateRevoked:
|
||||
return "CertificateRevoked"
|
||||
case CertificateExpired:
|
||||
return "CertificateExpired"
|
||||
case CertificateUnknown:
|
||||
return "CertificateUnknown"
|
||||
case IllegalParameter:
|
||||
return "IllegalParameter"
|
||||
case UnknownCA:
|
||||
return "UnknownCA"
|
||||
case AccessDenied:
|
||||
return "AccessDenied"
|
||||
case DecodeError:
|
||||
return "DecodeError"
|
||||
case DecryptError:
|
||||
return "DecryptError"
|
||||
case ExportRestriction:
|
||||
return "ExportRestriction"
|
||||
case ProtocolVersion:
|
||||
return "ProtocolVersion"
|
||||
case InsufficientSecurity:
|
||||
return "InsufficientSecurity"
|
||||
case InternalError:
|
||||
return "InternalError"
|
||||
case UserCanceled:
|
||||
return "UserCanceled"
|
||||
case NoRenegotiation:
|
||||
return "NoRenegotiation"
|
||||
case UnsupportedExtension:
|
||||
return "UnsupportedExtension"
|
||||
default:
|
||||
return "Invalid alert description"
|
||||
}
|
||||
}
|
||||
|
||||
// Alert is one of the content types supported by the TLS record layer.
|
||||
// Alert messages convey the severity of the message
|
||||
// (warning or fatal) and a description of the alert. Alert messages
|
||||
// with a level of fatal result in the immediate termination of the
|
||||
// connection. In this case, other connections corresponding to the
|
||||
// session may continue, but the session identifier MUST be invalidated,
|
||||
// preventing the failed session from being used to establish new
|
||||
// connections. Like other messages, alert messages are encrypted and
|
||||
// compressed, as specified by the current connection state.
|
||||
// https://tools.ietf.org/html/rfc5246#section-7.2
|
||||
type Alert struct {
|
||||
Level Level
|
||||
Description Description
|
||||
}
|
||||
|
||||
// ContentType returns the ContentType of this Content
|
||||
func (a Alert) ContentType() protocol.ContentType {
|
||||
return protocol.ContentTypeAlert
|
||||
}
|
||||
|
||||
// Marshal returns the encoded alert
|
||||
func (a *Alert) Marshal() ([]byte, error) {
|
||||
return []byte{byte(a.Level), byte(a.Description)}, nil
|
||||
}
|
||||
|
||||
// Unmarshal populates the alert from binary data
|
||||
func (a *Alert) Unmarshal(data []byte) error {
|
||||
if len(data) != 2 {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
|
||||
a.Level = Level(data[0])
|
||||
a.Description = Description(data[1])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Alert) String() string {
|
||||
return fmt.Sprintf("Alert %s: %s", a.Level, a.Description)
|
||||
}
|
49
dtls-2.0.9/pkg/protocol/alert/alert_test.go
Normal file
49
dtls-2.0.9/pkg/protocol/alert/alert_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package alert
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAlert(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
Name string
|
||||
Data []byte
|
||||
Want *Alert
|
||||
WantUnmarshalError error
|
||||
}{
|
||||
{
|
||||
Name: "Valid Alert",
|
||||
Data: []byte{0x02, 0x0A},
|
||||
Want: &Alert{
|
||||
Level: Fatal,
|
||||
Description: UnexpectedMessage,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Invalid alert length",
|
||||
Data: []byte{0x00},
|
||||
Want: &Alert{},
|
||||
WantUnmarshalError: errBufferTooSmall,
|
||||
},
|
||||
} {
|
||||
a := &Alert{}
|
||||
if err := a.Unmarshal(test.Data); !errors.Is(err, test.WantUnmarshalError) {
|
||||
t.Errorf("Unexpected Error %v: exp: %v got: %v", test.Name, test.WantUnmarshalError, err)
|
||||
} else if !reflect.DeepEqual(test.Want, a) {
|
||||
t.Errorf("%q alert.unmarshal: got %v, want %v", test.Name, a, test.Want)
|
||||
}
|
||||
|
||||
if test.WantUnmarshalError != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, marshalErr := a.Marshal()
|
||||
if marshalErr != nil {
|
||||
t.Errorf("Unexpected Error %v: got: %v", test.Name, marshalErr)
|
||||
} else if !reflect.DeepEqual(test.Data, data) {
|
||||
t.Errorf("%q alert.marshal: got % 02x, want % 02x", test.Name, data, test.Data)
|
||||
}
|
||||
}
|
||||
}
|
26
dtls-2.0.9/pkg/protocol/application_data.go
Normal file
26
dtls-2.0.9/pkg/protocol/application_data.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package protocol
|
||||
|
||||
// ApplicationData messages are carried by the record layer and are
|
||||
// fragmented, compressed, and encrypted based on the current connection
|
||||
// state. The messages are treated as transparent data to the record
|
||||
// layer.
|
||||
// https://tools.ietf.org/html/rfc5246#section-10
|
||||
type ApplicationData struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// ContentType returns the ContentType of this content
|
||||
func (a ApplicationData) ContentType() ContentType {
|
||||
return ContentTypeApplicationData
|
||||
}
|
||||
|
||||
// Marshal encodes the ApplicationData to binary
|
||||
func (a *ApplicationData) Marshal() ([]byte, error) {
|
||||
return append([]byte{}, a.Data...), nil
|
||||
}
|
||||
|
||||
// Unmarshal populates the ApplicationData from binary
|
||||
func (a *ApplicationData) Unmarshal(data []byte) error {
|
||||
a.Data = append([]byte{}, data...)
|
||||
return nil
|
||||
}
|
28
dtls-2.0.9/pkg/protocol/change_cipher_spec.go
Normal file
28
dtls-2.0.9/pkg/protocol/change_cipher_spec.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package protocol
|
||||
|
||||
// ChangeCipherSpec protocol exists to signal transitions in
|
||||
// ciphering strategies. The protocol consists of a single message,
|
||||
// which is encrypted and compressed under the current (not the pending)
|
||||
// connection state. The message consists of a single byte of value 1.
|
||||
// https://tools.ietf.org/html/rfc5246#section-7.1
|
||||
type ChangeCipherSpec struct {
|
||||
}
|
||||
|
||||
// ContentType returns the ContentType of this content
|
||||
func (c ChangeCipherSpec) ContentType() ContentType {
|
||||
return ContentTypeChangeCipherSpec
|
||||
}
|
||||
|
||||
// Marshal encodes the ChangeCipherSpec to binary
|
||||
func (c *ChangeCipherSpec) Marshal() ([]byte, error) {
|
||||
return []byte{0x01}, nil
|
||||
}
|
||||
|
||||
// Unmarshal populates the ChangeCipherSpec from binary
|
||||
func (c *ChangeCipherSpec) Unmarshal(data []byte) error {
|
||||
if len(data) == 1 && data[0] == 0x01 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errInvalidCipherSpec
|
||||
}
|
31
dtls-2.0.9/pkg/protocol/change_cipher_spec_test.go
Normal file
31
dtls-2.0.9/pkg/protocol/change_cipher_spec_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChangeCipherSpecRoundTrip(t *testing.T) {
|
||||
c := ChangeCipherSpec{}
|
||||
raw, err := c.Marshal()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
var cNew ChangeCipherSpec
|
||||
if err := cNew.Unmarshal(raw); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(c, cNew) {
|
||||
t.Errorf("ChangeCipherSpec round trip: got %#v, want %#v", cNew, c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangeCipherSpecInvalid(t *testing.T) {
|
||||
c := ChangeCipherSpec{}
|
||||
if err := c.Unmarshal([]byte{0x00}); !errors.Is(err, errInvalidCipherSpec) {
|
||||
t.Errorf("ChangeCipherSpec invalid assert: got %#v, want %#v", err, errInvalidCipherSpec)
|
||||
}
|
||||
}
|
48
dtls-2.0.9/pkg/protocol/compression_method.go
Normal file
48
dtls-2.0.9/pkg/protocol/compression_method.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package protocol
|
||||
|
||||
// CompressionMethodID is the ID for a CompressionMethod
|
||||
type CompressionMethodID byte
|
||||
|
||||
const (
|
||||
compressionMethodNull CompressionMethodID = 0
|
||||
)
|
||||
|
||||
// CompressionMethod represents a TLS Compression Method
|
||||
type CompressionMethod struct {
|
||||
ID CompressionMethodID
|
||||
}
|
||||
|
||||
// CompressionMethods returns all supported CompressionMethods
|
||||
func CompressionMethods() map[CompressionMethodID]*CompressionMethod {
|
||||
return map[CompressionMethodID]*CompressionMethod{
|
||||
compressionMethodNull: {ID: compressionMethodNull},
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeCompressionMethods the given compression methods
|
||||
func DecodeCompressionMethods(buf []byte) ([]*CompressionMethod, error) {
|
||||
if len(buf) < 1 {
|
||||
return nil, errBufferTooSmall
|
||||
}
|
||||
compressionMethodsCount := int(buf[0])
|
||||
c := []*CompressionMethod{}
|
||||
for i := 0; i < compressionMethodsCount; i++ {
|
||||
if len(buf) <= i+1 {
|
||||
return nil, errBufferTooSmall
|
||||
}
|
||||
id := CompressionMethodID(buf[i+1])
|
||||
if compressionMethod, ok := CompressionMethods()[id]; ok {
|
||||
c = append(c, compressionMethod)
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// EncodeCompressionMethods the given compression methods
|
||||
func EncodeCompressionMethods(c []*CompressionMethod) []byte {
|
||||
out := []byte{byte(len(c))}
|
||||
for i := len(c); i > 0; i-- {
|
||||
out = append(out, byte(c[i-1].ID))
|
||||
}
|
||||
return out
|
||||
}
|
23
dtls-2.0.9/pkg/protocol/compression_method_test.go
Normal file
23
dtls-2.0.9/pkg/protocol/compression_method_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecodeCompressionMethods(t *testing.T) {
|
||||
testCases := []struct {
|
||||
buf []byte
|
||||
result []*CompressionMethod
|
||||
err error
|
||||
}{
|
||||
{[]byte{}, nil, errBufferTooSmall},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
_, err := DecodeCompressionMethods(testCase.buf)
|
||||
if !errors.Is(err, testCase.err) {
|
||||
t.Fatal("Unexpected error", err)
|
||||
}
|
||||
}
|
||||
}
|
21
dtls-2.0.9/pkg/protocol/content.go
Normal file
21
dtls-2.0.9/pkg/protocol/content.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package protocol
|
||||
|
||||
// ContentType represents the IANA Registered ContentTypes
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc4346#section-6.2.1
|
||||
type ContentType uint8
|
||||
|
||||
// ContentType enums
|
||||
const (
|
||||
ContentTypeChangeCipherSpec ContentType = 20
|
||||
ContentTypeAlert ContentType = 21
|
||||
ContentTypeHandshake ContentType = 22
|
||||
ContentTypeApplicationData ContentType = 23
|
||||
)
|
||||
|
||||
// Content is the top level distinguisher for a DTLS Datagram
|
||||
type Content interface {
|
||||
ContentType() ContentType
|
||||
Marshal() ([]byte, error)
|
||||
Unmarshal(data []byte) error
|
||||
}
|
104
dtls-2.0.9/pkg/protocol/errors.go
Normal file
104
dtls-2.0.9/pkg/protocol/errors.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
errBufferTooSmall = &TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
|
||||
errInvalidCipherSpec = &FatalError{Err: errors.New("cipher spec invalid")} //nolint:goerr113
|
||||
)
|
||||
|
||||
// FatalError indicates that the DTLS connection is no longer available.
|
||||
// It is mainly caused by wrong configuration of server or client.
|
||||
type FatalError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// InternalError indicates and internal error caused by the implementation, and the DTLS connection is no longer available.
|
||||
// It is mainly caused by bugs or tried to use unimplemented features.
|
||||
type InternalError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// TemporaryError indicates that the DTLS connection is still available, but the request was failed temporary.
|
||||
type TemporaryError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// TimeoutError indicates that the request was timed out.
|
||||
type TimeoutError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// HandshakeError indicates that the handshake failed.
|
||||
type HandshakeError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Timeout implements net.Error.Timeout()
|
||||
func (*FatalError) Timeout() bool { return false }
|
||||
|
||||
// Temporary implements net.Error.Temporary()
|
||||
func (*FatalError) Temporary() bool { return false }
|
||||
|
||||
// Unwrap implements Go1.13 error unwrapper.
|
||||
func (e *FatalError) Unwrap() error { return e.Err }
|
||||
|
||||
func (e *FatalError) Error() string { return fmt.Sprintf("dtls fatal: %v", e.Err) }
|
||||
|
||||
// Timeout implements net.Error.Timeout()
|
||||
func (*InternalError) Timeout() bool { return false }
|
||||
|
||||
// Temporary implements net.Error.Temporary()
|
||||
func (*InternalError) Temporary() bool { return false }
|
||||
|
||||
// Unwrap implements Go1.13 error unwrapper.
|
||||
func (e *InternalError) Unwrap() error { return e.Err }
|
||||
|
||||
func (e *InternalError) Error() string { return fmt.Sprintf("dtls internal: %v", e.Err) }
|
||||
|
||||
// Timeout implements net.Error.Timeout()
|
||||
func (*TemporaryError) Timeout() bool { return false }
|
||||
|
||||
// Temporary implements net.Error.Temporary()
|
||||
func (*TemporaryError) Temporary() bool { return true }
|
||||
|
||||
// Unwrap implements Go1.13 error unwrapper.
|
||||
func (e *TemporaryError) Unwrap() error { return e.Err }
|
||||
|
||||
func (e *TemporaryError) Error() string { return fmt.Sprintf("dtls temporary: %v", e.Err) }
|
||||
|
||||
// Timeout implements net.Error.Timeout()
|
||||
func (*TimeoutError) Timeout() bool { return true }
|
||||
|
||||
// Temporary implements net.Error.Temporary()
|
||||
func (*TimeoutError) Temporary() bool { return true }
|
||||
|
||||
// Unwrap implements Go1.13 error unwrapper.
|
||||
func (e *TimeoutError) Unwrap() error { return e.Err }
|
||||
|
||||
func (e *TimeoutError) Error() string { return fmt.Sprintf("dtls timeout: %v", e.Err) }
|
||||
|
||||
// Timeout implements net.Error.Timeout()
|
||||
func (e *HandshakeError) Timeout() bool {
|
||||
if netErr, ok := e.Err.(net.Error); ok {
|
||||
return netErr.Timeout()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Temporary implements net.Error.Temporary()
|
||||
func (e *HandshakeError) Temporary() bool {
|
||||
if netErr, ok := e.Err.(net.Error); ok {
|
||||
return netErr.Temporary()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Unwrap implements Go1.13 error unwrapper.
|
||||
func (e *HandshakeError) Unwrap() error { return e.Err }
|
||||
|
||||
func (e *HandshakeError) Error() string { return fmt.Sprintf("handshake error: %v", e.Err) }
|
14
dtls-2.0.9/pkg/protocol/extension/errors.go
Normal file
14
dtls-2.0.9/pkg/protocol/extension/errors.go
Normal 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
|
||||
)
|
96
dtls-2.0.9/pkg/protocol/extension/extension.go
Normal file
96
dtls-2.0.9/pkg/protocol/extension/extension.go
Normal 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
|
||||
}
|
22
dtls-2.0.9/pkg/protocol/extension/extension_test.go
Normal file
22
dtls-2.0.9/pkg/protocol/extension/extension_test.go
Normal 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")
|
||||
}
|
||||
})
|
||||
}
|
43
dtls-2.0.9/pkg/protocol/extension/renegotiation_info.go
Normal file
43
dtls-2.0.9/pkg/protocol/extension/renegotiation_info.go
Normal 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
|
||||
}
|
22
dtls-2.0.9/pkg/protocol/extension/renegotiation_info_test.go
Normal file
22
dtls-2.0.9/pkg/protocol/extension/renegotiation_info_test.go
Normal 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)
|
||||
}
|
||||
}
|
78
dtls-2.0.9/pkg/protocol/extension/server_name.go
Normal file
78
dtls-2.0.9/pkg/protocol/extension/server_name.go
Normal 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
|
||||
}
|
22
dtls-2.0.9/pkg/protocol/extension/server_name_test.go
Normal file
22
dtls-2.0.9/pkg/protocol/extension/server_name_test.go
Normal 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)
|
||||
}
|
||||
}
|
21
dtls-2.0.9/pkg/protocol/extension/srtp_protection_profile.go
Normal file
21
dtls-2.0.9/pkg/protocol/extension/srtp_protection_profile.go
Normal 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,
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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)
|
||||
}
|
||||
}
|
62
dtls-2.0.9/pkg/protocol/extension/supported_point_formats.go
Normal file
62
dtls-2.0.9/pkg/protocol/extension/supported_point_formats.go
Normal 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
|
||||
}
|
@@ -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)
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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)
|
||||
}
|
||||
}
|
45
dtls-2.0.9/pkg/protocol/extension/use_master_secret.go
Normal file
45
dtls-2.0.9/pkg/protocol/extension/use_master_secret.go
Normal 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
|
||||
}
|
59
dtls-2.0.9/pkg/protocol/extension/use_srtp.go
Normal file
59
dtls-2.0.9/pkg/protocol/extension/use_srtp.go
Normal 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
|
||||
}
|
20
dtls-2.0.9/pkg/protocol/extension/use_srtp_test.go
Normal file
20
dtls-2.0.9/pkg/protocol/extension/use_srtp_test.go
Normal 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)
|
||||
}
|
||||
}
|
29
dtls-2.0.9/pkg/protocol/handshake/cipher_suite.go
Normal file
29
dtls-2.0.9/pkg/protocol/handshake/cipher_suite.go
Normal 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
|
||||
}
|
23
dtls-2.0.9/pkg/protocol/handshake/cipher_suite_test.go
Normal file
23
dtls-2.0.9/pkg/protocol/handshake/cipher_suite_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecodeCipherSuiteIDs(t *testing.T) {
|
||||
testCases := []struct {
|
||||
buf []byte
|
||||
result []uint16
|
||||
err error
|
||||
}{
|
||||
{[]byte{}, nil, errBufferTooSmall},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
_, err := decodeCipherSuiteIDs(testCase.buf)
|
||||
if !errors.Is(err, testCase.err) {
|
||||
t.Fatal("Unexpected error", err)
|
||||
}
|
||||
}
|
||||
}
|
25
dtls-2.0.9/pkg/protocol/handshake/errors.go
Normal file
25
dtls-2.0.9/pkg/protocol/handshake/errors.go
Normal 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
|
||||
)
|
145
dtls-2.0.9/pkg/protocol/handshake/handshake.go
Normal file
145
dtls-2.0.9/pkg/protocol/handshake/handshake.go
Normal file
@@ -0,0 +1,145 @@
|
||||
// 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:])
|
||||
}
|
50
dtls-2.0.9/pkg/protocol/handshake/header.go
Normal file
50
dtls-2.0.9/pkg/protocol/handshake/header.go
Normal 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
|
||||
}
|
66
dtls-2.0.9/pkg/protocol/handshake/message_certificate.go
Normal file
66
dtls-2.0.9/pkg/protocol/handshake/message_certificate.go
Normal 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
|
||||
}
|
100
dtls-2.0.9/pkg/protocol/handshake/message_certificate_request.go
Normal file
100
dtls-2.0.9/pkg/protocol/handshake/message_certificate_request.go
Normal 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
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func TestHandshakeMessageCertificateRequest(t *testing.T) {
|
||||
rawCertificateRequest := []byte{
|
||||
0x02, 0x01, 0x40, 0x00, 0x0C, 0x04, 0x03, 0x04, 0x01, 0x05,
|
||||
0x03, 0x05, 0x01, 0x06, 0x01, 0x02, 0x01, 0x00, 0x00,
|
||||
}
|
||||
parsedCertificateRequest := &MessageCertificateRequest{
|
||||
CertificateTypes: []clientcertificate.Type{
|
||||
clientcertificate.RSASign,
|
||||
clientcertificate.ECDSASign,
|
||||
},
|
||||
SignatureHashAlgorithms: []signaturehash.Algorithm{
|
||||
{Hash: hash.SHA256, Signature: signature.ECDSA},
|
||||
{Hash: hash.SHA256, Signature: signature.RSA},
|
||||
{Hash: hash.SHA384, Signature: signature.ECDSA},
|
||||
{Hash: hash.SHA384, Signature: signature.RSA},
|
||||
{Hash: hash.SHA512, Signature: signature.RSA},
|
||||
{Hash: hash.SHA1, Signature: signature.RSA},
|
||||
},
|
||||
}
|
||||
|
||||
c := &MessageCertificateRequest{}
|
||||
if err := c.Unmarshal(rawCertificateRequest); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(c, parsedCertificateRequest) {
|
||||
t.Errorf("parsedCertificateRequest unmarshal: got %#v, want %#v", c, parsedCertificateRequest)
|
||||
}
|
||||
|
||||
raw, err := c.Marshal()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(raw, rawCertificateRequest) {
|
||||
t.Errorf("parsedCertificateRequest marshal: got %#v, want %#v", raw, rawCertificateRequest)
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHandshakeMessageCertificate(t *testing.T) {
|
||||
// Not easy to mock out these members, just copy for now (since everything else matches)
|
||||
copyCertificatePrivateMembers := func(src, dst *x509.Certificate) {
|
||||
dst.PublicKey = src.PublicKey
|
||||
dst.SerialNumber = src.SerialNumber
|
||||
dst.Issuer = src.Issuer
|
||||
dst.Subject = src.Subject
|
||||
dst.NotBefore = src.NotBefore
|
||||
dst.NotAfter = src.NotAfter
|
||||
}
|
||||
|
||||
rawCertificate := []byte{
|
||||
0x00, 0x01, 0x8c, 0x00, 0x01, 0x89, 0x30, 0x82, 0x01, 0x85, 0x30, 0x82, 0x01, 0x2b, 0x02, 0x14,
|
||||
0x7d, 0x00, 0xcf, 0x07, 0xfc, 0xe2, 0xb6, 0xb8, 0x3f, 0x72, 0xeb, 0x11, 0x36, 0x1b, 0xf6, 0x39,
|
||||
0xf1, 0x3c, 0x33, 0x41, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
|
||||
0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31,
|
||||
0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53,
|
||||
0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20,
|
||||
0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x31, 0x30, 0x32,
|
||||
0x35, 0x30, 0x38, 0x35, 0x31, 0x31, 0x32, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x30, 0x32, 0x35,
|
||||
0x30, 0x38, 0x35, 0x31, 0x31, 0x32, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
|
||||
0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c,
|
||||
0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
|
||||
0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
|
||||
0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x59,
|
||||
0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
|
||||
0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xf9, 0xb1, 0x62, 0xd6, 0x07, 0xae, 0xc3,
|
||||
0x36, 0x34, 0xf5, 0xa3, 0x09, 0x39, 0x86, 0xe7, 0x3b, 0x59, 0xf7, 0x4a, 0x1d, 0xf4, 0x97, 0x4f,
|
||||
0x91, 0x40, 0x56, 0x1b, 0x3d, 0x6c, 0x5a, 0x38, 0x10, 0x15, 0x58, 0xf5, 0xa4, 0xcc, 0xdf, 0xd5,
|
||||
0xf5, 0x4a, 0x35, 0x40, 0x0f, 0x9f, 0x54, 0xb7, 0xe9, 0xe2, 0xae, 0x63, 0x83, 0x6a, 0x4c, 0xfc,
|
||||
0xc2, 0x5f, 0x78, 0xa0, 0xbb, 0x46, 0x54, 0xa4, 0xda, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48,
|
||||
0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20, 0x47, 0x1a, 0x5f, 0x58,
|
||||
0x2a, 0x74, 0x33, 0x6d, 0xed, 0xac, 0x37, 0x21, 0xfa, 0x76, 0x5a, 0x4d, 0x78, 0x68, 0x1a, 0xdd,
|
||||
0x80, 0xa4, 0xd4, 0xb7, 0x7f, 0x7d, 0x78, 0xb3, 0xfb, 0xf3, 0x95, 0xfb, 0x02, 0x21, 0x00, 0xc0,
|
||||
0x73, 0x30, 0xda, 0x2b, 0xc0, 0x0c, 0x9e, 0xb2, 0x25, 0x0d, 0x46, 0xb0, 0xbc, 0x66, 0x7f, 0x71,
|
||||
0x66, 0xbf, 0x16, 0xb3, 0x80, 0x78, 0xd0, 0x0c, 0xef, 0xcc, 0xf5, 0xc1, 0x15, 0x0f, 0x58,
|
||||
}
|
||||
|
||||
parsedCertificate := &x509.Certificate{
|
||||
Raw: rawCertificate[6:],
|
||||
RawTBSCertificate: rawCertificate[10:313],
|
||||
RawSubjectPublicKeyInfo: rawCertificate[222:313],
|
||||
RawSubject: rawCertificate[48:119],
|
||||
RawIssuer: rawCertificate[48:119],
|
||||
Signature: rawCertificate[328:],
|
||||
SignatureAlgorithm: x509.ECDSAWithSHA256,
|
||||
PublicKeyAlgorithm: x509.ECDSA,
|
||||
Version: 1,
|
||||
}
|
||||
|
||||
c := &MessageCertificate{}
|
||||
if err := c.Unmarshal(rawCertificate); err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
certificate, err := x509.ParseCertificate(c.Certificate[0])
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
copyCertificatePrivateMembers(certificate, parsedCertificate)
|
||||
if !reflect.DeepEqual(certificate, parsedCertificate) {
|
||||
t.Errorf("handshakeMessageCertificate unmarshal: got %#v, want %#v", c, parsedCertificate)
|
||||
}
|
||||
}
|
||||
|
||||
raw, err := c.Marshal()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(raw, rawCertificate) {
|
||||
t.Errorf("handshakeMessageCertificate marshal: got %#v, want %#v", raw, rawCertificate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyHandshakeMessageCertificate(t *testing.T) {
|
||||
rawCertificate := []byte{
|
||||
0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
expectedCertificate := &MessageCertificate{
|
||||
Certificate: nil,
|
||||
}
|
||||
|
||||
c := &MessageCertificate{}
|
||||
if err := c.Unmarshal(rawCertificate); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(c, expectedCertificate) {
|
||||
t.Errorf("handshakeMessageCertificate unmarshal: got %#v, want %#v", c, expectedCertificate)
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/crypto/hash"
|
||||
"github.com/pion/dtls/v2/pkg/crypto/signature"
|
||||
)
|
||||
|
||||
func TestHandshakeMessageCertificateVerify(t *testing.T) {
|
||||
rawCertificateVerify := []byte{
|
||||
0x04, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x20, 0x6b, 0x63, 0x17, 0xad, 0xbe, 0xb7, 0x7b, 0x0f,
|
||||
0x86, 0x73, 0x39, 0x1e, 0xba, 0xb3, 0x50, 0x9c, 0xce, 0x9c, 0xe4, 0x8b, 0xe5, 0x13, 0x07, 0x59,
|
||||
0x18, 0x1f, 0xe5, 0xa0, 0x2b, 0xca, 0xa6, 0xad, 0x02, 0x21, 0x00, 0xd3, 0xb5, 0x01, 0xbe, 0x87,
|
||||
0x6c, 0x04, 0xa1, 0xdc, 0x28, 0xaa, 0x5f, 0xf7, 0x1e, 0x9c, 0xc0, 0x1e, 0x00, 0x2c, 0xe5, 0x94,
|
||||
0xbb, 0x03, 0x0e, 0xf1, 0xcb, 0x28, 0x22, 0x33, 0x23, 0x88, 0xad,
|
||||
}
|
||||
parsedCertificateVerify := &MessageCertificateVerify{
|
||||
HashAlgorithm: hash.Algorithm(rawCertificateVerify[0]),
|
||||
SignatureAlgorithm: signature.Algorithm(rawCertificateVerify[1]),
|
||||
Signature: rawCertificateVerify[4:],
|
||||
}
|
||||
|
||||
c := &MessageCertificateVerify{}
|
||||
if err := c.Unmarshal(rawCertificateVerify); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(c, parsedCertificateVerify) {
|
||||
t.Errorf("handshakeMessageCertificate unmarshal: got %#v, want %#v", c, parsedCertificateVerify)
|
||||
}
|
||||
|
||||
raw, err := c.Marshal()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(raw, rawCertificateVerify) {
|
||||
t.Errorf("handshakeMessageCertificateVerify marshal: got %#v, want %#v", raw, rawCertificateVerify)
|
||||
}
|
||||
}
|
130
dtls-2.0.9/pkg/protocol/handshake/message_client_hello.go
Normal file
130
dtls-2.0.9/pkg/protocol/handshake/message_client_hello.go
Normal file
@@ -0,0 +1,130 @@
|
||||
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 // TODO 添加anylink支持
|
||||
|
||||
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, 0x00) // 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 += int(data[currOffset]) + 1 // SessionID
|
||||
|
||||
// TODO 添加SessionID
|
||||
m.SessionID = data[handshakeMessageClientHelloVariableWidthStart+1 : currOffset]
|
||||
|
||||
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
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/crypto/elliptic"
|
||||
"github.com/pion/dtls/v2/pkg/protocol"
|
||||
"github.com/pion/dtls/v2/pkg/protocol/extension"
|
||||
)
|
||||
|
||||
func TestHandshakeMessageClientHello(t *testing.T) {
|
||||
rawClientHello := []byte{
|
||||
0xfe, 0xfd, 0xb6, 0x2f, 0xce, 0x5c, 0x42, 0x54, 0xff, 0x86, 0xe1, 0x24, 0x41, 0x91, 0x42,
|
||||
0x62, 0x15, 0xad, 0x16, 0xc9, 0x15, 0x8d, 0x95, 0x71, 0x8a, 0xbb, 0x22, 0xd7, 0x47, 0xec,
|
||||
0xd8, 0x3d, 0xdc, 0x4b, 0x00, 0x14, 0xe6, 0x14, 0x3a, 0x1b, 0x04, 0xea, 0x9e, 0x7a, 0x14,
|
||||
0xd6, 0x6c, 0x57, 0xd0, 0x0e, 0x32, 0x85, 0x76, 0x18, 0xde, 0xd8, 0x00, 0x04, 0xc0, 0x2b,
|
||||
0xc0, 0x0a, 0x01, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0x00, 0x1d,
|
||||
}
|
||||
parsedClientHello := &MessageClientHello{
|
||||
Version: protocol.Version{Major: 0xFE, Minor: 0xFD},
|
||||
Random: Random{
|
||||
GMTUnixTime: time.Unix(3056586332, 0),
|
||||
RandomBytes: [28]byte{0x42, 0x54, 0xff, 0x86, 0xe1, 0x24, 0x41, 0x91, 0x42, 0x62, 0x15, 0xad, 0x16, 0xc9, 0x15, 0x8d, 0x95, 0x71, 0x8a, 0xbb, 0x22, 0xd7, 0x47, 0xec, 0xd8, 0x3d, 0xdc, 0x4b},
|
||||
},
|
||||
Cookie: []byte{0xe6, 0x14, 0x3a, 0x1b, 0x04, 0xea, 0x9e, 0x7a, 0x14, 0xd6, 0x6c, 0x57, 0xd0, 0x0e, 0x32, 0x85, 0x76, 0x18, 0xde, 0xd8},
|
||||
CipherSuiteIDs: []uint16{
|
||||
0xc02b,
|
||||
0xc00a,
|
||||
},
|
||||
CompressionMethods: []*protocol.CompressionMethod{
|
||||
{},
|
||||
},
|
||||
Extensions: []extension.Extension{
|
||||
&extension.SupportedEllipticCurves{EllipticCurves: []elliptic.Curve{elliptic.X25519}},
|
||||
},
|
||||
}
|
||||
|
||||
c := &MessageClientHello{}
|
||||
if err := c.Unmarshal(rawClientHello); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(c, parsedClientHello) {
|
||||
t.Errorf("handshakeMessageClientHello unmarshal: got %#v, want %#v", c, parsedClientHello)
|
||||
}
|
||||
|
||||
raw, err := c.Marshal()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(raw, rawClientHello) {
|
||||
t.Errorf("handshakeMessageClientHello marshal: got %#v, want %#v", raw, rawClientHello)
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHandshakeMessageClientKeyExchange(t *testing.T) {
|
||||
rawClientKeyExchange := []byte{
|
||||
0x20, 0x26, 0x78, 0x4a, 0x78, 0x70, 0xc1, 0xf9, 0x71, 0xea, 0x50, 0x4a, 0xb5, 0xbb, 0x00, 0x76,
|
||||
0x02, 0x05, 0xda, 0xf7, 0xd0, 0x3f, 0xe3, 0xf7, 0x4e, 0x8a, 0x14, 0x6f, 0xb7, 0xe0, 0xc0, 0xff,
|
||||
0x54,
|
||||
}
|
||||
parsedClientKeyExchange := &MessageClientKeyExchange{
|
||||
PublicKey: rawClientKeyExchange[1:],
|
||||
}
|
||||
|
||||
c := &MessageClientKeyExchange{}
|
||||
if err := c.Unmarshal(rawClientKeyExchange); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(c, parsedClientKeyExchange) {
|
||||
t.Errorf("handshakeMessageClientKeyExchange unmarshal: got %#v, want %#v", c, parsedClientKeyExchange)
|
||||
}
|
||||
|
||||
raw, err := c.Marshal()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(raw, rawClientKeyExchange) {
|
||||
t.Errorf("handshakeMessageClientKeyExchange marshal: got %#v, want %#v", raw, rawClientKeyExchange)
|
||||
}
|
||||
}
|
27
dtls-2.0.9/pkg/protocol/handshake/message_finished.go
Normal file
27
dtls-2.0.9/pkg/protocol/handshake/message_finished.go
Normal 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
|
||||
}
|
29
dtls-2.0.9/pkg/protocol/handshake/message_finished_test.go
Normal file
29
dtls-2.0.9/pkg/protocol/handshake/message_finished_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHandshakeMessageFinished(t *testing.T) {
|
||||
rawFinished := []byte{
|
||||
0x01, 0x01, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
}
|
||||
parsedFinished := &MessageFinished{
|
||||
VerifyData: rawFinished,
|
||||
}
|
||||
|
||||
c := &MessageFinished{}
|
||||
if err := c.Unmarshal(rawFinished); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(c, parsedFinished) {
|
||||
t.Errorf("handshakeMessageFinished unmarshal: got %#v, want %#v", c, parsedFinished)
|
||||
}
|
||||
|
||||
raw, err := c.Marshal()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(raw, rawFinished) {
|
||||
t.Errorf("handshakeMessageFinished marshal: got %#v, want %#v", raw, rawFinished)
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/protocol"
|
||||
)
|
||||
|
||||
func TestHandshakeMessageHelloVerifyRequest(t *testing.T) {
|
||||
rawHelloVerifyRequest := []byte{
|
||||
0xfe, 0xff, 0x14, 0x25, 0xfb, 0xee, 0xb3, 0x7c, 0x95, 0xcf, 0x00,
|
||||
0xeb, 0xad, 0xe2, 0xef, 0xc7, 0xfd, 0xbb, 0xed, 0xf7, 0x1f, 0x6c, 0xcd,
|
||||
}
|
||||
parsedHelloVerifyRequest := &MessageHelloVerifyRequest{
|
||||
Version: protocol.Version{Major: 0xFE, Minor: 0xFF},
|
||||
Cookie: []byte{0x25, 0xfb, 0xee, 0xb3, 0x7c, 0x95, 0xcf, 0x00, 0xeb, 0xad, 0xe2, 0xef, 0xc7, 0xfd, 0xbb, 0xed, 0xf7, 0x1f, 0x6c, 0xcd},
|
||||
}
|
||||
|
||||
h := &MessageHelloVerifyRequest{}
|
||||
if err := h.Unmarshal(rawHelloVerifyRequest); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(h, parsedHelloVerifyRequest) {
|
||||
t.Errorf("handshakeMessageClientHello unmarshal: got %#v, want %#v", h, parsedHelloVerifyRequest)
|
||||
}
|
||||
|
||||
raw, err := h.Marshal()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(raw, rawHelloVerifyRequest) {
|
||||
t.Errorf("handshakeMessageClientHello marshal: got %#v, want %#v", raw, rawHelloVerifyRequest)
|
||||
}
|
||||
}
|
111
dtls-2.0.9/pkg/protocol/handshake/message_server_hello.go
Normal file
111
dtls-2.0.9/pkg/protocol/handshake/message_server_hello.go
Normal file
@@ -0,0 +1,111 @@
|
||||
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 // TODO 添加anylink支持
|
||||
|
||||
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, 0x00) // SessionID
|
||||
// TODO 添加SessionID
|
||||
out = append(out, byte(len(m.SessionID))) // 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 += int(data[currOffset]) + 1 // SessionID
|
||||
if len(data) < (currOffset + 2) {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
|
||||
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
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
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
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHandshakeMessageServerHelloDone(t *testing.T) {
|
||||
rawServerHelloDone := []byte{}
|
||||
parsedServerHelloDone := &MessageServerHelloDone{}
|
||||
|
||||
c := &MessageServerHelloDone{}
|
||||
if err := c.Unmarshal(rawServerHelloDone); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(c, parsedServerHelloDone) {
|
||||
t.Errorf("handshakeMessageServerHelloDone unmarshal: got %#v, want %#v", c, parsedServerHelloDone)
|
||||
}
|
||||
|
||||
raw, err := c.Marshal()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(raw, rawServerHelloDone) {
|
||||
t.Errorf("handshakeMessageServerHelloDone marshal: got %#v, want %#v", raw, rawServerHelloDone)
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/protocol"
|
||||
"github.com/pion/dtls/v2/pkg/protocol/extension"
|
||||
)
|
||||
|
||||
func TestHandshakeMessageServerHello(t *testing.T) {
|
||||
rawServerHello := []byte{
|
||||
0xfe, 0xfd, 0x21, 0x63, 0x32, 0x21, 0x81, 0x0e, 0x98, 0x6c,
|
||||
0x85, 0x3d, 0xa4, 0x39, 0xaf, 0x5f, 0xd6, 0x5c, 0xcc, 0x20,
|
||||
0x7f, 0x7c, 0x78, 0xf1, 0x5f, 0x7e, 0x1c, 0xb7, 0xa1, 0x1e,
|
||||
0xcf, 0x63, 0x84, 0x28, 0x00, 0xc0, 0x2b, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
cipherSuiteID := uint16(0xc02b)
|
||||
|
||||
parsedServerHello := &MessageServerHello{
|
||||
Version: protocol.Version{Major: 0xFE, Minor: 0xFD},
|
||||
Random: Random{
|
||||
GMTUnixTime: time.Unix(560149025, 0),
|
||||
RandomBytes: [28]byte{0x81, 0x0e, 0x98, 0x6c, 0x85, 0x3d, 0xa4, 0x39, 0xaf, 0x5f, 0xd6, 0x5c, 0xcc, 0x20, 0x7f, 0x7c, 0x78, 0xf1, 0x5f, 0x7e, 0x1c, 0xb7, 0xa1, 0x1e, 0xcf, 0x63, 0x84, 0x28},
|
||||
},
|
||||
CipherSuiteID: &cipherSuiteID,
|
||||
CompressionMethod: &protocol.CompressionMethod{},
|
||||
Extensions: []extension.Extension{},
|
||||
}
|
||||
|
||||
c := &MessageServerHello{}
|
||||
if err := c.Unmarshal(rawServerHello); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(c, parsedServerHello) {
|
||||
t.Errorf("handshakeMessageServerHello unmarshal: got %#v, want %#v", c, parsedServerHello)
|
||||
}
|
||||
|
||||
raw, err := c.Marshal()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(raw, rawServerHello) {
|
||||
t.Errorf("handshakeMessageServerHello marshal: got %#v, want %#v", raw, rawServerHello)
|
||||
}
|
||||
}
|
119
dtls-2.0.9/pkg/protocol/handshake/message_server_key_exchange.go
Normal file
119
dtls-2.0.9/pkg/protocol/handshake/message_server_key_exchange.go
Normal 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
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/crypto/elliptic"
|
||||
"github.com/pion/dtls/v2/pkg/crypto/hash"
|
||||
"github.com/pion/dtls/v2/pkg/crypto/signature"
|
||||
)
|
||||
|
||||
func TestHandshakeMessageServerKeyExchange(t *testing.T) {
|
||||
test := func(rawServerKeyExchange []byte, parsedServerKeyExchange *MessageServerKeyExchange) {
|
||||
c := &MessageServerKeyExchange{}
|
||||
if err := c.Unmarshal(rawServerKeyExchange); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(c, parsedServerKeyExchange) {
|
||||
t.Errorf("handshakeMessageServerKeyExchange unmarshal: got %#v, want %#v", c, parsedServerKeyExchange)
|
||||
}
|
||||
|
||||
raw, err := c.Marshal()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(raw, rawServerKeyExchange) {
|
||||
t.Errorf("handshakeMessageServerKeyExchange marshal: got %#v, want %#v", raw, rawServerKeyExchange)
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("Hash+Signature", func(t *testing.T) {
|
||||
rawServerKeyExchange := []byte{
|
||||
0x03, 0x00, 0x1d, 0x41, 0x04, 0x0c, 0xb9, 0xa3, 0xb9, 0x90, 0x71, 0x35, 0x4a, 0x08, 0x66, 0xaf,
|
||||
0xd6, 0x88, 0x58, 0x29, 0x69, 0x98, 0xf1, 0x87, 0x0f, 0xb5, 0xa8, 0xcd, 0x92, 0xf6, 0x2b, 0x08,
|
||||
0x0c, 0xd4, 0x16, 0x5b, 0xcc, 0x81, 0xf2, 0x58, 0x91, 0x8e, 0x62, 0xdf, 0xc1, 0xec, 0x72, 0xe8,
|
||||
0x47, 0x24, 0x42, 0x96, 0xb8, 0x7b, 0xee, 0xe7, 0x0d, 0xdc, 0x44, 0xec, 0xf3, 0x97, 0x6b, 0x1b,
|
||||
0x45, 0x28, 0xac, 0x3f, 0x35, 0x02, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xb2, 0x0b,
|
||||
0x22, 0x95, 0x3d, 0x56, 0x57, 0x6a, 0x3f, 0x85, 0x30, 0x6f, 0x55, 0xc3, 0xf4, 0x24, 0x1b, 0x21,
|
||||
0x07, 0xe5, 0xdf, 0xba, 0x24, 0x02, 0x68, 0x95, 0x1f, 0x6e, 0x13, 0xbd, 0x9f, 0xaa, 0x02, 0x20,
|
||||
0x49, 0x9c, 0x9d, 0xdf, 0x84, 0x60, 0x33, 0x27, 0x96, 0x9e, 0x58, 0x6d, 0x72, 0x13, 0xe7, 0x3a,
|
||||
0xe8, 0xdf, 0x43, 0x75, 0xc7, 0xb9, 0x37, 0x6e, 0x90, 0xe5, 0x3b, 0x81, 0xd4, 0xda, 0x68, 0xcd,
|
||||
}
|
||||
parsedServerKeyExchange := &MessageServerKeyExchange{
|
||||
EllipticCurveType: elliptic.CurveTypeNamedCurve,
|
||||
NamedCurve: elliptic.X25519,
|
||||
PublicKey: rawServerKeyExchange[4:69],
|
||||
HashAlgorithm: hash.SHA1,
|
||||
SignatureAlgorithm: signature.ECDSA,
|
||||
Signature: rawServerKeyExchange[73:144],
|
||||
}
|
||||
|
||||
test(rawServerKeyExchange, parsedServerKeyExchange)
|
||||
})
|
||||
|
||||
t.Run("Anonymous", func(t *testing.T) {
|
||||
rawServerKeyExchange := []byte{
|
||||
0x03, 0x00, 0x1d, 0x41, 0x04, 0x0c, 0xb9, 0xa3, 0xb9, 0x90, 0x71, 0x35, 0x4a, 0x08, 0x66, 0xaf,
|
||||
0xd6, 0x88, 0x58, 0x29, 0x69, 0x98, 0xf1, 0x87, 0x0f, 0xb5, 0xa8, 0xcd, 0x92, 0xf6, 0x2b, 0x08,
|
||||
0x0c, 0xd4, 0x16, 0x5b, 0xcc, 0x81, 0xf2, 0x58, 0x91, 0x8e, 0x62, 0xdf, 0xc1, 0xec, 0x72, 0xe8,
|
||||
0x47, 0x24, 0x42, 0x96, 0xb8, 0x7b, 0xee, 0xe7, 0x0d, 0xdc, 0x44, 0xec, 0xf3, 0x97, 0x6b, 0x1b,
|
||||
0x45, 0x28, 0xac, 0x3f, 0x35,
|
||||
}
|
||||
parsedServerKeyExchange := &MessageServerKeyExchange{
|
||||
EllipticCurveType: elliptic.CurveTypeNamedCurve,
|
||||
NamedCurve: elliptic.X25519,
|
||||
PublicKey: rawServerKeyExchange[4:69],
|
||||
HashAlgorithm: hash.None,
|
||||
SignatureAlgorithm: signature.Anonymous,
|
||||
}
|
||||
|
||||
test(rawServerKeyExchange, parsedServerKeyExchange)
|
||||
})
|
||||
}
|
49
dtls-2.0.9/pkg/protocol/handshake/random.go
Normal file
49
dtls-2.0.9/pkg/protocol/handshake/random.go
Normal 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
|
||||
}
|
16
dtls-2.0.9/pkg/protocol/recordlayer/errors.go
Normal file
16
dtls-2.0.9/pkg/protocol/recordlayer/errors.go
Normal file
@@ -0,0 +1,16 @@
|
||||
// Package recordlayer implements the TLS Record Layer https://tools.ietf.org/html/rfc5246#section-6
|
||||
package recordlayer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/protocol"
|
||||
)
|
||||
|
||||
var (
|
||||
errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
|
||||
errInvalidPacketLength = &protocol.TemporaryError{Err: errors.New("packet length and declared length do not match")} //nolint:goerr113
|
||||
errSequenceNumberOverflow = &protocol.InternalError{Err: errors.New("sequence number overflow")} //nolint:goerr113
|
||||
errUnsupportedProtocolVersion = &protocol.FatalError{Err: errors.New("unsupported protocol version")} //nolint:goerr113
|
||||
errInvalidContentType = &protocol.TemporaryError{Err: errors.New("invalid content type")} //nolint:goerr113
|
||||
)
|
61
dtls-2.0.9/pkg/protocol/recordlayer/header.go
Normal file
61
dtls-2.0.9/pkg/protocol/recordlayer/header.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package recordlayer
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/pion/dtls/v2/internal/util"
|
||||
"github.com/pion/dtls/v2/pkg/protocol"
|
||||
)
|
||||
|
||||
// Header implements a TLS RecordLayer header
|
||||
type Header struct {
|
||||
ContentType protocol.ContentType
|
||||
ContentLen uint16
|
||||
Version protocol.Version
|
||||
Epoch uint16
|
||||
SequenceNumber uint64 // uint48 in spec
|
||||
}
|
||||
|
||||
// RecordLayer enums
|
||||
const (
|
||||
HeaderSize = 13
|
||||
MaxSequenceNumber = 0x0000FFFFFFFFFFFF
|
||||
)
|
||||
|
||||
// Marshal encodes a TLS RecordLayer Header to binary
|
||||
func (h *Header) Marshal() ([]byte, error) {
|
||||
if h.SequenceNumber > MaxSequenceNumber {
|
||||
return nil, errSequenceNumberOverflow
|
||||
}
|
||||
|
||||
out := make([]byte, HeaderSize)
|
||||
out[0] = byte(h.ContentType)
|
||||
out[1] = h.Version.Major
|
||||
out[2] = h.Version.Minor
|
||||
binary.BigEndian.PutUint16(out[3:], h.Epoch)
|
||||
util.PutBigEndianUint48(out[5:], h.SequenceNumber)
|
||||
binary.BigEndian.PutUint16(out[HeaderSize-2:], h.ContentLen)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Unmarshal populates a TLS RecordLayer Header from binary
|
||||
func (h *Header) Unmarshal(data []byte) error {
|
||||
if len(data) < HeaderSize {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
h.ContentType = protocol.ContentType(data[0])
|
||||
h.Version.Major = data[1]
|
||||
h.Version.Minor = data[2]
|
||||
h.Epoch = binary.BigEndian.Uint16(data[3:])
|
||||
|
||||
// SequenceNumber is stored as uint48, make into uint64
|
||||
seqCopy := make([]byte, 8)
|
||||
copy(seqCopy[2:], data[5:11])
|
||||
h.SequenceNumber = binary.BigEndian.Uint64(seqCopy)
|
||||
|
||||
if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) {
|
||||
return errUnsupportedProtocolVersion
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
99
dtls-2.0.9/pkg/protocol/recordlayer/recordlayer.go
Normal file
99
dtls-2.0.9/pkg/protocol/recordlayer/recordlayer.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package recordlayer
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/protocol"
|
||||
"github.com/pion/dtls/v2/pkg/protocol/alert"
|
||||
"github.com/pion/dtls/v2/pkg/protocol/handshake"
|
||||
)
|
||||
|
||||
// RecordLayer which handles all data transport.
|
||||
// The record layer is assumed to sit directly on top of some
|
||||
// reliable transport such as TCP. The record layer can carry four types of content:
|
||||
//
|
||||
// 1. Handshake messages—used for algorithm negotiation and key establishment.
|
||||
// 2. ChangeCipherSpec messages—really part of the handshake but technically a separate kind of message.
|
||||
// 3. Alert messages—used to signal that errors have occurred
|
||||
// 4. Application layer data
|
||||
//
|
||||
// The DTLS record layer is extremely similar to that of TLS 1.1. The
|
||||
// only change is the inclusion of an explicit sequence number in the
|
||||
// record. This sequence number allows the recipient to correctly
|
||||
// verify the TLS MAC.
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc4347#section-4.1
|
||||
type RecordLayer struct {
|
||||
Header Header
|
||||
Content protocol.Content
|
||||
}
|
||||
|
||||
// Marshal encodes the RecordLayer to binary
|
||||
func (r *RecordLayer) Marshal() ([]byte, error) {
|
||||
contentRaw, err := r.Content.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.Header.ContentLen = uint16(len(contentRaw))
|
||||
r.Header.ContentType = r.Content.ContentType()
|
||||
|
||||
headerRaw, err := r.Header.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(headerRaw, contentRaw...), nil
|
||||
}
|
||||
|
||||
// Unmarshal populates the RecordLayer from binary
|
||||
func (r *RecordLayer) Unmarshal(data []byte) error {
|
||||
if len(data) < HeaderSize {
|
||||
return errBufferTooSmall
|
||||
}
|
||||
if err := r.Header.Unmarshal(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch protocol.ContentType(data[0]) {
|
||||
case protocol.ContentTypeChangeCipherSpec:
|
||||
r.Content = &protocol.ChangeCipherSpec{}
|
||||
case protocol.ContentTypeAlert:
|
||||
r.Content = &alert.Alert{}
|
||||
case protocol.ContentTypeHandshake:
|
||||
r.Content = &handshake.Handshake{}
|
||||
case protocol.ContentTypeApplicationData:
|
||||
r.Content = &protocol.ApplicationData{}
|
||||
default:
|
||||
return errInvalidContentType
|
||||
}
|
||||
|
||||
return r.Content.Unmarshal(data[HeaderSize:])
|
||||
}
|
||||
|
||||
// UnpackDatagram extracts all RecordLayer messages from a single datagram.
|
||||
// Note that as with TLS, multiple handshake messages may be placed in
|
||||
// the same DTLS record, provided that there is room and that they are
|
||||
// part of the same flight. Thus, there are two acceptable ways to pack
|
||||
// two DTLS messages into the same datagram: in the same record or in
|
||||
// separate records.
|
||||
// https://tools.ietf.org/html/rfc6347#section-4.2.3
|
||||
func UnpackDatagram(buf []byte) ([][]byte, error) {
|
||||
out := [][]byte{}
|
||||
|
||||
for offset := 0; len(buf) != offset; {
|
||||
if len(buf)-offset <= HeaderSize {
|
||||
return nil, errInvalidPacketLength
|
||||
}
|
||||
|
||||
pktLen := (HeaderSize + int(binary.BigEndian.Uint16(buf[offset+11:])))
|
||||
if offset+pktLen > len(buf) {
|
||||
return nil, errInvalidPacketLength
|
||||
}
|
||||
|
||||
out = append(out, buf[offset:offset+pktLen])
|
||||
offset += pktLen
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
92
dtls-2.0.9/pkg/protocol/recordlayer/recordlayer_test.go
Normal file
92
dtls-2.0.9/pkg/protocol/recordlayer/recordlayer_test.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package recordlayer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/protocol"
|
||||
)
|
||||
|
||||
func TestUDPDecode(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
Name string
|
||||
Data []byte
|
||||
Want [][]byte
|
||||
WantError error
|
||||
}{
|
||||
{
|
||||
Name: "Change Cipher Spec, single packet",
|
||||
Data: []byte{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01},
|
||||
Want: [][]byte{
|
||||
{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Change Cipher Spec, multi packet",
|
||||
Data: []byte{
|
||||
0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01,
|
||||
0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, 0x01,
|
||||
},
|
||||
Want: [][]byte{
|
||||
{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01},
|
||||
{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, 0x01},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Invalid packet length",
|
||||
Data: []byte{0x14, 0xfe},
|
||||
WantError: errInvalidPacketLength,
|
||||
},
|
||||
{
|
||||
Name: "Packet declared invalid length",
|
||||
Data: []byte{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xFF, 0x01},
|
||||
WantError: errInvalidPacketLength,
|
||||
},
|
||||
} {
|
||||
dtlsPkts, err := UnpackDatagram(test.Data)
|
||||
if !errors.Is(err, test.WantError) {
|
||||
t.Errorf("Unexpected Error %q: exp: %v got: %v", test.Name, test.WantError, err)
|
||||
} else if !reflect.DeepEqual(test.Want, dtlsPkts) {
|
||||
t.Errorf("%q UDP decode: got %q, want %q", test.Name, dtlsPkts, test.Want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordLayerRoundTrip(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
Name string
|
||||
Data []byte
|
||||
Want *RecordLayer
|
||||
WantMarshalError error
|
||||
WantUnmarshalError error
|
||||
}{
|
||||
{
|
||||
Name: "Change Cipher Spec, single packet",
|
||||
Data: []byte{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01},
|
||||
Want: &RecordLayer{
|
||||
Header: Header{
|
||||
ContentType: protocol.ContentTypeChangeCipherSpec,
|
||||
Version: protocol.Version{Major: 0xfe, Minor: 0xff},
|
||||
Epoch: 0,
|
||||
SequenceNumber: 18,
|
||||
},
|
||||
Content: &protocol.ChangeCipherSpec{},
|
||||
},
|
||||
},
|
||||
} {
|
||||
r := &RecordLayer{}
|
||||
if err := r.Unmarshal(test.Data); !errors.Is(err, test.WantUnmarshalError) {
|
||||
t.Errorf("Unexpected Error %q: exp: %v got: %v", test.Name, test.WantUnmarshalError, err)
|
||||
} else if !reflect.DeepEqual(test.Want, r) {
|
||||
t.Errorf("%q recordLayer.unmarshal: got %q, want %q", test.Name, r, test.Want)
|
||||
}
|
||||
|
||||
data, marshalErr := r.Marshal()
|
||||
if !errors.Is(marshalErr, test.WantMarshalError) {
|
||||
t.Errorf("Unexpected Error %q: exp: %v got: %v", test.Name, test.WantMarshalError, marshalErr)
|
||||
} else if !reflect.DeepEqual(test.Data, data) {
|
||||
t.Errorf("%q recordLayer.marshal: got % 02x, want % 02x", test.Name, data, test.Data)
|
||||
}
|
||||
}
|
||||
}
|
21
dtls-2.0.9/pkg/protocol/version.go
Normal file
21
dtls-2.0.9/pkg/protocol/version.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Package protocol provides the DTLS wire format
|
||||
package protocol
|
||||
|
||||
// Version enums
|
||||
var (
|
||||
Version1_0 = Version{Major: 0xfe, Minor: 0xff} //nolint:gochecknoglobals
|
||||
Version1_2 = Version{Major: 0xfe, Minor: 0xfd} //nolint:gochecknoglobals
|
||||
)
|
||||
|
||||
// Version is the minor/major value in the RecordLayer
|
||||
// and ClientHello/ServerHello
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc4346#section-6.2.1
|
||||
type Version struct {
|
||||
Major, Minor uint8
|
||||
}
|
||||
|
||||
// Equal determines if two protocol versions are equal
|
||||
func (v Version) Equal(x Version) bool {
|
||||
return v.Major == x.Major && v.Minor == x.Minor
|
||||
}
|
Reference in New Issue
Block a user