package _sha3 import "core:encoding/endian" import "core:math/bits" init_cshake :: proc "contextless" (ctx: ^Context, n, s: []byte, sec_strength: int) { ctx.mdlen = sec_strength / 8 // No domain separator is equivalent to vanilla SHAKE. if len(n) == 0 && len(s) == 0 { ctx.dsbyte = DS_SHAKE init(ctx) return } ctx.dsbyte = DS_CSHAKE init(ctx) bytepad(ctx, [][]byte{n, s}, rate_cshake(sec_strength)) } final_cshake :: proc "contextless" (ctx: ^Context, dst: []byte, finalize_clone: bool = false) { ctx := ctx if finalize_clone { tmp_ctx: Context clone(&tmp_ctx, ctx) ctx = &tmp_ctx } defer reset(ctx) encode_byte_len(ctx, len(dst), false) // right_encode shake_xof(ctx) shake_out(ctx, dst) } rate_cshake :: #force_inline proc "contextless" (sec_strength: int) -> int { switch sec_strength { case 128: return RATE_128 case 256: return RATE_256 } panic_contextless("crypto/sha3: invalid security strength") } // right_encode and left_encode are defined to support 0 <= x < 2^2040 // however, the largest value we will ever need to encode is `max(int) * 8`. // // This is unfortunate as the extreme upper edge is larger than // `max(u64)`. While such values are impractical at present, // they are possible (ie: https://arxiv.org/pdf/quant-ph/9908043.pdf). // // Thus we support 0 <= x < 2^128. @(private, rodata) _PAD: [RATE_128]byte // Biggest possible value of w per spec. bytepad :: proc "contextless" (ctx: ^Context, x_strings: [][]byte, w: int) { // 1. z = left_encode(w) || X. z_hi: u64 z_lo := left_right_encode(ctx, 0, u64(w), true) for x in x_strings { // All uses of bytepad in SP 800-185 use the output from // one or more encode_string values for `X`. hi, lo := encode_string(ctx, x) carry: u64 z_lo, carry = bits.add_u64(z_lo, lo, 0) z_hi, carry = bits.add_u64(z_hi, hi, carry) // This isn't actually possible, at least with the currently // defined SP 800-185 routines. ensure_contextless(carry == 0, "crypto/sha3: bytepad input length overflow") } // We skip this step as we are doing a byte-oriented implementation // rather than a bit oriented one. // // 2. while len(z) mod 8 ≠ 0: // z = z || 0 // 3. while (len(z)/8) mod w != 0: // z = z || 00000000 z_len := u128(z_hi) << 64 | u128(z_lo) z_rem := int(z_len % u128(w)) if z_rem != 0 { pad := _PAD[:w - z_rem] // We just add the padding to the state, instead of returning z. // // 4. return z. update(ctx, pad) } } encode_string :: #force_inline proc "contextless" (ctx: ^Context, s: []byte) -> (u64, u64) { l := encode_byte_len(ctx, len(s), true) // left_encode update(ctx, s) lo, hi := bits.add_u64(l, u64(len(s)), 0) return hi, lo } encode_byte_len :: #force_inline proc "contextless" (ctx: ^Context, l: int, is_left: bool) -> u64 { hi, lo := bits.mul_u64(u64(l), 8) return left_right_encode(ctx, hi, lo, is_left) } @(private) left_right_encode :: proc "contextless" (ctx: ^Context, hi, lo: u64, is_left: bool) -> u64 { HI_OFFSET :: 1 LO_OFFSET :: HI_OFFSET + 8 RIGHT_OFFSET :: LO_OFFSET + 8 BUF_LEN :: RIGHT_OFFSET + 1 buf: [BUF_LEN]byte // prefix + largest uint + postfix endian.unchecked_put_u64be(buf[HI_OFFSET:], hi) endian.unchecked_put_u64be(buf[LO_OFFSET:], lo) // 2. Strip leading `0x00` bytes. off: int for off = HI_OFFSET; off < RIGHT_OFFSET - 1; off = off + 1 {// Note: Minimum size is 1, not 0. if buf[off] != 0 { break } } n := byte(RIGHT_OFFSET - off) // 3. Prefix (left_encode) or postfix (right_encode) the length in bytes. b: []byte switch is_left { case true: buf[off - 1] = n // n | x b = buf[off - 1:RIGHT_OFFSET] case false: buf[RIGHT_OFFSET] = n // x | n b = buf[off:] } update(ctx, b) return u64(len(b)) }