mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-07 13:33:13 +00:00
242 lines
7.8 KiB
Odin
242 lines
7.8 KiB
Odin
package aead
|
|
|
|
import "core:crypto/aegis"
|
|
import "core:crypto/aes"
|
|
import "core:crypto/chacha20"
|
|
import "core:crypto/chacha20poly1305"
|
|
import "core:crypto/deoxysii"
|
|
import "core:reflect"
|
|
|
|
// Implementation is an AEAD implementation. Most callers will not need
|
|
// to use this as the package will automatically select the most performant
|
|
// implementation available.
|
|
Implementation :: union {
|
|
aes.Implementation,
|
|
chacha20.Implementation,
|
|
}
|
|
|
|
// MAX_TAG_SIZE is the maximum size tag that can be returned by any of the
|
|
// Algorithms supported via this package.
|
|
MAX_TAG_SIZE :: 32
|
|
|
|
// Algorithm is the algorithm identifier associated with a given Context.
|
|
Algorithm :: enum {
|
|
Invalid,
|
|
AES_GCM_128,
|
|
AES_GCM_192,
|
|
AES_GCM_256,
|
|
CHACHA20POLY1305,
|
|
XCHACHA20POLY1305,
|
|
AEGIS_128L,
|
|
AEGIS_128L_256, // AEGIS-128L (256-bit tag)
|
|
AEGIS_256,
|
|
AEGIS_256_256, // AEGIS-256 (256-bit tag)
|
|
DEOXYS_II_256,
|
|
}
|
|
|
|
// ALGORITM_NAMES is the Algorithm to algorithm name string.
|
|
ALGORITHM_NAMES := [Algorithm]string {
|
|
.Invalid = "Invalid",
|
|
.AES_GCM_128 = "AES-GCM-128",
|
|
.AES_GCM_192 = "AES-GCM-192",
|
|
.AES_GCM_256 = "AES-GCM-256",
|
|
.CHACHA20POLY1305 = "chacha20poly1305",
|
|
.XCHACHA20POLY1305 = "xchacha20poly1305",
|
|
.AEGIS_128L = "AEGIS-128L",
|
|
.AEGIS_128L_256 = "AEGIS-128L-256",
|
|
.AEGIS_256 = "AEGIS-256",
|
|
.AEGIS_256_256 = "AEGIS-256-256",
|
|
.DEOXYS_II_256 = "Deoxys-II-256",
|
|
}
|
|
|
|
// TAG_SIZES is the Algorithm to tag size in bytes.
|
|
TAG_SIZES := [Algorithm]int {
|
|
.Invalid = 0,
|
|
.AES_GCM_128 = aes.GCM_TAG_SIZE,
|
|
.AES_GCM_192 = aes.GCM_TAG_SIZE,
|
|
.AES_GCM_256 = aes.GCM_TAG_SIZE,
|
|
.CHACHA20POLY1305 = chacha20poly1305.TAG_SIZE,
|
|
.XCHACHA20POLY1305 = chacha20poly1305.TAG_SIZE,
|
|
.AEGIS_128L = aegis.TAG_SIZE_128,
|
|
.AEGIS_128L_256 = aegis.TAG_SIZE_256,
|
|
.AEGIS_256 = aegis.TAG_SIZE_128,
|
|
.AEGIS_256_256 = aegis.TAG_SIZE_256,
|
|
.DEOXYS_II_256 = deoxysii.TAG_SIZE,
|
|
}
|
|
|
|
// KEY_SIZES is the Algorithm to key size in bytes.
|
|
KEY_SIZES := [Algorithm]int {
|
|
.Invalid = 0,
|
|
.AES_GCM_128 = aes.KEY_SIZE_128,
|
|
.AES_GCM_192 = aes.KEY_SIZE_192,
|
|
.AES_GCM_256 = aes.KEY_SIZE_256,
|
|
.CHACHA20POLY1305 = chacha20poly1305.KEY_SIZE,
|
|
.XCHACHA20POLY1305 = chacha20poly1305.KEY_SIZE,
|
|
.AEGIS_128L = aegis.KEY_SIZE_128L,
|
|
.AEGIS_128L_256 = aegis.KEY_SIZE_128L,
|
|
.AEGIS_256 = aegis.KEY_SIZE_256,
|
|
.AEGIS_256_256 = aegis.KEY_SIZE_256,
|
|
.DEOXYS_II_256 = deoxysii.KEY_SIZE,
|
|
}
|
|
|
|
// IV_SIZES is the Algorithm to initialization vector size in bytes.
|
|
//
|
|
// Note: Some algorithms (such as AES-GCM) support variable IV sizes.
|
|
IV_SIZES := [Algorithm]int {
|
|
.Invalid = 0,
|
|
.AES_GCM_128 = aes.GCM_IV_SIZE,
|
|
.AES_GCM_192 = aes.GCM_IV_SIZE,
|
|
.AES_GCM_256 = aes.GCM_IV_SIZE,
|
|
.CHACHA20POLY1305 = chacha20poly1305.IV_SIZE,
|
|
.XCHACHA20POLY1305 = chacha20poly1305.XIV_SIZE,
|
|
.AEGIS_128L = aegis.IV_SIZE_128L,
|
|
.AEGIS_128L_256 = aegis.IV_SIZE_128L,
|
|
.AEGIS_256 = aegis.IV_SIZE_256,
|
|
.AEGIS_256_256 = aegis.IV_SIZE_256,
|
|
.DEOXYS_II_256 = deoxysii.IV_SIZE,
|
|
}
|
|
|
|
// Context is a concrete instantiation of a specific AEAD algorithm.
|
|
Context :: struct {
|
|
_algo: Algorithm,
|
|
_impl: union {
|
|
aes.Context_GCM,
|
|
chacha20poly1305.Context,
|
|
aegis.Context,
|
|
deoxysii.Context,
|
|
},
|
|
}
|
|
|
|
@(private)
|
|
_IMPL_IDS := [Algorithm]typeid {
|
|
.Invalid = nil,
|
|
.AES_GCM_128 = typeid_of(aes.Context_GCM),
|
|
.AES_GCM_192 = typeid_of(aes.Context_GCM),
|
|
.AES_GCM_256 = typeid_of(aes.Context_GCM),
|
|
.CHACHA20POLY1305 = typeid_of(chacha20poly1305.Context),
|
|
.XCHACHA20POLY1305 = typeid_of(chacha20poly1305.Context),
|
|
.AEGIS_128L = typeid_of(aegis.Context),
|
|
.AEGIS_128L_256 = typeid_of(aegis.Context),
|
|
.AEGIS_256 = typeid_of(aegis.Context),
|
|
.AEGIS_256_256 = typeid_of(aegis.Context),
|
|
.DEOXYS_II_256 = typeid_of(deoxysii.Context),
|
|
}
|
|
|
|
// init initializes a Context with a specific AEAD Algorithm.
|
|
init :: proc(ctx: ^Context, algorithm: Algorithm, key: []byte, impl: Implementation = nil) {
|
|
if ctx._impl != nil {
|
|
reset(ctx)
|
|
}
|
|
|
|
ensure(len(key) == KEY_SIZES[algorithm], "crypto/aead: invalid key size")
|
|
|
|
// Directly specialize the union by setting the type ID (save a copy).
|
|
reflect.set_union_variant_typeid(
|
|
ctx._impl,
|
|
_IMPL_IDS[algorithm],
|
|
)
|
|
switch algorithm {
|
|
case .AES_GCM_128, .AES_GCM_192, .AES_GCM_256:
|
|
impl_ := impl != nil ? impl.(aes.Implementation) : aes.DEFAULT_IMPLEMENTATION
|
|
aes.init_gcm(&ctx._impl.(aes.Context_GCM), key, impl_)
|
|
case .CHACHA20POLY1305:
|
|
impl_ := impl != nil ? impl.(chacha20.Implementation) : chacha20.DEFAULT_IMPLEMENTATION
|
|
chacha20poly1305.init(&ctx._impl.(chacha20poly1305.Context), key, impl_)
|
|
case .XCHACHA20POLY1305:
|
|
impl_ := impl != nil ? impl.(chacha20.Implementation) : chacha20.DEFAULT_IMPLEMENTATION
|
|
chacha20poly1305.init_xchacha(&ctx._impl.(chacha20poly1305.Context), key, impl_)
|
|
case .AEGIS_128L, .AEGIS_128L_256, .AEGIS_256, .AEGIS_256_256:
|
|
impl_ := impl != nil ? impl.(aes.Implementation) : aes.DEFAULT_IMPLEMENTATION
|
|
aegis.init(&ctx._impl.(aegis.Context), key, impl_)
|
|
case .DEOXYS_II_256:
|
|
impl_ := impl != nil ? impl.(aes.Implementation) : aes.DEFAULT_IMPLEMENTATION
|
|
deoxysii.init(&ctx._impl.(deoxysii.Context), key, impl_)
|
|
case .Invalid:
|
|
panic("crypto/aead: uninitialized algorithm")
|
|
case:
|
|
panic("crypto/aead: invalid algorithm")
|
|
}
|
|
|
|
ctx._algo = algorithm
|
|
}
|
|
|
|
// seal_ctx encrypts the plaintext and authenticates the aad and ciphertext,
|
|
// with the provided Context and iv, stores the output in dst and tag.
|
|
//
|
|
// dst and plaintext MUST alias exactly or not at all.
|
|
seal_ctx :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
|
|
ensure(len(tag) == TAG_SIZES[ctx._algo], "crypto/aead: invalid tag size")
|
|
|
|
switch &impl in ctx._impl {
|
|
case aes.Context_GCM:
|
|
aes.seal_gcm(&impl, dst, tag, iv, aad, plaintext)
|
|
case chacha20poly1305.Context:
|
|
chacha20poly1305.seal(&impl, dst, tag, iv, aad, plaintext)
|
|
case aegis.Context:
|
|
aegis.seal(&impl, dst, tag, iv, aad, plaintext)
|
|
case deoxysii.Context:
|
|
deoxysii.seal(&impl, dst, tag, iv, aad, plaintext)
|
|
case:
|
|
panic("crypto/aead: uninitialized algorithm")
|
|
}
|
|
}
|
|
|
|
// open_ctx authenticates the aad and ciphertext, and decrypts the ciphertext,
|
|
// with the provided Context, iv, and tag, and stores the output in dst,
|
|
// returning true iff the authentication was successful. If authentication
|
|
// fails, the destination buffer will be zeroed.
|
|
//
|
|
// dst and plaintext MUST alias exactly or not at all.
|
|
@(require_results)
|
|
open_ctx :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
|
ensure(len(tag) == TAG_SIZES[ctx._algo], "crypto/aead: invalid tag size")
|
|
|
|
switch &impl in ctx._impl {
|
|
case aes.Context_GCM:
|
|
return aes.open_gcm(&impl, dst, iv, aad, ciphertext, tag)
|
|
case chacha20poly1305.Context:
|
|
return chacha20poly1305.open(&impl, dst, iv, aad, ciphertext, tag)
|
|
case aegis.Context:
|
|
return aegis.open(&impl, dst, iv, aad, ciphertext, tag)
|
|
case deoxysii.Context:
|
|
return deoxysii.open(&impl, dst, iv, aad, ciphertext, tag)
|
|
case:
|
|
panic("crypto/aead: uninitialized algorithm")
|
|
}
|
|
}
|
|
|
|
// reset sanitizes the Context. The Context must be re-initialized to
|
|
// be used again.
|
|
reset :: proc(ctx: ^Context) {
|
|
switch &impl in ctx._impl {
|
|
case aes.Context_GCM:
|
|
aes.reset_gcm(&impl)
|
|
case chacha20poly1305.Context:
|
|
chacha20poly1305.reset(&impl)
|
|
case aegis.Context:
|
|
aegis.reset(&impl)
|
|
case deoxysii.Context:
|
|
deoxysii.reset(&impl)
|
|
case:
|
|
// Calling reset repeatedly is fine.
|
|
}
|
|
|
|
ctx._algo = .Invalid
|
|
ctx._impl = nil
|
|
}
|
|
|
|
// algorithm returns the Algorithm used by a Context instance.
|
|
algorithm :: proc(ctx: ^Context) -> Algorithm {
|
|
return ctx._algo
|
|
}
|
|
|
|
// iv_size returns the IV size of a Context instance in bytes.
|
|
iv_size :: proc(ctx: ^Context) -> int {
|
|
return IV_SIZES[ctx._algo]
|
|
}
|
|
|
|
// tag_size returns the tag size of a Context instance in bytes.
|
|
tag_size :: proc(ctx: ^Context) -> int {
|
|
return TAG_SIZES[ctx._algo]
|
|
}
|