mirror of https://github.com/bjdgyc/anylink.git
105 lines
2.8 KiB
Go
105 lines
2.8 KiB
Go
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
|
|
}
|