core:crypto/hash: Add a generic higher level hash interface

There is a lot of code duplicated in convenience methods in each hash
implementation, and having a generic hash type makes implementing
higher-level constructs such as HMAC significantly easier down the road.
This commit is contained in:
Yawning Angel
2024-02-07 00:37:18 +09:00
parent ca10fc2d47
commit 00ab3beed9
18 changed files with 1531 additions and 2255 deletions
+64 -43
View File
@@ -12,10 +12,11 @@ package _sha3
*/
import "core:math/bits"
import "core:mem"
ROUNDS :: 24
Sha3_Context :: struct {
Context :: struct {
st: struct #raw_union {
b: [200]u8,
q: [25]u64,
@@ -103,81 +104,101 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
}
}
init :: proc(c: ^Sha3_Context) {
init :: proc(ctx: ^Context) {
for i := 0; i < 25; i += 1 {
c.st.q[i] = 0
ctx.st.q[i] = 0
}
c.rsiz = 200 - 2 * c.mdlen
c.pt = 0
ctx.rsiz = 200 - 2 * ctx.mdlen
ctx.pt = 0
c.is_initialized = true
c.is_finalized = false
ctx.is_initialized = true
ctx.is_finalized = false
}
update :: proc(c: ^Sha3_Context, data: []byte) {
assert(c.is_initialized)
assert(!c.is_finalized)
update :: proc(ctx: ^Context, data: []byte) {
assert(ctx.is_initialized)
assert(!ctx.is_finalized)
j := c.pt
j := ctx.pt
for i := 0; i < len(data); i += 1 {
c.st.b[j] ~= data[i]
ctx.st.b[j] ~= data[i]
j += 1
if j >= c.rsiz {
keccakf(&c.st.q)
if j >= ctx.rsiz {
keccakf(&ctx.st.q)
j = 0
}
}
c.pt = j
ctx.pt = j
}
final :: proc(c: ^Sha3_Context, hash: []byte) {
assert(c.is_initialized)
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
assert(ctx.is_initialized)
if len(hash) < c.mdlen {
if c.is_keccak {
if len(hash) < ctx.mdlen {
if ctx.is_keccak {
panic("crypto/keccac: invalid destination digest size")
}
panic("crypto/sha3: invalid destination digest size")
}
if c.is_keccak {
c.st.b[c.pt] ~= 0x01
ctx := ctx
if finalize_clone {
tmp_ctx: Context
clone(&tmp_ctx, ctx)
ctx = &tmp_ctx
}
defer(reset(ctx))
if ctx.is_keccak {
ctx.st.b[ctx.pt] ~= 0x01
} else {
c.st.b[c.pt] ~= 0x06
ctx.st.b[ctx.pt] ~= 0x06
}
c.st.b[c.rsiz - 1] ~= 0x80
keccakf(&c.st.q)
for i := 0; i < c.mdlen; i += 1 {
hash[i] = c.st.b[i]
ctx.st.b[ctx.rsiz - 1] ~= 0x80
keccakf(&ctx.st.q)
for i := 0; i < ctx.mdlen; i += 1 {
hash[i] = ctx.st.b[i]
}
}
clone :: proc(ctx, other: ^Context) {
ctx^ = other^
}
reset :: proc(ctx: ^Context) {
if !ctx.is_initialized {
return
}
c.is_initialized = false // No more absorb, no more squeeze.
mem.zero_explicit(ctx, size_of(ctx^))
}
shake_xof :: proc(c: ^Sha3_Context) {
assert(c.is_initialized)
assert(!c.is_finalized)
c.st.b[c.pt] ~= 0x1F
c.st.b[c.rsiz - 1] ~= 0x80
keccakf(&c.st.q)
c.pt = 0
shake_xof :: proc(ctx: ^Context) {
assert(ctx.is_initialized)
assert(!ctx.is_finalized)
c.is_finalized = true // No more absorb, unlimited squeeze.
ctx.st.b[ctx.pt] ~= 0x1F
ctx.st.b[ctx.rsiz - 1] ~= 0x80
keccakf(&ctx.st.q)
ctx.pt = 0
ctx.is_finalized = true // No more absorb, unlimited squeeze.
}
shake_out :: proc(c: ^Sha3_Context, hash: []byte) {
assert(c.is_initialized)
assert(c.is_finalized)
shake_out :: proc(ctx: ^Context, hash: []byte) {
assert(ctx.is_initialized)
assert(ctx.is_finalized)
j := c.pt
j := ctx.pt
for i := 0; i < len(hash); i += 1 {
if j >= c.rsiz {
keccakf(&c.st.q)
if j >= ctx.rsiz {
keccakf(&ctx.st.q)
j = 0
}
hash[i] = c.st.b[j]
hash[i] = ctx.st.b[j]
j += 1
}
c.pt = j
ctx.pt = j
}