mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-21 05:45:19 +00:00
This change was made in order to allow things produced with Odin and using Odin's core library, to not require the LICENSE to also be distributed alongside the binary form.
212 lines
4.5 KiB
Odin
212 lines
4.5 KiB
Odin
package _sha3
|
|
|
|
/*
|
|
Copyright 2021 zhibog
|
|
Made available under Odin's license.
|
|
|
|
List of contributors:
|
|
zhibog, dotbmp: Initial implementation.
|
|
|
|
Implementation of the Keccak hashing algorithm, standardized as SHA3
|
|
in <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf>.
|
|
|
|
As the only difference between the legacy Keccak and SHA3 is the domain
|
|
separation byte, set dsbyte to the appropriate value to pick the desired
|
|
algorithm.
|
|
*/
|
|
|
|
import "core:math/bits"
|
|
import "core:mem"
|
|
|
|
ROUNDS :: 24
|
|
|
|
RATE_128 :: 1344 / 8 // ONLY for SHAKE128.
|
|
RATE_224 :: 1152 / 8
|
|
RATE_256 :: 1088 / 8
|
|
RATE_384 :: 832 / 8
|
|
RATE_512 :: 576 / 8
|
|
|
|
DS_KECCAK :: 0x01
|
|
DS_SHA3 :: 0x06
|
|
DS_SHAKE :: 0x1f
|
|
DS_CSHAKE :: 0x04
|
|
|
|
Context :: struct {
|
|
st: struct #raw_union {
|
|
b: [200]u8,
|
|
q: [25]u64,
|
|
},
|
|
pt: int,
|
|
rsiz: int,
|
|
mdlen: int,
|
|
dsbyte: byte,
|
|
is_initialized: bool,
|
|
is_finalized: bool, // For SHAKE (unlimited squeeze is allowed)
|
|
}
|
|
|
|
@(private, rodata)
|
|
keccakf_rndc := [?]u64 {
|
|
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
|
|
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
|
|
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
|
|
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
|
|
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
|
|
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
|
|
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
|
|
0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
|
|
}
|
|
|
|
@(private, rodata)
|
|
keccakf_rotc := [?]int {
|
|
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
|
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
|
|
}
|
|
|
|
@(private, rodata)
|
|
keccakf_piln := [?]i32 {
|
|
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
|
|
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
|
|
}
|
|
|
|
@(private)
|
|
keccakf :: proc "contextless" (st: ^[25]u64) {
|
|
i, j, r: i32 = ---, ---, ---
|
|
t: u64 = ---
|
|
bc: [5]u64 = ---
|
|
|
|
when ODIN_ENDIAN != .Little {
|
|
for i = 0; i < 25; i += 1 {
|
|
st[i] = bits.byte_swap(st[i])
|
|
}
|
|
}
|
|
|
|
for r = 0; r < ROUNDS; r += 1 {
|
|
// theta
|
|
for i = 0; i < 5; i += 1 {
|
|
bc[i] = st[i] ~ st[i + 5] ~ st[i + 10] ~ st[i + 15] ~ st[i + 20]
|
|
}
|
|
|
|
for i = 0; i < 5; i += 1 {
|
|
t = bc[(i + 4) % 5] ~ bits.rotate_left64(bc[(i + 1) % 5], 1)
|
|
for j = 0; j < 25; j += 5 {
|
|
st[j + i] ~= t
|
|
}
|
|
}
|
|
|
|
// rho pi
|
|
t = st[1]
|
|
for i = 0; i < 24; i += 1 {
|
|
j = keccakf_piln[i]
|
|
bc[0] = st[j]
|
|
st[j] = bits.rotate_left64(t, keccakf_rotc[i])
|
|
t = bc[0]
|
|
}
|
|
|
|
// chi
|
|
for j = 0; j < 25; j += 5 {
|
|
for i = 0; i < 5; i += 1 {
|
|
bc[i] = st[j + i]
|
|
}
|
|
for i = 0; i < 5; i += 1 {
|
|
st[j + i] ~= ~bc[(i + 1) % 5] & bc[(i + 2) % 5]
|
|
}
|
|
}
|
|
|
|
st[0] ~= keccakf_rndc[r]
|
|
}
|
|
|
|
when ODIN_ENDIAN != .Little {
|
|
for i = 0; i < 25; i += 1 {
|
|
st[i] = bits.byte_swap(st[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
init :: proc "contextless" (ctx: ^Context) {
|
|
for i := 0; i < 25; i += 1 {
|
|
ctx.st.q[i] = 0
|
|
}
|
|
ctx.rsiz = 200 - 2 * ctx.mdlen
|
|
ctx.pt = 0
|
|
|
|
ctx.is_initialized = true
|
|
ctx.is_finalized = false
|
|
}
|
|
|
|
update :: proc "contextless" (ctx: ^Context, data: []byte) {
|
|
ensure_contextless(ctx.is_initialized)
|
|
ensure_contextless(!ctx.is_finalized)
|
|
|
|
j := ctx.pt
|
|
for i := 0; i < len(data); i += 1 {
|
|
ctx.st.b[j] ~= data[i]
|
|
j += 1
|
|
if j >= ctx.rsiz {
|
|
keccakf(&ctx.st.q)
|
|
j = 0
|
|
}
|
|
}
|
|
ctx.pt = j
|
|
}
|
|
|
|
final :: proc "contextless" (ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
|
ensure_contextless(ctx.is_initialized)
|
|
ensure_contextless(len(hash) >= ctx.mdlen, "crypto/sha3: invalid destination digest size")
|
|
|
|
ctx := ctx
|
|
if finalize_clone {
|
|
tmp_ctx: Context
|
|
clone(&tmp_ctx, ctx)
|
|
ctx = &tmp_ctx
|
|
}
|
|
defer (reset(ctx))
|
|
|
|
ctx.st.b[ctx.pt] ~= ctx.dsbyte
|
|
|
|
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 "contextless" (ctx, other: ^Context) {
|
|
ctx^ = other^
|
|
}
|
|
|
|
reset :: proc "contextless" (ctx: ^Context) {
|
|
if !ctx.is_initialized {
|
|
return
|
|
}
|
|
|
|
mem.zero_explicit(ctx, size_of(ctx^))
|
|
}
|
|
|
|
shake_xof :: proc "contextless" (ctx: ^Context) {
|
|
ensure_contextless(ctx.is_initialized)
|
|
ensure_contextless(!ctx.is_finalized)
|
|
|
|
ctx.st.b[ctx.pt] ~= ctx.dsbyte
|
|
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 "contextless" (ctx: ^Context, hash: []byte) {
|
|
ensure_contextless(ctx.is_initialized)
|
|
ensure_contextless(ctx.is_finalized)
|
|
|
|
j := ctx.pt
|
|
for i := 0; i < len(hash); i += 1 {
|
|
if j >= ctx.rsiz {
|
|
keccakf(&ctx.st.q)
|
|
j = 0
|
|
}
|
|
hash[i] = ctx.st.b[j]
|
|
j += 1
|
|
}
|
|
ctx.pt = j
|
|
}
|