| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
- // SPDX-License-Identifier: MIT
- 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) //nolint:errorlint
- }
- return append(in[:recordlayer.HeaderSize], out...), nil
- }
|