Skip to content

Commit 4198575

Browse files
committed
core/crypto/aegis: Initial import
1 parent 5f8be67 commit 4198575

File tree

7 files changed

+930
-2
lines changed

7 files changed

+930
-2
lines changed

core/crypto/aead/low_level.odin

+45-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package aead
22

3+
import "core:crypto/aegis"
34
import "core:crypto/aes"
45
import "core:crypto/chacha20"
56
import "core:crypto/chacha20poly1305"
@@ -15,7 +16,7 @@ Implementation :: union {
1516

1617
// MAX_TAG_SIZE is the maximum size tag that can be returned by any of the
1718
// Algorithms supported via this package.
18-
MAX_TAG_SIZE :: 16
19+
MAX_TAG_SIZE :: 32
1920

2021
// Algorithm is the algorithm identifier associated with a given Context.
2122
Algorithm :: enum {
@@ -25,16 +26,24 @@ Algorithm :: enum {
2526
AES_GCM_256,
2627
CHACHA20POLY1305,
2728
XCHACHA20POLY1305,
29+
AEGIS_128L,
30+
AEGIS_128L_256, // AEGIS-128L (256-bit tag)
31+
AEGIS_256,
32+
AEGIS_256_256, // AEGIS-256 (256-bit tag)
2833
}
2934

30-
// ALGORITM_NAMES is the Agorithm to algorithm name string.
35+
// ALGORITM_NAMES is the Algorithm to algorithm name string.
3136
ALGORITHM_NAMES := [Algorithm]string {
3237
.Invalid = "Invalid",
3338
.AES_GCM_128 = "AES-GCM-128",
3439
.AES_GCM_192 = "AES-GCM-192",
3540
.AES_GCM_256 = "AES-GCM-256",
3641
.CHACHA20POLY1305 = "chacha20poly1305",
3742
.XCHACHA20POLY1305 = "xchacha20poly1305",
43+
.AEGIS_128L = "AEGIS-128L",
44+
.AEGIS_128L_256 = "AEGIS-128L-256",
45+
.AEGIS_256 = "AEGIS-256",
46+
.AEGIS_256_256 = "AEGIS-256-256",
3847
}
3948

4049
// TAG_SIZES is the Algorithm to tag size in bytes.
@@ -45,6 +54,10 @@ TAG_SIZES := [Algorithm]int {
4554
.AES_GCM_256 = aes.GCM_TAG_SIZE,
4655
.CHACHA20POLY1305 = chacha20poly1305.TAG_SIZE,
4756
.XCHACHA20POLY1305 = chacha20poly1305.TAG_SIZE,
57+
.AEGIS_128L = aegis.TAG_SIZE_128,
58+
.AEGIS_128L_256 = aegis.TAG_SIZE_256,
59+
.AEGIS_256 = aegis.TAG_SIZE_128,
60+
.AEGIS_256_256 = aegis.TAG_SIZE_256,
4861
}
4962

5063
// KEY_SIZES is the Algorithm to key size in bytes.
@@ -55,6 +68,10 @@ KEY_SIZES := [Algorithm]int {
5568
.AES_GCM_256 = aes.KEY_SIZE_256,
5669
.CHACHA20POLY1305 = chacha20poly1305.KEY_SIZE,
5770
.XCHACHA20POLY1305 = chacha20poly1305.KEY_SIZE,
71+
.AEGIS_128L = aegis.KEY_SIZE_128L,
72+
.AEGIS_128L_256 = aegis.KEY_SIZE_128L,
73+
.AEGIS_256 = aegis.KEY_SIZE_256,
74+
.AEGIS_256_256 = aegis.KEY_SIZE_256,
5875
}
5976

6077
// IV_SIZES is the Algorithm to initialization vector size in bytes.
@@ -67,6 +84,10 @@ IV_SIZES := [Algorithm]int {
6784
.AES_GCM_256 = aes.GCM_IV_SIZE,
6885
.CHACHA20POLY1305 = chacha20poly1305.IV_SIZE,
6986
.XCHACHA20POLY1305 = chacha20poly1305.XIV_SIZE,
87+
.AEGIS_128L = aegis.IV_SIZE_128L,
88+
.AEGIS_128L_256 = aegis.IV_SIZE_128L,
89+
.AEGIS_256 = aegis.IV_SIZE_256,
90+
.AEGIS_256_256 = aegis.IV_SIZE_256,
7091
}
7192

7293
// Context is a concrete instantiation of a specific AEAD algorithm.
@@ -75,6 +96,7 @@ Context :: struct {
7596
_impl: union {
7697
aes.Context_GCM,
7798
chacha20poly1305.Context,
99+
aegis.Context,
78100
},
79101
}
80102

@@ -86,6 +108,10 @@ _IMPL_IDS := [Algorithm]typeid {
86108
.AES_GCM_256 = typeid_of(aes.Context_GCM),
87109
.CHACHA20POLY1305 = typeid_of(chacha20poly1305.Context),
88110
.XCHACHA20POLY1305 = typeid_of(chacha20poly1305.Context),
111+
.AEGIS_128L = typeid_of(aegis.Context),
112+
.AEGIS_128L_256 = typeid_of(aegis.Context),
113+
.AEGIS_256 = typeid_of(aegis.Context),
114+
.AEGIS_256_256 = typeid_of(aegis.Context),
89115
}
90116

91117
// init initializes a Context with a specific AEAD Algorithm.
@@ -113,6 +139,9 @@ init :: proc(ctx: ^Context, algorithm: Algorithm, key: []byte, impl: Implementat
113139
case .XCHACHA20POLY1305:
114140
impl_ := impl != nil ? impl.(chacha20.Implementation) : chacha20.DEFAULT_IMPLEMENTATION
115141
chacha20poly1305.init_xchacha(&ctx._impl.(chacha20poly1305.Context), key, impl_)
142+
case .AEGIS_128L, .AEGIS_128L_256, .AEGIS_256, .AEGIS_256_256:
143+
impl_ := impl != nil ? impl.(aes.Implementation) : aes.DEFAULT_IMPLEMENTATION
144+
aegis.init(&ctx._impl.(aegis.Context), key, impl_)
116145
case .Invalid:
117146
panic("crypto/aead: uninitialized algorithm")
118147
case:
@@ -127,11 +156,17 @@ init :: proc(ctx: ^Context, algorithm: Algorithm, key: []byte, impl: Implementat
127156
//
128157
// dst and plaintext MUST alias exactly or not at all.
129158
seal_ctx :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
159+
if len(tag) != TAG_SIZES[ctx._algo] {
160+
panic("crypto/aead: invalid tag size")
161+
}
162+
130163
switch &impl in ctx._impl {
131164
case aes.Context_GCM:
132165
aes.seal_gcm(&impl, dst, tag, iv, aad, plaintext)
133166
case chacha20poly1305.Context:
134167
chacha20poly1305.seal(&impl, dst, tag, iv, aad, plaintext)
168+
case aegis.Context:
169+
aegis.seal(&impl, dst, tag, iv, aad, plaintext)
135170
case:
136171
panic("crypto/aead: uninitialized algorithm")
137172
}
@@ -145,11 +180,17 @@ seal_ctx :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
145180
// dst and plaintext MUST alias exactly or not at all.
146181
@(require_results)
147182
open_ctx :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
183+
if len(tag) != TAG_SIZES[ctx._algo] {
184+
panic("crypto/aead: invalid tag size")
185+
}
186+
148187
switch &impl in ctx._impl {
149188
case aes.Context_GCM:
150189
return aes.open_gcm(&impl, dst, iv, aad, ciphertext, tag)
151190
case chacha20poly1305.Context:
152191
return chacha20poly1305.open(&impl, dst, iv, aad, ciphertext, tag)
192+
case aegis.Context:
193+
return aegis.open(&impl, dst, iv, aad, ciphertext, tag)
153194
case:
154195
panic("crypto/aead: uninitialized algorithm")
155196
}
@@ -163,6 +204,8 @@ reset :: proc(ctx: ^Context) {
163204
aes.reset_gcm(&impl)
164205
case chacha20poly1305.Context:
165206
chacha20poly1305.reset(&impl)
207+
case aegis.Context:
208+
aegis.reset(&impl)
166209
case:
167210
// Calling reset repeatedly is fine.
168211
}

core/crypto/aegis/aegis.odin

+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
package aegis implements the AEGIS-128L and AEGIS-256 Authenticated
3+
Encryption with Additional Data algorithms.
4+
5+
See:
6+
- [[ https://www.ietf.org/archive/id/draft-irtf-cfrg-aegis-aead-11.txt ]]
7+
*/
8+
package aegis
9+
10+
import "core:bytes"
11+
import "core:crypto"
12+
import "core:crypto/aes"
13+
import "core:mem"
14+
15+
// KEY_SIZE_128L is the AEGIS-128L key size in bytes.
16+
KEY_SIZE_128L :: 16
17+
// KEY_SIZE_256 is the AEGIS-256 key size in bytes.
18+
KEY_SIZE_256 :: 32
19+
// IV_SIZE_128L is the AEGIS-128L IV size in bytes.
20+
IV_SIZE_128L :: 16
21+
// IV_SIZE_256 is the AEGIS-256 IV size in bytes.
22+
IV_SIZE_256 :: 32
23+
// TAG_SIZE_128 is the AEGIS-128L or AEGIS-256 128-bit tag size in bytes.
24+
TAG_SIZE_128 :: 16
25+
// TAG_SIZE_256 is the AEGIS-128L or AEGIS-256 256-bit tag size in bytes.
26+
TAG_SIZE_256 :: 32
27+
28+
@(private)
29+
_RATE_128L :: 32
30+
@(private)
31+
_RATE_256 :: 16
32+
@(private)
33+
_RATE_MAX :: _RATE_128L
34+
35+
@(private, rodata)
36+
_C0 := [16]byte{
37+
0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0d,
38+
0x15, 0x22, 0x37, 0x59, 0x90, 0xe9, 0x79, 0x62,
39+
}
40+
41+
@(private, rodata)
42+
_C1 := [16]byte {
43+
0xdb, 0x3d, 0x18, 0x55, 0x6d, 0xc2, 0x2f, 0xf1,
44+
0x20, 0x11, 0x31, 0x42, 0x73, 0xb5, 0x28, 0xdd,
45+
}
46+
47+
// Context is a keyed AEGIS-128L or AEGIS-256 instance.
48+
Context :: struct {
49+
_key: [KEY_SIZE_256]byte,
50+
_key_len: int,
51+
_impl: aes.Implementation,
52+
_is_initialized: bool,
53+
}
54+
55+
@(private)
56+
_validate_common_slice_sizes :: proc (ctx: ^Context, tag, iv, aad, text: []byte) {
57+
switch len(tag) {
58+
case TAG_SIZE_128, TAG_SIZE_256:
59+
case:
60+
panic("crypto/aegis: invalid destination tag size")
61+
}
62+
63+
iv_ok: bool
64+
switch ctx._key_len {
65+
case KEY_SIZE_128L:
66+
iv_ok = len(iv) == IV_SIZE_128L
67+
case KEY_SIZE_256:
68+
iv_ok = len(iv) == IV_SIZE_256
69+
}
70+
if !iv_ok {
71+
panic("crypto/aegis: invalid IV size")
72+
}
73+
74+
#assert(size_of(int) == 8 || size_of(int) <= 4)
75+
// As A_MAX and P_MAX are both defined to be 2^61 - 1 bytes, and
76+
// the maximum length of a slice is bound by `size_of(int)`, where
77+
// `int` is register sized, there is no need to check AAD/text
78+
// lengths/
79+
}
80+
81+
// init initializes a Context with the provided key, for AEGIS-128L or AEGIS-256.
82+
init :: proc(ctx: ^Context, key: []byte, impl := aes.DEFAULT_IMPLEMENTATION) {
83+
switch len(key) {
84+
case KEY_SIZE_128L, KEY_SIZE_256:
85+
case:
86+
panic("crypto/aegis: invalid key size")
87+
}
88+
89+
copy(ctx._key[:], key)
90+
ctx._key_len = len(key)
91+
ctx._impl = impl
92+
if ctx._impl == .Hardware && !is_hardware_accelerated() {
93+
ctx._impl = .Portable
94+
}
95+
ctx._is_initialized = true
96+
}
97+
98+
// seal encrypts the plaintext and authenticates the aad and ciphertext,
99+
// with the provided Context and iv, stores the output in dst and tag.
100+
//
101+
// dst and plaintext MUST alias exactly or not at all.
102+
seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
103+
assert(ctx._is_initialized)
104+
105+
_validate_common_slice_sizes(ctx, tag, iv, aad, plaintext)
106+
if len(dst) != len(plaintext) {
107+
panic("crypto/aegis: invalid destination ciphertext size")
108+
}
109+
if bytes.alias_inexactly(dst, plaintext) {
110+
panic("crypto/aegis: dst and plaintext alias inexactly")
111+
}
112+
113+
if ctx._impl == .Hardware {
114+
st: State_HW
115+
defer reset_state_hw(&st)
116+
117+
init_hw(ctx, &st, iv)
118+
119+
aad_len, pt_len := len(aad), len(plaintext)
120+
if aad_len > 0 {
121+
absorb_hw(&st, aad)
122+
}
123+
124+
if pt_len > 0 {
125+
enc_hw(&st, dst, plaintext)
126+
}
127+
128+
finalize_hw(&st, tag, aad_len, pt_len)
129+
} else {
130+
panic("core/crypto/aegis: not implemented")
131+
}
132+
}
133+
134+
// open authenticates the aad and ciphertext, and decrypts the ciphertext,
135+
// with the provided Context, iv, and tag, and stores the output in dst,
136+
// returning true iff the authentication was successful. If authentication
137+
// fails, the destination buffer will be zeroed.
138+
//
139+
// dst and plaintext MUST alias exactly or not at all.
140+
@(require_results)
141+
open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
142+
assert(ctx._is_initialized)
143+
144+
_validate_common_slice_sizes(ctx, tag, iv, aad, ciphertext)
145+
if len(dst) != len(ciphertext) {
146+
panic("crypto/aegis: invalid destination plaintext size")
147+
}
148+
if bytes.alias_inexactly(dst, ciphertext) {
149+
panic("crypto/aegis: dst and ciphertext alias inexactly")
150+
}
151+
152+
if ctx._impl == .Hardware {
153+
st: State_HW
154+
defer reset_state_hw(&st)
155+
156+
init_hw(ctx, &st, iv)
157+
158+
aad_len, ct_len := len(aad), len(ciphertext)
159+
if aad_len > 0 {
160+
absorb_hw(&st, aad)
161+
}
162+
163+
if ct_len > 0 {
164+
dec_hw(&st, dst, ciphertext)
165+
}
166+
167+
tmp: [TAG_SIZE_256]byte
168+
derived_tag := tmp[:len(tag)]
169+
finalize_hw(&st, derived_tag, aad_len, ct_len)
170+
171+
if crypto.compare_constant_time(tag, derived_tag) != 1 {
172+
mem.zero_explicit(raw_data(dst), ct_len)
173+
return false
174+
}
175+
} else {
176+
panic("core/crypto/aegis: not implemented")
177+
}
178+
179+
return true
180+
}
181+
182+
// reset sanitizes the Context. The Context must be
183+
// re-initialized to be used again.
184+
reset :: proc "contextless" (ctx: ^Context) {
185+
mem.zero_explicit(&ctx._key, len(ctx._key))
186+
ctx._key_len = 0
187+
ctx._is_initialized = false
188+
}
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//+build !amd64
2+
package aegis
3+
4+
@(private = "file")
5+
ERR_HW_NOT_SUPPORTED :: "crypto/aegis: hardware implementation unsupported"
6+
7+
@(private)
8+
State_HW :: struct {}
9+
10+
// is_hardware_accelerated returns true iff hardware accelerated AEGIS
11+
// is supported.
12+
is_hardware_accelerated :: proc "contextless" () -> bool {
13+
return false
14+
}
15+
16+
@(private)
17+
init_hw :: proc "contextless" (ctx: ^Context, st: ^State_HW, iv: []byte) {
18+
panic_contextless(ERR_HW_NOT_SUPPORTED)
19+
}
20+
21+
@(private)
22+
absorb_hw :: proc "contextless" (st: ^State_HW, aad: []byte) {
23+
panic_contextless(ERR_HW_NOT_SUPPORTED)
24+
}
25+
26+
@(private)
27+
enc_hw :: proc "contextless" (st: ^State_HW, dst, src: []byte) {
28+
panic_contextless(ERR_HW_NOT_SUPPORTED)
29+
}
30+
31+
@(private)
32+
dec_hw :: proc "contextless" (st: ^State_HW, dst, src: []byte) {
33+
panic_contextless(ERR_HW_NOT_SUPPORTED)
34+
}
35+
36+
@(private)
37+
finalize_hw :: proc "contextless" (st: ^State_HW, tag: []byte, ad_len, msg_len: int) {
38+
panic_contextless(ERR_HW_NOT_SUPPORTED)
39+
}
40+
41+
@(private)
42+
reset_state_hw :: proc "contextless" (st: ^State_HW) {
43+
panic_contextless(ERR_HW_NOT_SUPPORTED)
44+
}

0 commit comments

Comments
 (0)