mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-18 14:29:47 -04:00
354 lines
9.9 KiB
Odin
354 lines
9.9 KiB
Odin
package crypto_hash
|
|
|
|
import "core:crypto/blake2b"
|
|
import "core:crypto/blake2s"
|
|
import "core:crypto/sha2"
|
|
import "core:crypto/sha3"
|
|
import "core:crypto/sm3"
|
|
import "core:crypto/legacy/keccak"
|
|
import "core:crypto/legacy/md5"
|
|
import "core:crypto/legacy/sha1"
|
|
|
|
import "core:reflect"
|
|
|
|
// MAX_DIGEST_SIZE is the maximum size digest that can be returned by any
|
|
// of the Algorithms supported via this package.
|
|
MAX_DIGEST_SIZE :: 64
|
|
// MAX_BLOCK_SIZE is the maximum block size used by any of Algorithms
|
|
// supported by this package.
|
|
MAX_BLOCK_SIZE :: sha3.BLOCK_SIZE_224
|
|
|
|
// Algorithm is the algorithm identifier associated with a given Context.
|
|
Algorithm :: enum {
|
|
Invalid,
|
|
BLAKE2B,
|
|
BLAKE2S,
|
|
SHA224,
|
|
SHA256,
|
|
SHA384,
|
|
SHA512,
|
|
SHA512_256,
|
|
SHA3_224,
|
|
SHA3_256,
|
|
SHA3_384,
|
|
SHA3_512,
|
|
SM3,
|
|
Legacy_KECCAK_224,
|
|
Legacy_KECCAK_256,
|
|
Legacy_KECCAK_384,
|
|
Legacy_KECCAK_512,
|
|
Insecure_MD5,
|
|
Insecure_SHA1,
|
|
}
|
|
|
|
// ALGORITHM_NAMES is the Algorithm to algorithm name string.
|
|
ALGORITHM_NAMES := [Algorithm]string {
|
|
.Invalid = "Invalid",
|
|
.BLAKE2B = "BLAKE2b",
|
|
.BLAKE2S = "BLAKE2s",
|
|
.SHA224 = "SHA-224",
|
|
.SHA256 = "SHA-256",
|
|
.SHA384 = "SHA-384",
|
|
.SHA512 = "SHA-512",
|
|
.SHA512_256 = "SHA-512/256",
|
|
.SHA3_224 = "SHA3-224",
|
|
.SHA3_256 = "SHA3-256",
|
|
.SHA3_384 = "SHA3-384",
|
|
.SHA3_512 = "SHA3-512",
|
|
.SM3 = "SM3",
|
|
.Legacy_KECCAK_224 = "Keccak-224",
|
|
.Legacy_KECCAK_256 = "Keccak-256",
|
|
.Legacy_KECCAK_384 = "Keccak-384",
|
|
.Legacy_KECCAK_512 = "Keccak-512",
|
|
.Insecure_MD5 = "MD5",
|
|
.Insecure_SHA1 = "SHA-1",
|
|
}
|
|
|
|
// DIGEST_SIZES is the Algorithm to digest size in bytes.
|
|
DIGEST_SIZES := [Algorithm]int {
|
|
.Invalid = 0,
|
|
.BLAKE2B = blake2b.DIGEST_SIZE,
|
|
.BLAKE2S = blake2s.DIGEST_SIZE,
|
|
.SHA224 = sha2.DIGEST_SIZE_224,
|
|
.SHA256 = sha2.DIGEST_SIZE_256,
|
|
.SHA384 = sha2.DIGEST_SIZE_384,
|
|
.SHA512 = sha2.DIGEST_SIZE_512,
|
|
.SHA512_256 = sha2.DIGEST_SIZE_512_256,
|
|
.SHA3_224 = sha3.DIGEST_SIZE_224,
|
|
.SHA3_256 = sha3.DIGEST_SIZE_256,
|
|
.SHA3_384 = sha3.DIGEST_SIZE_384,
|
|
.SHA3_512 = sha3.DIGEST_SIZE_512,
|
|
.SM3 = sm3.DIGEST_SIZE,
|
|
.Legacy_KECCAK_224 = keccak.DIGEST_SIZE_224,
|
|
.Legacy_KECCAK_256 = keccak.DIGEST_SIZE_256,
|
|
.Legacy_KECCAK_384 = keccak.DIGEST_SIZE_384,
|
|
.Legacy_KECCAK_512 = keccak.DIGEST_SIZE_512,
|
|
.Insecure_MD5 = md5.DIGEST_SIZE,
|
|
.Insecure_SHA1 = sha1.DIGEST_SIZE,
|
|
}
|
|
|
|
// BLOCK_SIZES is the Algoritm to block size in bytes.
|
|
BLOCK_SIZES := [Algorithm]int {
|
|
.Invalid = 0,
|
|
.BLAKE2B = blake2b.BLOCK_SIZE,
|
|
.BLAKE2S = blake2s.BLOCK_SIZE,
|
|
.SHA224 = sha2.BLOCK_SIZE_256,
|
|
.SHA256 = sha2.BLOCK_SIZE_256,
|
|
.SHA384 = sha2.BLOCK_SIZE_512,
|
|
.SHA512 = sha2.BLOCK_SIZE_512,
|
|
.SHA512_256 = sha2.BLOCK_SIZE_512,
|
|
.SHA3_224 = sha3.BLOCK_SIZE_224,
|
|
.SHA3_256 = sha3.BLOCK_SIZE_256,
|
|
.SHA3_384 = sha3.BLOCK_SIZE_384,
|
|
.SHA3_512 = sha3.BLOCK_SIZE_512,
|
|
.SM3 = sm3.BLOCK_SIZE,
|
|
.Legacy_KECCAK_224 = keccak.BLOCK_SIZE_224,
|
|
.Legacy_KECCAK_256 = keccak.BLOCK_SIZE_256,
|
|
.Legacy_KECCAK_384 = keccak.BLOCK_SIZE_384,
|
|
.Legacy_KECCAK_512 = keccak.BLOCK_SIZE_512,
|
|
.Insecure_MD5 = md5.BLOCK_SIZE,
|
|
.Insecure_SHA1 = sha1.BLOCK_SIZE,
|
|
}
|
|
|
|
// Context is a concrete instantiation of a specific hash algorithm.
|
|
Context :: struct {
|
|
_algo: Algorithm,
|
|
_impl: union {
|
|
blake2b.Context,
|
|
blake2s.Context,
|
|
sha2.Context_256,
|
|
sha2.Context_512,
|
|
sha3.Context,
|
|
sm3.Context,
|
|
keccak.Context,
|
|
md5.Context,
|
|
sha1.Context,
|
|
},
|
|
}
|
|
|
|
@(private)
|
|
_IMPL_IDS := [Algorithm]typeid {
|
|
.Invalid = nil,
|
|
.BLAKE2B = typeid_of(blake2b.Context),
|
|
.BLAKE2S = typeid_of(blake2s.Context),
|
|
.SHA224 = typeid_of(sha2.Context_256),
|
|
.SHA256 = typeid_of(sha2.Context_256),
|
|
.SHA384 = typeid_of(sha2.Context_512),
|
|
.SHA512 = typeid_of(sha2.Context_512),
|
|
.SHA512_256 = typeid_of(sha2.Context_512),
|
|
.SHA3_224 = typeid_of(sha3.Context),
|
|
.SHA3_256 = typeid_of(sha3.Context),
|
|
.SHA3_384 = typeid_of(sha3.Context),
|
|
.SHA3_512 = typeid_of(sha3.Context),
|
|
.SM3 = typeid_of(sm3.Context),
|
|
.Legacy_KECCAK_224 = typeid_of(keccak.Context),
|
|
.Legacy_KECCAK_256 = typeid_of(keccak.Context),
|
|
.Legacy_KECCAK_384 = typeid_of(keccak.Context),
|
|
.Legacy_KECCAK_512 = typeid_of(keccak.Context),
|
|
.Insecure_MD5 = typeid_of(md5.Context),
|
|
.Insecure_SHA1 = typeid_of(sha1.Context),
|
|
}
|
|
|
|
// init initializes a Context with a specific hash Algorithm.
|
|
init :: proc(ctx: ^Context, algorithm: Algorithm) {
|
|
if ctx._impl != nil {
|
|
reset(ctx)
|
|
}
|
|
|
|
// 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 .BLAKE2B:
|
|
blake2b.init(&ctx._impl.(blake2b.Context))
|
|
case .BLAKE2S:
|
|
blake2s.init(&ctx._impl.(blake2s.Context))
|
|
case .SHA224:
|
|
sha2.init_224(&ctx._impl.(sha2.Context_256))
|
|
case .SHA256:
|
|
sha2.init_256(&ctx._impl.(sha2.Context_256))
|
|
case .SHA384:
|
|
sha2.init_384(&ctx._impl.(sha2.Context_512))
|
|
case .SHA512:
|
|
sha2.init_512(&ctx._impl.(sha2.Context_512))
|
|
case .SHA512_256:
|
|
sha2.init_512_256(&ctx._impl.(sha2.Context_512))
|
|
case .SHA3_224:
|
|
sha3.init_224(&ctx._impl.(sha3.Context))
|
|
case .SHA3_256:
|
|
sha3.init_256(&ctx._impl.(sha3.Context))
|
|
case .SHA3_384:
|
|
sha3.init_384(&ctx._impl.(sha3.Context))
|
|
case .SHA3_512:
|
|
sha3.init_512(&ctx._impl.(sha3.Context))
|
|
case .SM3:
|
|
sm3.init(&ctx._impl.(sm3.Context))
|
|
case .Legacy_KECCAK_224:
|
|
keccak.init_224(&ctx._impl.(keccak.Context))
|
|
case .Legacy_KECCAK_256:
|
|
keccak.init_256(&ctx._impl.(keccak.Context))
|
|
case .Legacy_KECCAK_384:
|
|
keccak.init_384(&ctx._impl.(keccak.Context))
|
|
case .Legacy_KECCAK_512:
|
|
keccak.init_512(&ctx._impl.(keccak.Context))
|
|
case .Insecure_MD5:
|
|
md5.init(&ctx._impl.(md5.Context))
|
|
case .Insecure_SHA1:
|
|
sha1.init(&ctx._impl.(sha1.Context))
|
|
case .Invalid:
|
|
panic("crypto/hash: uninitialized algorithm")
|
|
case:
|
|
panic("crypto/hash: invalid algorithm")
|
|
}
|
|
|
|
ctx._algo = algorithm
|
|
}
|
|
|
|
// update adds more data to the Context.
|
|
update :: proc(ctx: ^Context, data: []byte) {
|
|
switch &impl in ctx._impl {
|
|
case blake2b.Context:
|
|
blake2b.update(&impl, data)
|
|
case blake2s.Context:
|
|
blake2s.update(&impl, data)
|
|
case sha2.Context_256:
|
|
sha2.update(&impl, data)
|
|
case sha2.Context_512:
|
|
sha2.update(&impl, data)
|
|
case sha3.Context:
|
|
sha3.update(&impl, data)
|
|
case sm3.Context:
|
|
sm3.update(&impl, data)
|
|
case keccak.Context:
|
|
keccak.update(&impl, data)
|
|
case md5.Context:
|
|
md5.update(&impl, data)
|
|
case sha1.Context:
|
|
sha1.update(&impl, data)
|
|
case:
|
|
panic("crypto/hash: uninitialized algorithm")
|
|
}
|
|
}
|
|
|
|
// final finalizes the Context, writes the digest to hash, and calls
|
|
// reset on the Context.
|
|
//
|
|
// If and only if (⟺) finalize_clone is set, final will work on a copy of the Context,
|
|
// which is useful for for calculating rolling digests.
|
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
|
switch &impl in ctx._impl {
|
|
case blake2b.Context:
|
|
blake2b.final(&impl, hash, finalize_clone)
|
|
case blake2s.Context:
|
|
blake2s.final(&impl, hash, finalize_clone)
|
|
case sha2.Context_256:
|
|
sha2.final(&impl, hash, finalize_clone)
|
|
case sha2.Context_512:
|
|
sha2.final(&impl, hash, finalize_clone)
|
|
case sha3.Context:
|
|
sha3.final(&impl, hash, finalize_clone)
|
|
case sm3.Context:
|
|
sm3.final(&impl, hash, finalize_clone)
|
|
case keccak.Context:
|
|
keccak.final(&impl, hash, finalize_clone)
|
|
case md5.Context:
|
|
md5.final(&impl, hash, finalize_clone)
|
|
case sha1.Context:
|
|
sha1.final(&impl, hash, finalize_clone)
|
|
case:
|
|
panic("crypto/hash: uninitialized algorithm")
|
|
}
|
|
|
|
if !finalize_clone {
|
|
reset(ctx)
|
|
}
|
|
}
|
|
|
|
// clone clones the Context other into ctx.
|
|
clone :: proc(ctx, other: ^Context) {
|
|
// XXX/yawning: Maybe these cases should panic, because both cases,
|
|
// are probably bugs.
|
|
if ctx == other {
|
|
return
|
|
}
|
|
if ctx._impl != nil {
|
|
reset(ctx)
|
|
}
|
|
|
|
ctx._algo = other._algo
|
|
|
|
reflect.set_union_variant_typeid(
|
|
ctx._impl,
|
|
reflect.union_variant_typeid(other._impl),
|
|
)
|
|
switch &src_impl in other._impl {
|
|
case blake2b.Context:
|
|
blake2b.clone(&ctx._impl.(blake2b.Context), &src_impl)
|
|
case blake2s.Context:
|
|
blake2s.clone(&ctx._impl.(blake2s.Context), &src_impl)
|
|
case sha2.Context_256:
|
|
sha2.clone(&ctx._impl.(sha2.Context_256), &src_impl)
|
|
case sha2.Context_512:
|
|
sha2.clone(&ctx._impl.(sha2.Context_512), &src_impl)
|
|
case sha3.Context:
|
|
sha3.clone(&ctx._impl.(sha3.Context), &src_impl)
|
|
case sm3.Context:
|
|
sm3.clone(&ctx._impl.(sm3.Context), &src_impl)
|
|
case keccak.Context:
|
|
keccak.clone(&ctx._impl.(keccak.Context), &src_impl)
|
|
case md5.Context:
|
|
md5.clone(&ctx._impl.(md5.Context), &src_impl)
|
|
case sha1.Context:
|
|
sha1.clone(&ctx._impl.(sha1.Context), &src_impl)
|
|
case:
|
|
panic("crypto/hash: 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 blake2b.Context:
|
|
blake2b.reset(&impl)
|
|
case blake2s.Context:
|
|
blake2s.reset(&impl)
|
|
case sha2.Context_256:
|
|
sha2.reset(&impl)
|
|
case sha2.Context_512:
|
|
sha2.reset(&impl)
|
|
case sha3.Context:
|
|
sha3.reset(&impl)
|
|
case sm3.Context:
|
|
sm3.reset(&impl)
|
|
case keccak.Context:
|
|
keccak.reset(&impl)
|
|
case md5.Context:
|
|
md5.reset(&impl)
|
|
case sha1.Context:
|
|
sha1.reset(&impl)
|
|
case:
|
|
// Unlike clone, 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
|
|
}
|
|
|
|
// digest_size returns the digest size of a Context instance in bytes.
|
|
digest_size :: proc(ctx: ^Context) -> int {
|
|
return DIGEST_SIZES[ctx._algo]
|
|
}
|
|
|
|
// block_size returns the block size of a Context instance in bytes.
|
|
block_size :: proc(ctx: ^Context) -> int {
|
|
return BLOCK_SIZES[ctx._algo]
|
|
}
|