-
Notifications
You must be signed in to change notification settings - Fork 196
Expand file tree
/
Copy pathchacha20poly1305.go
More file actions
126 lines (101 loc) · 3.83 KB
/
Copy pathchacha20poly1305.go
File metadata and controls
126 lines (101 loc) · 3.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package ciphersuite
import (
"crypto/cipher"
"encoding/binary"
"fmt"
"github.com/pion/dtls/v3/pkg/protocol"
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
"golang.org/x/crypto/chacha20poly1305"
)
const (
chachaTagLength = 16
chachaNonceLength = 12
)
// ChaCha20Poly1305 Provides an API to Encrypt/Decrypt DTLS 1.2 Packets.
//
// Per RFC 7905, ChaCha20-Poly1305 nonce is formed by XOR-ing the write_IV with
// the padded 64-bit sequence number (epoch || sequence_number).
type ChaCha20Poly1305 struct {
localCipher cipher.AEAD
remoteCipher cipher.AEAD
localWriteIV []byte
remoteWriteIV []byte
}
// NewChaCha20Poly1305 creates a DTLS ChaCha20-Poly1305 Cipher.
func NewChaCha20Poly1305(localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*ChaCha20Poly1305, error) {
localChaCha20Poly1305, err := chacha20poly1305.New(localKey)
if err != nil {
return nil, err
}
remoteChaCha20Poly1305, err := chacha20poly1305.New(remoteKey)
if err != nil {
return nil, err
}
return &ChaCha20Poly1305{
localCipher: localChaCha20Poly1305,
remoteCipher: remoteChaCha20Poly1305,
localWriteIV: localWriteIV,
remoteWriteIV: remoteWriteIV,
}, nil
}
// Encrypt encrypts a DTLS RecordLayer message.
func (c *ChaCha20Poly1305) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
payload := raw[pkt.Header.MarshalSize():]
raw = raw[:pkt.Header.MarshalSize()]
var nonce [chachaNonceLength]byte
copy(nonce[:], c.localWriteIV)
// https://www.rfc-editor.org/rfc/rfc9325#name-nonce-reuse-in-tls-12
seq64 := (uint64(pkt.Header.Epoch) << 48) | (pkt.Header.SequenceNumber & 0x0000ffffffffffff)
// XOR the last 8 bytes of the nonce with the sequence number
for i := range 8 {
nonce[4+i] ^= byte(seq64 >> (56 - uint(i)*8)) //nolint:gosec
}
var additionalData []byte
if pkt.Header.ContentType == protocol.ContentTypeConnectionID {
additionalData = generateAEADAdditionalDataCID(&pkt.Header, len(payload))
} else {
additionalData = generateAEADAdditionalData(&pkt.Header, len(payload))
}
// NOTE: ChaCha20-Poly1305 does NOT include an explicit nonce
// in the record (unlike GCM which includes 8 bytes)
encrypted := c.localCipher.Seal(nil, nonce[:], payload, additionalData)
result := make([]byte, len(raw)+len(encrypted))
copy(result, raw)
copy(result[len(raw):], encrypted)
binary.BigEndian.PutUint16(result[pkt.Header.MarshalSize()-2:], uint16(len(encrypted))) //nolint:gosec
return result, nil
}
// Decrypt decrypts a DTLS RecordLayer message.
func (c *ChaCha20Poly1305) Decrypt(header recordlayer.Header, in []byte) ([]byte, error) {
err := header.Unmarshal(in)
switch {
case err != nil:
return nil, err
case header.ContentType == protocol.ContentTypeChangeCipherSpec:
// Nothing to decrypt with ChangeCipherSpec
return in, nil
}
var nonce [chachaNonceLength]byte
copy(nonce[:], c.remoteWriteIV)
// https://www.rfc-editor.org/rfc/rfc9325#name-nonce-reuse-in-tls-12
seq64 := (uint64(header.Epoch) << 48) | (header.SequenceNumber & 0x0000ffffffffffff)
// XOR the last 8 bytes of the nonce with the sequence number
for i := range 8 {
nonce[4+i] ^= byte(seq64 >> (56 - uint(i)*8)) //nolint:gosec
}
// NOTE: ChaCha20-Poly1305 has NO explicit nonce in the record
ciphertext := in[header.MarshalSize():]
var additionalData []byte
if header.ContentType == protocol.ContentTypeConnectionID {
additionalData = generateAEADAdditionalDataCID(&header, len(ciphertext)-chachaTagLength)
} else {
additionalData = generateAEADAdditionalData(&header, len(ciphertext)-chachaTagLength)
}
plaintext, err := c.remoteCipher.Open(nil, nonce[:], ciphertext, additionalData)
if err != nil {
return nil, fmt.Errorf("%w: %v", errDecryptPacket, err) //nolint:errorlint
}
return append(in[:header.MarshalSize()], plaintext...), nil
}