mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-02 04:38:16 +00:00
core/crypto/aead: Initial import
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
package aead
|
||||
|
||||
// seal_oneshot encrypts the plaintext and authenticates the aad and ciphertext,
|
||||
// with the provided algorithm, key, and iv, stores the output in dst and tag.
|
||||
//
|
||||
// dst and plaintext MUST alias exactly or not at all.
|
||||
seal_oneshot :: proc(algo: Algorithm, dst, tag, key, iv, aad, plaintext: []byte, impl: Implementation = nil) {
|
||||
ctx: Context
|
||||
init(&ctx, algo, key, impl)
|
||||
defer reset(&ctx)
|
||||
seal_ctx(&ctx, dst, tag, iv, aad, plaintext)
|
||||
}
|
||||
|
||||
// open authenticates the aad and ciphertext, and decrypts the ciphertext,
|
||||
// with the provided algorithm, key, 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_oneshot :: proc(algo: Algorithm, dst, key, iv, aad, ciphertext, tag: []byte, impl: Implementation = nil) -> bool {
|
||||
ctx: Context
|
||||
init(&ctx, algo, key, impl)
|
||||
defer reset(&ctx)
|
||||
return open_ctx(&ctx, dst, iv, aad, ciphertext, tag)
|
||||
}
|
||||
|
||||
seal :: proc {
|
||||
seal_ctx,
|
||||
seal_oneshot,
|
||||
}
|
||||
|
||||
open :: proc {
|
||||
open_ctx,
|
||||
open_oneshot,
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
package aead provides a generic interface to the supported Authenticated
|
||||
Encryption with Associated Data algorithms.
|
||||
|
||||
Both a one-shot and context based interface are provided, with similar
|
||||
usage. If multiple messages are to be sealed/opened via the same key,
|
||||
the context based interface may be more efficient, depending on the
|
||||
algorithm.
|
||||
|
||||
WARNING: Reusing the same key + iv to seal (encrypt) multiple messages
|
||||
results in catastrophic loss of security for most algorithms.
|
||||
|
||||
```odin
|
||||
package aead_example
|
||||
|
||||
import "core:bytes"
|
||||
import "core:crypto"
|
||||
import "core:crypto/aead"
|
||||
|
||||
main :: proc() {
|
||||
algo := aead.Algorithm.XCHACHA20POLY1305
|
||||
|
||||
// The example added associated data, and plaintext.
|
||||
aad_str := "Get your ass in gear boys."
|
||||
pt_str := "They're immanetizing the Eschaton."
|
||||
|
||||
aad := transmute([]byte)aad_str
|
||||
plaintext := transmute([]byte)pt_str
|
||||
pt_len := len(plaintext)
|
||||
|
||||
// Generate a random key for the purposes of illustration.
|
||||
key := make([]byte, aead.KEY_SIZES[algo])
|
||||
defer delete(key)
|
||||
crypto.rand_bytes(key)
|
||||
|
||||
// `ciphertext || tag`, is a common way data is transmitted, so
|
||||
// demonstrate that.
|
||||
buf := make([]byte, pt_len + aead.TAG_SIZES[algo])
|
||||
defer delete(buf)
|
||||
ciphertext, tag := buf[:pt_len], buf[pt_len:]
|
||||
|
||||
// Seal the AAD + Plaintext.
|
||||
iv := make([]byte, aead.IV_SIZES[algo])
|
||||
defer delete(iv)
|
||||
crypto.rand_bytes(iv) // Random IVs are safe with XChaCha20-Poly1305.
|
||||
aead.seal(algo, ciphertext, tag, key, iv, aad, plaintext)
|
||||
|
||||
// Open the AAD + Ciphertext.
|
||||
opened_pt := buf[:pt_len]
|
||||
if ok := aead.open(algo, opened_pt, key, iv, aad, ciphertext, tag); !ok {
|
||||
panic("aead example: failed to open")
|
||||
}
|
||||
|
||||
assert(bytes.equal(opened_pt, plaintext))
|
||||
}
|
||||
```
|
||||
*/
|
||||
package aead
|
||||
@@ -0,0 +1,187 @@
|
||||
package aead
|
||||
|
||||
import "core:crypto/aes"
|
||||
import "core:crypto/chacha20"
|
||||
import "core:crypto/chacha20poly1305"
|
||||
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 :: 16
|
||||
|
||||
// Algorithm is the algorithm identifier associated with a given Context.
|
||||
Algorithm :: enum {
|
||||
Invalid,
|
||||
AES_GCM_128,
|
||||
AES_GCM_192,
|
||||
AES_GCM_256,
|
||||
CHACHA20POLY1305,
|
||||
XCHACHA20POLY1305,
|
||||
}
|
||||
|
||||
// ALGORITM_NAMES is the Agorithm 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",
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
// Context is a concrete instantiation of a specific AEAD algorithm.
|
||||
Context :: struct {
|
||||
_algo: Algorithm,
|
||||
_impl: union {
|
||||
aes.Context_GCM,
|
||||
chacha20poly1305.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),
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
if len(key) != KEY_SIZES[algorithm] {
|
||||
panic("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 .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) {
|
||||
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:
|
||||
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 {
|
||||
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:
|
||||
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:
|
||||
// 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]
|
||||
}
|
||||
@@ -20,7 +20,7 @@ Context_CTR :: struct {
|
||||
}
|
||||
|
||||
// init_ctr initializes a Context_CTR with the provided key and IV.
|
||||
init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := Implementation.Hardware) {
|
||||
init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
|
||||
if len(iv) != CTR_IV_SIZE {
|
||||
panic("crypto/aes: invalid CTR IV size")
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ Context_ECB :: struct {
|
||||
}
|
||||
|
||||
// init_ecb initializes a Context_ECB with the provided key.
|
||||
init_ecb :: proc(ctx: ^Context_ECB, key: []byte, impl := Implementation.Hardware) {
|
||||
init_ecb :: proc(ctx: ^Context_ECB, key: []byte, impl := DEFAULT_IMPLEMENTATION) {
|
||||
init_impl(&ctx._impl, key, impl)
|
||||
ctx._is_initialized = true
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ Context_GCM :: struct {
|
||||
}
|
||||
|
||||
// init_gcm initializes a Context_GCM with the provided key.
|
||||
init_gcm :: proc(ctx: ^Context_GCM, key: []byte, impl := Implementation.Hardware) {
|
||||
init_gcm :: proc(ctx: ^Context_GCM, key: []byte, impl := DEFAULT_IMPLEMENTATION) {
|
||||
init_impl(&ctx._impl, key, impl)
|
||||
ctx._is_initialized = true
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ Context_Impl :: union {
|
||||
Context_Impl_Hardware,
|
||||
}
|
||||
|
||||
// DEFAULT_IMPLEMENTATION is the implementation that will be used by
|
||||
// default if possible.
|
||||
DEFAULT_IMPLEMENTATION :: Implementation.Hardware
|
||||
|
||||
// Implementation is an AES implementation. Most callers will not need
|
||||
// to use this as the package will automatically select the most performant
|
||||
// implementation available (See `is_hardware_accelerated()`).
|
||||
|
||||
@@ -26,7 +26,7 @@ Context :: struct {
|
||||
|
||||
// init inititializes a Context for ChaCha20 or XChaCha20 with the provided
|
||||
// key and iv.
|
||||
init :: proc(ctx: ^Context, key, iv: []byte, impl := Implementation.Simd256) {
|
||||
init :: proc(ctx: ^Context, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
|
||||
if len(key) != KEY_SIZE {
|
||||
panic("crypto/chacha20: invalid (X)ChaCha20 key size")
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@ import "core:crypto/_chacha20/ref"
|
||||
import "core:crypto/_chacha20/simd128"
|
||||
import "core:crypto/_chacha20/simd256"
|
||||
|
||||
// DEFAULT_IMPLEMENTATION is the implementation that will be used by
|
||||
// default if possible.
|
||||
DEFAULT_IMPLEMENTATION :: Implementation.Simd256
|
||||
|
||||
// Implementation is a ChaCha20 implementation. Most callers will not need
|
||||
// to use this as the package will automatically select the most performant
|
||||
// implementation available.
|
||||
|
||||
@@ -70,7 +70,7 @@ Context :: struct {
|
||||
}
|
||||
|
||||
// init initializes a Context with the provided key, for AEAD_CHACHA20_POLY1305.
|
||||
init :: proc(ctx: ^Context, key: []byte, impl := chacha20.Implementation.Simd256) {
|
||||
init :: proc(ctx: ^Context, key: []byte, impl := chacha20.DEFAULT_IMPLEMENTATION) {
|
||||
if len(key) != KEY_SIZE {
|
||||
panic("crypto/chacha20poly1305: invalid key size")
|
||||
}
|
||||
@@ -86,7 +86,7 @@ init :: proc(ctx: ^Context, key: []byte, impl := chacha20.Implementation.Simd256
|
||||
//
|
||||
// Note: While there are multiple definitions of XChaCha20-Poly1305
|
||||
// this sticks to the IETF draft and uses a 32-bit counter.
|
||||
init_xchacha :: proc(ctx: ^Context, key: []byte, impl := chacha20.Implementation.Simd256) {
|
||||
init_xchacha :: proc(ctx: ^Context, key: []byte, impl := chacha20.DEFAULT_IMPLEMENTATION) {
|
||||
init(ctx, key, impl)
|
||||
ctx._is_xchacha = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user