mirror of
https://github.com/odin-lang/Odin.git
synced 2026-05-29 15:15:08 +00:00
111 lines
3.1 KiB
Odin
111 lines
3.1 KiB
Odin
/*
|
|
`KMAC` message authentication code (`MAC`) algorithm.
|
|
|
|
See:
|
|
- [[ https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf ]]
|
|
*/
|
|
package kmac
|
|
|
|
import "../_sha3"
|
|
import "core:crypto"
|
|
import "core:crypto/shake"
|
|
|
|
// MIN_KEY_SIZE_128 is the minimum key size for KMAC128 in bytes.
|
|
MIN_KEY_SIZE_128 :: 128 / 8
|
|
// MIN_KEY_SIZE_256 is the minimum key size for KMAC256 in bytes.
|
|
MIN_KEY_SIZE_256 :: 256 / 8
|
|
|
|
// MIN_TAG_SIZE is the absolute minimum tag size for KMAC in bytes (8.4.2).
|
|
// Most callers SHOULD use at least 128-bits if not 256-bits for the tag
|
|
// size.
|
|
MIN_TAG_SIZE :: 32 / 8
|
|
|
|
// sum will compute the KMAC with the specified security strength,
|
|
// key, and domain separator over msg, and write the computed digest to
|
|
// dst.
|
|
sum :: proc(sec_strength: int, dst, msg, key, domain_sep: []byte) {
|
|
ctx: Context
|
|
|
|
_init_kmac(&ctx, key, domain_sep, sec_strength)
|
|
update(&ctx, msg)
|
|
final(&ctx, dst)
|
|
}
|
|
|
|
// verify will verify the KMAC tag computed with the specified security
|
|
// strength, key and domain separator over msg and return true iff the
|
|
// tag is valid.
|
|
verify :: proc(sec_strength: int, tag, msg, key, domain_sep: []byte, allocator := context.temp_allocator) -> bool {
|
|
derived_tag := make([]byte, len(tag), allocator)
|
|
defer(delete(derived_tag))
|
|
|
|
sum(sec_strength, derived_tag, msg, key, domain_sep)
|
|
|
|
return crypto.compare_constant_time(derived_tag, tag) == 1
|
|
}
|
|
|
|
// Context is a KMAC instance.
|
|
Context :: distinct shake.Context
|
|
|
|
// init_128 initializes a Context for KMAC28. This routine will panic if
|
|
// the key length is less than MIN_KEY_SIZE_128.
|
|
init_128 :: proc(ctx: ^Context, key, domain_sep: []byte) {
|
|
_init_kmac(ctx, key, domain_sep, 128)
|
|
}
|
|
|
|
// init_256 initializes a Context for KMAC256. This routine will panic if
|
|
// the key length is less than MIN_KEY_SIZE_256.
|
|
init_256 :: proc(ctx: ^Context, key, domain_sep: []byte) {
|
|
_init_kmac(ctx, key, domain_sep, 256)
|
|
}
|
|
|
|
// update adds more data to the Context.
|
|
update :: proc(ctx: ^Context, data: []byte) {
|
|
shake.write((^shake.Context)(ctx), data)
|
|
}
|
|
|
|
// final finalizes the Context, writes the tag to dst, and calls reset
|
|
// on the Context. This routine will panic if the dst length is less than
|
|
// MIN_TAG_SIZE.
|
|
final :: proc(ctx: ^Context, dst: []byte) {
|
|
defer reset(ctx)
|
|
|
|
ensure(len(dst) >= MIN_TAG_SIZE, "crypto/kmac: invalid KMAC tag_size, too short")
|
|
|
|
_sha3.final_cshake((^_sha3.Context)(ctx), dst)
|
|
}
|
|
|
|
// clone clones the Context other into ctx.
|
|
clone :: proc(ctx, other: ^Context) {
|
|
if ctx == other {
|
|
return
|
|
}
|
|
|
|
shake.clone((^shake.Context)(ctx), (^shake.Context)(other))
|
|
}
|
|
|
|
// reset sanitizes the Context. The Context must be re-initialized to
|
|
// be used again.
|
|
reset :: proc(ctx: ^Context) {
|
|
if !ctx.is_initialized {
|
|
return
|
|
}
|
|
|
|
shake.reset((^shake.Context)(ctx))
|
|
}
|
|
|
|
@(private)
|
|
_init_kmac :: proc(ctx: ^Context, key, s: []byte, sec_strength: int) {
|
|
if ctx.is_initialized {
|
|
reset(ctx)
|
|
}
|
|
|
|
ensure(len(key) >= sec_strength / 8, "crypto/kmac: invalid KMAC key, too short")
|
|
|
|
ctx_ := (^_sha3.Context)(ctx)
|
|
_sha3.init_cshake(ctx_, N_KMAC, s, sec_strength)
|
|
_sha3.bytepad(ctx_, [][]byte{key}, _sha3.rate_cshake(sec_strength))
|
|
}
|
|
|
|
@(private, rodata)
|
|
N_KMAC := []byte{'K', 'M', 'A', 'C'}
|