diff --git a/core/crypto/hash/hash.odin b/core/crypto/hash/hash.odin index e861be72e..0840910c1 100644 --- a/core/crypto/hash/hash.odin +++ b/core/crypto/hash/hash.odin @@ -39,7 +39,7 @@ hash_string_to_buffer :: proc(algorithm: Algorithm, data: string, hash: []byte) hash_bytes_to_buffer :: proc(algorithm: Algorithm, data, hash: []byte) { ctx: Context - init(&ctx, algorithm, context.temp_allocator) + init(&ctx, algorithm) update(&ctx, data) final(&ctx, hash) } @@ -56,7 +56,7 @@ hash_stream :: proc( ) { ctx: Context - init(&ctx, algorithm, context.temp_allocator) + init(&ctx, algorithm) buffer_size := block_size(&ctx) * 4 buf := make([]byte, buffer_size, context.temp_allocator) diff --git a/core/crypto/hash/low_level.odin b/core/crypto/hash/low_level.odin index 696b521fd..2b40a0a7c 100644 --- a/core/crypto/hash/low_level.odin +++ b/core/crypto/hash/low_level.odin @@ -9,7 +9,7 @@ import "core:crypto/legacy/keccak" import "core:crypto/legacy/md5" import "core:crypto/legacy/sha1" -import "core:mem" +import "core:reflect" // Algorithm is the algorithm identifier associated with a given Context. Algorithm :: enum { @@ -107,101 +107,89 @@ BLOCK_SIZES := [Algorithm]int { 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, + blake2b.Context, + blake2s.Context, + sha2.Context_256, + sha2.Context_512, + sha3.Context, + sm3.Context, + keccak.Context, + md5.Context, + sha1.Context, }, - _allocator: mem.Allocator, +} + +@(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. -// -// Warning: Internal state is allocated, and resources must be freed -// either implicitly via a call to final, or explicitly via calling reset. -init :: proc(ctx: ^Context, algorithm: Algorithm, allocator := context.allocator) { +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: - impl := new(blake2b.Context, allocator) - blake2b.init(impl) - ctx._impl = impl + blake2b.init(&ctx._impl.(blake2b.Context)) case .BLAKE2S: - impl := new(blake2s.Context, allocator) - blake2s.init(impl) - ctx._impl = impl + blake2s.init(&ctx._impl.(blake2s.Context)) case .SHA224: - impl := new(sha2.Context_256, allocator) - sha2.init_224(impl) - ctx._impl = impl + sha2.init_224(&ctx._impl.(sha2.Context_256)) case .SHA256: - impl := new(sha2.Context_256, allocator) - sha2.init_256(impl) - ctx._impl = impl + sha2.init_256(&ctx._impl.(sha2.Context_256)) case .SHA384: - impl := new(sha2.Context_512, allocator) - sha2.init_384(impl) - ctx._impl = impl + sha2.init_384(&ctx._impl.(sha2.Context_512)) case .SHA512: - impl := new(sha2.Context_512, allocator) - sha2.init_512(impl) - ctx._impl = impl + sha2.init_512(&ctx._impl.(sha2.Context_512)) case .SHA512_256: - impl := new(sha2.Context_512, allocator) - sha2.init_512_256(impl) - ctx._impl = impl + sha2.init_512_256(&ctx._impl.(sha2.Context_512)) case .SHA3_224: - impl := new(sha3.Context, allocator) - sha3.init_224(impl) - ctx._impl = impl + sha3.init_224(&ctx._impl.(sha3.Context)) case .SHA3_256: - impl := new(sha3.Context, allocator) - sha3.init_256(impl) - ctx._impl = impl + sha3.init_256(&ctx._impl.(sha3.Context)) case .SHA3_384: - impl := new(sha3.Context, allocator) - sha3.init_384(impl) - ctx._impl = impl + sha3.init_384(&ctx._impl.(sha3.Context)) case .SHA3_512: - impl := new(sha3.Context, allocator) - sha3.init_512(impl) - ctx._impl = impl + sha3.init_512(&ctx._impl.(sha3.Context)) case .SM3: - impl := new(sm3.Context, allocator) - sm3.init(impl) - ctx._impl = impl + sm3.init(&ctx._impl.(sm3.Context)) case .Legacy_KECCAK_224: - impl := new(keccak.Context, allocator) - keccak.init_224(impl) - ctx._impl = impl + keccak.init_224(&ctx._impl.(keccak.Context)) case .Legacy_KECCAK_256: - impl := new(keccak.Context, allocator) - keccak.init_256(impl) - ctx._impl = impl + keccak.init_256(&ctx._impl.(keccak.Context)) case .Legacy_KECCAK_384: - impl := new(keccak.Context, allocator) - keccak.init_384(impl) - ctx._impl = impl + keccak.init_384(&ctx._impl.(keccak.Context)) case .Legacy_KECCAK_512: - impl := new(keccak.Context, allocator) - keccak.init_512(impl) - ctx._impl = impl + keccak.init_512(&ctx._impl.(keccak.Context)) case .Insecure_MD5: - impl := new(md5.Context, allocator) - md5.init(impl) - ctx._impl = impl + md5.init(&ctx._impl.(md5.Context)) case .Insecure_SHA1: - impl := new(sha1.Context, allocator) - sha1.init(impl) - ctx._impl = impl + sha1.init(&ctx._impl.(sha1.Context)) case .Invalid: panic("crypto/hash: uninitialized algorithm") case: @@ -209,30 +197,29 @@ init :: proc(ctx: ^Context, algorithm: Algorithm, allocator := context.allocator } ctx._algo = algorithm - ctx._allocator = allocator } // 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) + 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") } @@ -244,25 +231,25 @@ update :: proc(ctx: ^Context, data: []byte) { // Iff 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) + 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") } @@ -273,7 +260,7 @@ final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) { } // clone clones the Context other into ctx. -clone :: proc(ctx, other: ^Context, allocator := context.allocator) { +clone :: proc(ctx, other: ^Context) { // XXX/yawning: Maybe these cases should panic, because both cases, // are probably bugs. if ctx == other { @@ -284,45 +271,30 @@ clone :: proc(ctx, other: ^Context, allocator := context.allocator) { } ctx._algo = other._algo - ctx._allocator = allocator - switch src_impl in other._impl { - case ^blake2b.Context: - impl := new(blake2b.Context, allocator) - blake2b.clone(impl, src_impl) - ctx._impl = impl - case ^blake2s.Context: - impl := new(blake2s.Context, allocator) - blake2s.clone(impl, src_impl) - ctx._impl = impl - case ^sha2.Context_256: - impl := new(sha2.Context_256, allocator) - sha2.clone(impl, src_impl) - ctx._impl = impl - case ^sha2.Context_512: - impl := new(sha2.Context_512, allocator) - sha2.clone(impl, src_impl) - ctx._impl = impl - case ^sha3.Context: - impl := new(sha3.Context, allocator) - sha3.clone(impl, src_impl) - ctx._impl = impl - case ^sm3.Context: - impl := new(sm3.Context, allocator) - sm3.clone(impl, src_impl) - ctx._impl = impl - case ^keccak.Context: - impl := new(keccak.Context, allocator) - keccak.clone(impl, src_impl) - ctx._impl = impl - case ^md5.Context: - impl := new(md5.Context, allocator) - md5.clone(impl, src_impl) - ctx._impl = impl - case ^sha1.Context: - impl := new(sha1.Context, allocator) - sha1.clone(impl, src_impl) - ctx._impl = impl + 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") } @@ -331,34 +303,25 @@ clone :: proc(ctx, other: ^Context, allocator := context.allocator) { // 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) - free(impl, ctx._allocator) - case ^blake2s.Context: - blake2s.reset(impl) - free(impl, ctx._allocator) - case ^sha2.Context_256: - sha2.reset(impl) - free(impl, ctx._allocator) - case ^sha2.Context_512: - sha2.reset(impl) - free(impl, ctx._allocator) - case ^sha3.Context: - sha3.reset(impl) - free(impl, ctx._allocator) - case ^sm3.Context: - sm3.reset(impl) - free(impl, ctx._allocator) - case ^keccak.Context: - keccak.reset(impl) - free(impl, ctx._allocator) - case ^md5.Context: - md5.reset(impl) - free(impl, ctx._allocator) - case ^sha1.Context: - sha1.reset(impl) - free(impl, ctx._allocator) + 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. } diff --git a/tests/core/crypto/test_core_crypto_hash.odin b/tests/core/crypto/test_core_crypto_hash.odin index 570a8d88d..1646c1baf 100644 --- a/tests/core/crypto/test_core_crypto_hash.odin +++ b/tests/core/crypto/test_core_crypto_hash.odin @@ -538,8 +538,8 @@ test_hash :: proc(t: ^testing.T) { // Exercise the rolling digest functionality, which also covers // each implementation's clone routine. - ctx: hash.Context - hash.init(&ctx, algo, context.temp_allocator) + ctx, ctx_clone: hash.Context + hash.init(&ctx, algo) api_algo := hash.algorithm(&ctx) api_digest_size := hash.digest_size(&ctx) @@ -565,20 +565,26 @@ test_hash :: proc(t: ^testing.T) { ) hash.update(&ctx, digest_a) + hash.clone(&ctx_clone, &ctx) hash.final(&ctx, digest_a, true) hash.final(&ctx, digest_b) + digest_c := make([]byte, hash.digest_size(&ctx_clone), context.temp_allocator) + hash.final(&ctx_clone, digest_c) + a_str = string(hex.encode(digest_a, context.temp_allocator)) b_str = string(hex.encode(digest_b, context.temp_allocator)) + c_str := string(hex.encode(digest_c, context.temp_allocator)) expect( t, - a_str == b_str, + a_str == b_str && b_str == c_str, fmt.tprintf( - "%s/rolling: Expected: %s (first) == %s (second)", + "%s/rolling: Expected: %s (first) == %s (second) == %s (third)", algo_name, a_str, b_str, + c_str, ), ) }