-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathaegis.go
201 lines (173 loc) · 5.4 KB
/
aegis.go
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
// Package aegis implements the AEGIS AEAD algorithm.
//
// [aegis]: https://www.ietf.org/archive/id/draft-denis-aegis-aead-00.html
//
package aegis
import (
"crypto/cipher"
"errors"
"fmt"
"runtime"
"github.com/ericlagergren/subtle"
)
var errOpen = errors.New("aegis: message authentication failure")
const (
// KeySize128L is the size in bytes of an AEGIS-128L key.
KeySize128L = 16
// NonceSize128L is the size in bytes of an AEGIS-128L nonce.
NonceSize128L = 16
// TagSize128L is the size in bytes of an AEGIS-128L
// authentication tag.
TagSize128L = 16
// BlockSize128L is the size in bytes of an AEGIS-128L block.
BlockSize128L = 32
// MaxPlaintextSize128L is the size in bytes of the largest
// allowed AESGIS-128L plaintext.
MaxPlaintextSize128L = 1 << 61
// MaxAdditionalDataSize128L is the size in bytes of the
// largest allowed AEGIS-128L additional data.
MaxAdditionalDataSize128L = 1 << 61
// ciphertextMax128L is the size in bytes of the largest
// allowed AESGIS-128L ciphertext.
ciphertextMax128L = MaxPlaintextSize128L + TagSize128L
// KeySize256 is the size in bytes of an AEGIS-256 key.
KeySize256 = 32
// NonceSize256 is the size in bytes of an AEGIS-256 nonce.
NonceSize256 = 32
// TagSize256 is the size in bytes of an AEGIS-256
// authentication tag.
TagSize256 = 16
// BlockSize256 is the size in bytes of an AEGIS-256 block.
BlockSize256 = 16
// MaxPlaintextSize256 is the size in bytes of the largest
// allowed AESGIS-256 plaintext.
MaxPlaintextSize256 = 1 << 61
// MaxAdditionalDataSize256 is the size in bytes of the
// largest allowed AEGIS-256 additional data.
MaxAdditionalDataSize256 = 1 << 61
// ciphertextMax256 is the size in bytes of the largest
// allowed AESGIS-256 ciphertext.
ciphertextMax256 = MaxPlaintextSize256 + TagSize256
)
// New creates an instance of the AEGIS AEAD algorithm.
//
// New accepts two key lengths. If the key is 128 bits, New
// returns an instance of AEGIS-128L. Otherwise, if the key is
// 256 bits, New returns an instance of AEGIS-256. Any other key
// lengths are an error.
func New(key []byte) (cipher.AEAD, error) {
switch len(key) {
case KeySize128L:
return &aegis128{key: *(*[KeySize128L]byte)(key)}, nil
case KeySize256:
return &aegis256{key: *(*[KeySize256]byte)(key)}, nil
default:
return nil, fmt.Errorf("invalid key length: %d", len(key))
}
}
type aegis128 struct {
key [KeySize128L]byte
}
func (*aegis128) NonceSize() int {
return NonceSize128L
}
func (*aegis128) Overhead() int {
return TagSize128L
}
func (a *aegis128) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
if uint64(len(plaintext)) > MaxPlaintextSize128L {
panic("aegis: plaintext too large")
}
if len(nonce) != NonceSize128L {
panic("aegis: invalid nonce length")
}
if uint64(len(additionalData)) > MaxAdditionalDataSize128L {
panic("aegis: additional data too large")
}
ret, out := subtle.SliceForAppend(dst, len(plaintext)+TagSize128L)
if subtle.InexactOverlap(out, plaintext) {
panic("aegis: invalid buffer overlap")
}
seal128L(&a.key, (*[NonceSize128L]byte)(nonce),
out, plaintext, additionalData)
return ret
}
func (a *aegis128) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
if len(nonce) != NonceSize128L {
panic("aegis: invalid nonce length")
}
if len(ciphertext) < TagSize128L ||
uint64(len(ciphertext)) > ciphertextMax128L ||
uint64(len(additionalData)) > MaxAdditionalDataSize128L {
return nil, errOpen
}
tag := ciphertext[len(ciphertext)-TagSize128L:]
ciphertext = ciphertext[:len(ciphertext)-TagSize128L]
ret, out := subtle.SliceForAppend(dst, len(ciphertext))
if subtle.InexactOverlap(out, ciphertext) {
panic("aegis: invalid buffer overlap")
}
ok := open128L(&a.key, (*[NonceSize128L]byte)(nonce), out,
ciphertext, tag, additionalData)
if !ok {
memclr(out)
return nil, errOpen
}
return ret, nil
}
type aegis256 struct {
key [KeySize256]byte
}
func (*aegis256) NonceSize() int {
return NonceSize256
}
func (*aegis256) Overhead() int {
return TagSize256
}
func (a *aegis256) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
if uint64(len(plaintext)) > MaxPlaintextSize256 {
panic("aegis: plaintext too large")
}
if len(nonce) != NonceSize256 {
panic("aegis: invalid nonce length")
}
if uint64(len(additionalData)) > MaxAdditionalDataSize256 {
panic("aegis: additional data too large")
}
ret, out := subtle.SliceForAppend(dst, len(plaintext)+TagSize256)
if subtle.InexactOverlap(out, plaintext) {
panic("aegis: invalid buffer overlap")
}
seal256(&a.key, (*[NonceSize256]byte)(nonce), out, plaintext, additionalData)
return ret
}
func (a *aegis256) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
if len(nonce) != NonceSize256 {
panic("aegis: invalid nonce length")
}
if len(ciphertext) < TagSize256 ||
uint64(len(ciphertext)) > ciphertextMax256 ||
uint64(len(additionalData)) > MaxAdditionalDataSize256 {
return nil, errOpen
}
tag := ciphertext[len(ciphertext)-TagSize256:]
ciphertext = ciphertext[:len(ciphertext)-TagSize256]
ret, out := subtle.SliceForAppend(dst, len(ciphertext))
if subtle.InexactOverlap(out, ciphertext) {
panic("aegis: invalid buffer overlap")
}
ok := open256(&a.key, (*[NonceSize256]byte)(nonce), out,
ciphertext, tag, additionalData)
if !ok {
memclr(out)
return nil, errOpen
}
return ret, nil
}
//go:noinline
func memclr(p []byte) {
for i := range p {
p[i] = 0
}
runtime.KeepAlive(p)
}