Skip to content

Commit 46ee7c3

Browse files
authored
Refactor Common AEAD Code in Ciphersuite Package (#789)
Signed-off-by: Adriano Sela Aviles <adriano.selaviles@gmail.com>
1 parent 10bd10a commit 46ee7c3

8 files changed

Lines changed: 256 additions & 403 deletions

File tree

pkg/crypto/ciphersuite/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ go test -tags=bench -bench=BenchmarkCBCEncrypt -benchmem
7070
go test -tags=bench -bench=BenchmarkCBCDecrypt -benchmem
7171
```
7272

73-
- All cipgers, with 1KB payloads only
73+
- All ciphers, with 1KB payloads only
7474

7575
```bash
7676
go test -tags=bench -bench=/1KB -benchmem
7777
```
7878

79-
- All cipgers, with 16B payloads only
79+
- All ciphers, with 16B payloads only
8080

8181
```bash
8282
go test -tags=bench -bench=/16B -benchmem

pkg/crypto/ciphersuite/bench_helpers_test.go

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,109 @@
55

66
package ciphersuite
77

8-
import "testing"
8+
import (
9+
"testing"
10+
11+
"github.com/pion/dtls/v3/pkg/protocol"
12+
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
13+
)
14+
15+
type testCipher interface {
16+
Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error)
17+
Decrypt(header recordlayer.Header, in []byte) ([]byte, error)
18+
}
19+
20+
// benchmarkEncrypt benchmarks a cipher's encryption with various payload sizes.
21+
func benchmarkEncrypt(b *testing.B, cipher testCipher) {
22+
b.Helper()
23+
24+
payloadSizes := []int{16, 64, 128, 256, 512, 800, 1024, 1200, 1500, 4096, 8192}
25+
26+
for _, size := range payloadSizes {
27+
b.Run(formatSize(b, size), func(b *testing.B) {
28+
hdr := recordlayer.Header{
29+
ContentType: protocol.ContentTypeApplicationData,
30+
Version: protocol.Version1_2,
31+
Epoch: 1,
32+
SequenceNumber: 12345,
33+
}
34+
35+
headerRaw, err := hdr.Marshal()
36+
if err != nil {
37+
b.Fatal(err)
38+
}
39+
40+
payload := make([]byte, size)
41+
raw := make([]byte, len(headerRaw)+len(payload))
42+
copy(raw, headerRaw)
43+
copy(raw[len(headerRaw):], payload)
44+
45+
pkt := &recordlayer.RecordLayer{Header: hdr}
46+
47+
b.ReportAllocs()
48+
b.SetBytes(int64(size))
49+
b.ResetTimer()
50+
51+
for i := 0; i < b.N; i++ {
52+
rawCopy := make([]byte, len(raw))
53+
copy(rawCopy, raw)
54+
55+
_, err := cipher.Encrypt(pkt, rawCopy)
56+
if err != nil {
57+
b.Fatal(err)
58+
}
59+
}
60+
})
61+
}
62+
}
63+
64+
// benchmarkDecrypt benchmarks a cipher's decryption with various payload sizes.
65+
func benchmarkDecrypt(b *testing.B, cipher testCipher) {
66+
b.Helper()
67+
68+
payloadSizes := []int{16, 64, 256, 512, 1024, 1500}
69+
70+
for _, size := range payloadSizes {
71+
b.Run(formatSize(b, size), func(b *testing.B) {
72+
hdr := recordlayer.Header{
73+
ContentType: protocol.ContentTypeApplicationData,
74+
Version: protocol.Version1_2,
75+
Epoch: 1,
76+
SequenceNumber: 12345,
77+
}
78+
79+
headerRaw, err := hdr.Marshal()
80+
if err != nil {
81+
b.Fatal(err)
82+
}
83+
84+
payload := make([]byte, size)
85+
raw := make([]byte, len(headerRaw)+len(payload))
86+
copy(raw, headerRaw)
87+
copy(raw[len(headerRaw):], payload)
88+
89+
pkt := &recordlayer.RecordLayer{Header: hdr}
90+
encrypted, err := cipher.Encrypt(pkt, raw)
91+
if err != nil {
92+
b.Fatal(err)
93+
}
94+
95+
b.ReportAllocs()
96+
b.SetBytes(int64(size))
97+
b.ResetTimer()
98+
99+
for i := 0; i < b.N; i++ {
100+
encCopy := make([]byte, len(encrypted))
101+
copy(encCopy, encrypted)
102+
103+
_, err := cipher.Decrypt(hdr, encCopy)
104+
if err != nil {
105+
b.Fatal(err)
106+
}
107+
}
108+
})
109+
}
110+
}
9111

10112
func formatSize(b *testing.B, size int) string {
11113
b.Helper()

pkg/crypto/ciphersuite/cbc_bench_test.go

Lines changed: 2 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ package ciphersuite
88
import (
99
"crypto/sha256"
1010
"testing"
11-
12-
"github.com/pion/dtls/v3/pkg/protocol"
13-
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
1411
)
1512

1613
// BenchmarkCBCEncrypt benchmarks CBC encryption with various payload sizes.
@@ -26,45 +23,7 @@ func BenchmarkCBCEncrypt(b *testing.B) {
2623
b.Fatal(err)
2724
}
2825

29-
payloadSizes := []int{16, 64, 256, 512, 1024, 1500}
30-
31-
// nolint:dupl
32-
for _, size := range payloadSizes {
33-
b.Run(formatSize(b, size), func(b *testing.B) {
34-
hdr := recordlayer.Header{
35-
ContentType: protocol.ContentTypeApplicationData,
36-
Version: protocol.Version1_2,
37-
Epoch: 1,
38-
SequenceNumber: 12345,
39-
}
40-
41-
headerRaw, err := hdr.Marshal()
42-
if err != nil {
43-
b.Fatal(err)
44-
}
45-
46-
payload := make([]byte, size)
47-
raw := make([]byte, len(headerRaw)+len(payload))
48-
copy(raw, headerRaw)
49-
copy(raw[len(headerRaw):], payload)
50-
51-
pkt := &recordlayer.RecordLayer{Header: hdr}
52-
53-
b.ReportAllocs()
54-
b.SetBytes(int64(size))
55-
b.ResetTimer()
56-
57-
for i := 0; i < b.N; i++ {
58-
rawCopy := make([]byte, len(raw))
59-
copy(rawCopy, raw)
60-
61-
_, err := cbcCipher.Encrypt(pkt, rawCopy)
62-
if err != nil {
63-
b.Fatal(err)
64-
}
65-
}
66-
})
67-
}
26+
benchmarkEncrypt(b, cbcCipher)
6827
}
6928

7029
// BenchmarkCBCDecrypt benchmarks CBC decryption with various payload sizes.
@@ -80,47 +39,5 @@ func BenchmarkCBCDecrypt(b *testing.B) {
8039
b.Fatal(err)
8140
}
8241

83-
payloadSizes := []int{16, 64, 256, 512, 1024, 1500}
84-
85-
// nolint:dupl
86-
for _, size := range payloadSizes {
87-
b.Run(formatSize(b, size), func(b *testing.B) {
88-
hdr := recordlayer.Header{
89-
ContentType: protocol.ContentTypeApplicationData,
90-
Version: protocol.Version1_2,
91-
Epoch: 1,
92-
SequenceNumber: 12345,
93-
}
94-
95-
headerRaw, err := hdr.Marshal()
96-
if err != nil {
97-
b.Fatal(err)
98-
}
99-
100-
payload := make([]byte, size)
101-
raw := make([]byte, len(headerRaw)+len(payload))
102-
copy(raw, headerRaw)
103-
copy(raw[len(headerRaw):], payload)
104-
105-
pkt := &recordlayer.RecordLayer{Header: hdr}
106-
encrypted, err := cbcCipher.Encrypt(pkt, raw)
107-
if err != nil {
108-
b.Fatal(err)
109-
}
110-
111-
b.ReportAllocs()
112-
b.SetBytes(int64(size))
113-
b.ResetTimer()
114-
115-
for i := 0; i < b.N; i++ {
116-
encCopy := make([]byte, len(encrypted))
117-
copy(encCopy, encrypted)
118-
119-
_, err := cbcCipher.Decrypt(hdr, encCopy)
120-
if err != nil {
121-
b.Fatal(err)
122-
}
123-
}
124-
})
125-
}
42+
benchmarkDecrypt(b, cbcCipher)
12643
}

pkg/crypto/ciphersuite/ccm.go

Lines changed: 11 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,8 @@ package ciphersuite
55

66
import (
77
"crypto/aes"
8-
"crypto/rand"
9-
"encoding/binary"
10-
"fmt"
11-
"sync"
128

139
"github.com/pion/dtls/v3/pkg/crypto/ccm"
14-
"github.com/pion/dtls/v3/pkg/protocol"
1510
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
1611
)
1712

@@ -27,12 +22,7 @@ const (
2722

2823
// CCM Provides an API to Encrypt/Decrypt DTLS 1.2 Packets.
2924
type CCM struct {
30-
localCCM, remoteCCM ccm.CCM
31-
localWriteIV, remoteWriteIV []byte
32-
tagLen CCMTagLen
33-
34-
// buffer pool for nonces.
35-
nonceBufferPool sync.Pool
25+
aead *aead
3626
}
3727

3828
// NewCCM creates a DTLS GCM Cipher.
@@ -56,82 +46,23 @@ func NewCCM(tagLen CCMTagLen, localKey, localWriteIV, remoteKey, remoteWriteIV [
5646
}
5747

5848
return &CCM{
59-
localCCM: localCCM,
60-
localWriteIV: localWriteIV,
61-
remoteCCM: remoteCCM,
62-
remoteWriteIV: remoteWriteIV,
63-
tagLen: tagLen,
64-
65-
nonceBufferPool: sync.Pool{
66-
New: func() any {
67-
b := make([]byte, ccmNonceLength)
68-
return &b // nolint:nlreturn
69-
},
70-
},
49+
aead: newAEAD(
50+
localCCM,
51+
localWriteIV,
52+
remoteCCM,
53+
remoteWriteIV,
54+
ccmNonceLength,
55+
int(tagLen),
56+
),
7157
}, nil
7258
}
7359

7460
// Encrypt encrypt a DTLS RecordLayer message.
7561
func (c *CCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
76-
payload := raw[pkt.Header.Size():]
77-
raw = raw[:pkt.Header.Size()]
78-
79-
noncePtr := c.nonceBufferPool.Get().(*[]byte) // nolint:forcetypeassert
80-
nonce := *noncePtr
81-
defer c.nonceBufferPool.Put(noncePtr)
82-
83-
copy(nonce[:4], c.localWriteIV[:4])
84-
if _, err := rand.Read(nonce[4:]); err != nil {
85-
return nil, err
86-
}
87-
88-
var additionalData []byte
89-
if pkt.Header.ContentType == protocol.ContentTypeConnectionID {
90-
additionalData = generateAEADAdditionalDataCID(&pkt.Header, len(payload))
91-
} else {
92-
additionalData = generateAEADAdditionalData(&pkt.Header, len(payload))
93-
}
94-
95-
finalSize := len(raw) + 8 + len(payload) + int(c.tagLen)
96-
result := make([]byte, finalSize)
97-
copy(result, raw)
98-
copy(result[len(raw):], nonce[4:])
99-
100-
c.localCCM.Seal(result[len(raw)+8:len(raw)+8], nonce, payload, additionalData)
101-
102-
// Update recordLayer size to include explicit nonce
103-
binary.BigEndian.PutUint16(result[pkt.Header.Size()-2:], uint16(len(result)-pkt.Header.Size())) //nolint:gosec //G115
104-
105-
return result, nil
62+
return c.aead.encrypt(pkt, raw)
10663
}
10764

10865
// Decrypt decrypts a DTLS RecordLayer message.
10966
func (c *CCM) Decrypt(header recordlayer.Header, in []byte) ([]byte, error) {
110-
if err := header.Unmarshal(in); err != nil {
111-
return nil, err
112-
}
113-
switch {
114-
case header.ContentType == protocol.ContentTypeChangeCipherSpec:
115-
// Nothing to encrypt with ChangeCipherSpec
116-
return in, nil
117-
case len(in) <= (8 + header.Size()):
118-
return nil, errNotEnoughRoomForNonce
119-
}
120-
121-
nonce := append(append([]byte{}, c.remoteWriteIV[:4]...), in[header.Size():header.Size()+8]...)
122-
out := in[header.Size()+8:]
123-
124-
var additionalData []byte
125-
if header.ContentType == protocol.ContentTypeConnectionID {
126-
additionalData = generateAEADAdditionalDataCID(&header, len(out)-int(c.tagLen))
127-
} else {
128-
additionalData = generateAEADAdditionalData(&header, len(out)-int(c.tagLen))
129-
}
130-
var err error
131-
out, err = c.remoteCCM.Open(out[:0], nonce, out, additionalData)
132-
if err != nil {
133-
return nil, fmt.Errorf("%w: %v", errDecryptPacket, err) //nolint:errorlint
134-
}
135-
136-
return append(in[:header.Size()], out...), nil
67+
return c.aead.decrypt(header, in)
13768
}

0 commit comments

Comments
 (0)