package ciphersuite
import (
"crypto/aes"
"crypto/rand"
"encoding/binary"
"fmt"
"github.com/pion/dtls/v3/pkg/crypto/ccm"
"github.com/pion/dtls/v3/pkg/protocol"
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
)
type CCMTagLen int
const (
CCMTagLength8 CCMTagLen = 8
CCMTagLength CCMTagLen = 16
ccmNonceLength = 12
)
type CCM struct {
localCCM, remoteCCM ccm .CCM
localWriteIV, remoteWriteIV []byte
tagLen CCMTagLen
}
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
}
func (c *CCM ) Encrypt (pkt *recordlayer .RecordLayer , raw []byte ) ([]byte , error ) {
payload := raw [pkt .Header .Size ():]
raw = raw [:pkt .Header .Size ()]
nonce := append (append ([]byte {}, c .localWriteIV [:4 ]...), make ([]byte , 8 )...)
if _ , err := rand .Read (nonce [4 :]); err != nil {
return nil , err
}
var additionalData []byte
if pkt .Header .ContentType == protocol .ContentTypeConnectionID {
additionalData = generateAEADAdditionalDataCID (&pkt .Header , len (payload ))
} else {
additionalData = generateAEADAdditionalData (&pkt .Header , len (payload ))
}
encryptedPayload := c .localCCM .Seal (nil , nonce , payload , additionalData )
encryptedPayload = append (nonce [4 :], encryptedPayload ...)
raw = append (raw , encryptedPayload ...)
binary .BigEndian .PutUint16 (raw [pkt .Header .Size ()-2 :], uint16 (len (raw )-pkt .Header .Size ()))
return raw , nil
}
func (c *CCM ) Decrypt (header recordlayer .Header , in []byte ) ([]byte , error ) {
if err := header .Unmarshal (in ); err != nil {
return nil , err
}
switch {
case header .ContentType == protocol .ContentTypeChangeCipherSpec :
return in , nil
case len (in ) <= (8 + header .Size ()):
return nil , errNotEnoughRoomForNonce
}
nonce := append (append ([]byte {}, c .remoteWriteIV [:4 ]...), in [header .Size ():header .Size ()+8 ]...)
out := in [header .Size ()+8 :]
var additionalData []byte
if header .ContentType == protocol .ContentTypeConnectionID {
additionalData = generateAEADAdditionalDataCID (&header , len (out )-int (c .tagLen ))
} else {
additionalData = generateAEADAdditionalData (&header , len (out )-int (c .tagLen ))
}
var err error
out , err = c .remoteCCM .Open (out [:0 ], nonce , out , additionalData )
if err != nil {
return nil , fmt .Errorf ("%w: %v" , errDecryptPacket , err )
}
return append (in [:header .Size ()], out ...), nil
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .