package ciphersuite
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/binary"
"fmt"
"github.com/pion/dtls/v3/pkg/protocol"
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
)
const (
gcmTagLength = 16
gcmNonceLength = 12
)
type GCM struct {
localGCM, remoteGCM cipher .AEAD
localWriteIV, remoteWriteIV []byte
}
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
}
func (g *GCM ) Encrypt (pkt *recordlayer .RecordLayer , raw []byte ) ([]byte , error ) {
payload := raw [pkt .Header .Size ():]
raw = raw [:pkt .Header .Size ()]
nonce := make ([]byte , gcmNonceLength )
copy (nonce , g .localWriteIV [:4 ])
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 := 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 )
binary .BigEndian .PutUint16 (r [pkt .Header .Size ()-2 :], uint16 (len (r )-pkt .Header .Size ()))
return r , nil
}
func (g *GCM ) Decrypt (header recordlayer .Header , in []byte ) ([]byte , error ) {
err := header .Unmarshal (in )
switch {
case err != nil :
return nil , err
case header .ContentType == protocol .ContentTypeChangeCipherSpec :
return in , nil
case len (in ) <= (8 + header .Size ()):
return nil , errNotEnoughRoomForNonce
}
nonce := make ([]byte , 0 , gcmNonceLength )
nonce = append (append (nonce , g .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 )-gcmTagLength )
} else {
additionalData = generateAEADAdditionalData (&header , 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 [: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 .