mirror of
https://github.com/odin-lang/Odin.git
synced 2026-05-29 07:05:10 +00:00
119 lines
3.6 KiB
Odin
119 lines
3.6 KiB
Odin
package _chacha20
|
|
|
|
import "core:crypto"
|
|
import "core:encoding/endian"
|
|
import "core:math/bits"
|
|
|
|
// KEY_SIZE is the (X)ChaCha20 key size in bytes.
|
|
KEY_SIZE :: 32
|
|
// IV_SIZE is the ChaCha20 IV size in bytes.
|
|
IV_SIZE :: 12
|
|
// XIV_SIZE is the XChaCha20 IV size in bytes.
|
|
XIV_SIZE :: 24
|
|
|
|
// MAX_CTR_IETF is the maximum counter value for the IETF flavor ChaCha20.
|
|
MAX_CTR_IETF :: 0xffffffff
|
|
// BLOCK_SIZE is the (X)ChaCha20 block size in bytes.
|
|
BLOCK_SIZE :: 64
|
|
// STATE_SIZE_U32 is the (X)ChaCha20 state size in u32s.
|
|
STATE_SIZE_U32 :: 16
|
|
// Rounds is the (X)ChaCha20 round count.
|
|
ROUNDS :: 20
|
|
|
|
// SIGMA_0 is sigma[0:4].
|
|
SIGMA_0: u32 : 0x61707865
|
|
// SIGMA_1 is sigma[4:8].
|
|
SIGMA_1: u32 : 0x3320646e
|
|
// SIGMA_2 is sigma[8:12].
|
|
SIGMA_2: u32 : 0x79622d32
|
|
// SIGMA_3 is sigma[12:16].
|
|
SIGMA_3: u32 : 0x6b206574
|
|
|
|
// Context is a ChaCha20 or XChaCha20 instance.
|
|
Context :: struct {
|
|
_s: [STATE_SIZE_U32]u32,
|
|
_buffer: [BLOCK_SIZE]byte,
|
|
_off: int,
|
|
_is_ietf_flavor: bool,
|
|
_is_initialized: bool,
|
|
}
|
|
|
|
// init inititializes a Context for ChaCha20 with the provided key and
|
|
// iv.
|
|
//
|
|
// WARNING: This ONLY handles ChaCha20. XChaCha20 sub-key and IV
|
|
// derivation is expected to be handled by the caller, so that the
|
|
// HChaCha call can be suitably accelerated.
|
|
init :: proc "contextless" (ctx: ^Context, key, iv: []byte, is_xchacha: bool) {
|
|
ensure_contextless(len(key) == KEY_SIZE, "chacha20: invalid key size")
|
|
ensure_contextless(len(iv) == IV_SIZE, "chacha20: invalid key size")
|
|
|
|
k, n := key, iv
|
|
|
|
ctx._s[0] = SIGMA_0
|
|
ctx._s[1] = SIGMA_1
|
|
ctx._s[2] = SIGMA_2
|
|
ctx._s[3] = SIGMA_3
|
|
ctx._s[4] = endian.unchecked_get_u32le(k[0:4])
|
|
ctx._s[5] = endian.unchecked_get_u32le(k[4:8])
|
|
ctx._s[6] = endian.unchecked_get_u32le(k[8:12])
|
|
ctx._s[7] = endian.unchecked_get_u32le(k[12:16])
|
|
ctx._s[8] = endian.unchecked_get_u32le(k[16:20])
|
|
ctx._s[9] = endian.unchecked_get_u32le(k[20:24])
|
|
ctx._s[10] = endian.unchecked_get_u32le(k[24:28])
|
|
ctx._s[11] = endian.unchecked_get_u32le(k[28:32])
|
|
ctx._s[12] = 0
|
|
ctx._s[13] = endian.unchecked_get_u32le(n[0:4])
|
|
ctx._s[14] = endian.unchecked_get_u32le(n[4:8])
|
|
ctx._s[15] = endian.unchecked_get_u32le(n[8:12])
|
|
|
|
ctx._off = BLOCK_SIZE
|
|
ctx._is_ietf_flavor = !is_xchacha
|
|
ctx._is_initialized = true
|
|
}
|
|
|
|
// seek seeks the (X)ChaCha20 stream counter to the specified block.
|
|
seek :: proc(ctx: ^Context, block_nr: u64) {
|
|
ensure(ctx._is_initialized)
|
|
|
|
if ctx._is_ietf_flavor {
|
|
ensure(block_nr <= MAX_CTR_IETF, "crypto/chacha20: attempted to seek past maximum counter")
|
|
} else {
|
|
ctx._s[13] = u32(block_nr >> 32)
|
|
}
|
|
ctx._s[12] = u32(block_nr)
|
|
ctx._off = BLOCK_SIZE
|
|
}
|
|
|
|
// reset sanitizes the Context. The Context must be re-initialized to
|
|
// be used again.
|
|
reset :: proc(ctx: ^Context) {
|
|
crypto.zero_explicit(&ctx._s, size_of(ctx._s))
|
|
crypto.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
|
|
|
|
ctx._is_initialized = false
|
|
}
|
|
|
|
check_counter_limit :: proc(ctx: ^Context, nr_blocks: int) {
|
|
// Enforce the maximum consumed keystream per IV.
|
|
//
|
|
// While all modern "standard" definitions of ChaCha20 use
|
|
// the IETF 32-bit counter, for XChaCha20 historical
|
|
// implementations allow for a 64-bit counter.
|
|
//
|
|
// Honestly, the answer here is "use a MRAE primitive", but
|
|
// go with "common" practice in the case of XChaCha20.
|
|
|
|
ERR_CTR_EXHAUSTED :: "crypto/chacha20: maximum (X)ChaCha20 keystream per IV reached"
|
|
|
|
ctr_ok: bool
|
|
if ctx._is_ietf_flavor {
|
|
ctr_ok = u64(ctx._s[12]) + u64(nr_blocks) <= MAX_CTR_IETF
|
|
} else {
|
|
ctr := (u64(ctx._s[13]) << 32) | u64(ctx._s[12])
|
|
_, carry := bits.add_u64(ctr, u64(nr_blocks), 0)
|
|
ctr_ok = carry == 0
|
|
}
|
|
|
|
ensure(ctr_ok, "crypto/chacha20: maximum (X)ChaCha20 keystream per IV reached")
|
|
} |