mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
core/crypto/shake: Support cSHAKE
This commit is contained in:
@@ -20,9 +20,7 @@ import "core:mem"
|
|||||||
|
|
||||||
ROUNDS :: 24
|
ROUNDS :: 24
|
||||||
|
|
||||||
RATE_SHAKE_128 :: 168
|
RATE_128 :: 1344 / 8 // ONLY for SHAKE128.
|
||||||
RATE_SHAKE_256 :: 136
|
|
||||||
|
|
||||||
RATE_224 :: 1152 / 8
|
RATE_224 :: 1152 / 8
|
||||||
RATE_256 :: 1088 / 8
|
RATE_256 :: 1088 / 8
|
||||||
RATE_384 :: 832 / 8
|
RATE_384 :: 832 / 8
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package _sha3
|
||||||
|
|
||||||
|
import "core:encoding/endian"
|
||||||
|
import "core:math/bits"
|
||||||
|
|
||||||
|
init_cshake :: proc(ctx: ^Context, n, s: []byte, sec_strength: int) {
|
||||||
|
rate: int
|
||||||
|
switch sec_strength {
|
||||||
|
case 128:
|
||||||
|
rate = RATE_128
|
||||||
|
case 256:
|
||||||
|
rate = RATE_256
|
||||||
|
case:
|
||||||
|
panic("crypto/sha3: invalid security strength")
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
_PAD: [RATE_128]byte // Biggest possible value of w per spec.
|
||||||
|
|
||||||
|
bytepad :: proc(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.
|
||||||
|
if carry != 0 {
|
||||||
|
panic("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))
|
||||||
|
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(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(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(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))
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
package shake implements the SHAKE XOF algorithm family.
|
package shake implements the SHAKE and cSHAKE XOF algorithm families.
|
||||||
|
|
||||||
The SHA3 hash algorithm can be found in the crypto/sha3.
|
The SHA3 hash algorithm can be found in the crypto/sha3.
|
||||||
|
|
||||||
See:
|
See:
|
||||||
- https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf
|
- https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf
|
||||||
|
- https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
|
||||||
*/
|
*/
|
||||||
package shake
|
package shake
|
||||||
|
|
||||||
@@ -18,25 +19,27 @@ package shake
|
|||||||
|
|
||||||
import "../_sha3"
|
import "../_sha3"
|
||||||
|
|
||||||
// Context is a SHAKE128 or SHAKE256 instance.
|
// Context is a SHAKE128, SHAKE256, cSHAKE128, or cSHAKE256 instance.
|
||||||
Context :: distinct _sha3.Context
|
Context :: distinct _sha3.Context
|
||||||
|
|
||||||
// init_128 initializes a Context for SHAKE128.
|
// init_128 initializes a Context for SHAKE128.
|
||||||
init_128 :: proc(ctx: ^Context) {
|
init_128 :: proc(ctx: ^Context) {
|
||||||
ctx.mdlen = 128 / 8
|
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, nil, 128)
|
||||||
_init(ctx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// init_256 initializes a Context for SHAKE256.
|
// init_256 initializes a Context for SHAKE256.
|
||||||
init_256 :: proc(ctx: ^Context) {
|
init_256 :: proc(ctx: ^Context) {
|
||||||
ctx.mdlen = 256 / 8
|
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, nil, 256)
|
||||||
_init(ctx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
// init_cshake_128 initializes a Context for cSHAKE128.
|
||||||
_init :: proc(ctx: ^Context) {
|
init_cshake_128 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||||
ctx.dsbyte = _sha3.DS_SHAKE
|
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, domain_sep, 128)
|
||||||
_sha3.init(transmute(^_sha3.Context)(ctx))
|
}
|
||||||
|
|
||||||
|
// init_cshake_256 initializes a Context for cSHAKE256.
|
||||||
|
init_cshake_256 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||||
|
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, domain_sep, 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
// write writes more data into the SHAKE instance. This MUST not be called
|
// write writes more data into the SHAKE instance. This MUST not be called
|
||||||
|
|||||||
@@ -415,6 +415,7 @@ test_rand_bytes :: proc(t: ^testing.T) {
|
|||||||
|
|
||||||
TestXOF :: struct {
|
TestXOF :: struct {
|
||||||
sec_strength: int,
|
sec_strength: int,
|
||||||
|
domainsep: string,
|
||||||
output: string,
|
output: string,
|
||||||
str: string,
|
str: string,
|
||||||
}
|
}
|
||||||
@@ -425,16 +426,19 @@ test_shake :: proc(t: ^testing.T) {
|
|||||||
// SHAKE128
|
// SHAKE128
|
||||||
{
|
{
|
||||||
128,
|
128,
|
||||||
|
"",
|
||||||
"7f9c2ba4e88f827d616045507605853e",
|
"7f9c2ba4e88f827d616045507605853e",
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
128,
|
128,
|
||||||
|
"",
|
||||||
"f4202e3c5852f9182a0430fd8144f0a7",
|
"f4202e3c5852f9182a0430fd8144f0a7",
|
||||||
"The quick brown fox jumps over the lazy dog",
|
"The quick brown fox jumps over the lazy dog",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
128,
|
128,
|
||||||
|
"",
|
||||||
"853f4538be0db9621a6cea659a06c110",
|
"853f4538be0db9621a6cea659a06c110",
|
||||||
"The quick brown fox jumps over the lazy dof",
|
"The quick brown fox jumps over the lazy dof",
|
||||||
},
|
},
|
||||||
@@ -442,31 +446,80 @@ test_shake :: proc(t: ^testing.T) {
|
|||||||
// SHAKE256
|
// SHAKE256
|
||||||
{
|
{
|
||||||
256,
|
256,
|
||||||
|
"",
|
||||||
"46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f",
|
"46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f",
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
256,
|
256,
|
||||||
|
"",
|
||||||
"2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca",
|
"2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca",
|
||||||
"The quick brown fox jumps over the lazy dog",
|
"The quick brown fox jumps over the lazy dog",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
256,
|
256,
|
||||||
|
"",
|
||||||
"46b1ebb2e142c38b9ac9081bef72877fe4723959640fa57119b366ce6899d401",
|
"46b1ebb2e142c38b9ac9081bef72877fe4723959640fa57119b366ce6899d401",
|
||||||
"The quick brown fox jumps over the lazy dof",
|
"The quick brown fox jumps over the lazy dof",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// cSHAKE128
|
||||||
|
// - https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/cSHAKE_samples.pdf
|
||||||
|
{
|
||||||
|
128,
|
||||||
|
"Email Signature",
|
||||||
|
"c1c36925b6409a04f1b504fcbca9d82b4017277cb5ed2b2065fc1d3814d5aaf5",
|
||||||
|
"00010203",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
128,
|
||||||
|
"Email Signature",
|
||||||
|
"c5221d50e4f822d96a2e8881a961420f294b7b24fe3d2094baed2c6524cc166b",
|
||||||
|
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7",
|
||||||
|
},
|
||||||
|
|
||||||
|
// cSHAKE256
|
||||||
|
// - https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/cSHAKE_samples.pdf
|
||||||
|
{
|
||||||
|
256,
|
||||||
|
"Email Signature",
|
||||||
|
"d008828e2b80ac9d2218ffee1d070c48b8e4c87bff32c9699d5b6896eee0edd164020e2be0560858d9c00c037e34a96937c561a74c412bb4c746469527281c8c",
|
||||||
|
"00010203",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
256,
|
||||||
|
"Email Signature",
|
||||||
|
"07dc27b11e51fbac75bc7b3c1d983e8b4b85fb1defaf218912ac86430273091727f42b17ed1df63e8ec118f04b23633c1dfb1574c8fb55cb45da8e25afb092bb",
|
||||||
|
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for v in test_vectors {
|
for v in test_vectors {
|
||||||
dst := make([]byte, len(v.output)/2, context.temp_allocator)
|
dst := make([]byte, len(v.output)/2, context.temp_allocator)
|
||||||
|
|
||||||
data := transmute([]byte)(v.str)
|
data := transmute([]byte)(v.str)
|
||||||
|
domainsep := transmute([]byte)(v.domainsep)
|
||||||
|
|
||||||
|
alg_prefix := ""
|
||||||
ctx: shake.Context
|
ctx: shake.Context
|
||||||
switch v.sec_strength {
|
if len(domainsep) == 0 {
|
||||||
case 128:
|
switch v.sec_strength {
|
||||||
shake.init_128(&ctx)
|
case 128:
|
||||||
case 256:
|
shake.init_128(&ctx)
|
||||||
shake.init_256(&ctx)
|
case 256:
|
||||||
|
shake.init_256(&ctx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alg_prefix = "c"
|
||||||
|
|
||||||
|
// The cSHAKE samples from NIST are binary data.
|
||||||
|
data, _ = hex.decode(data)
|
||||||
|
|
||||||
|
switch v.sec_strength {
|
||||||
|
case 128:
|
||||||
|
shake.init_cshake_128(&ctx, domainsep)
|
||||||
|
case 256:
|
||||||
|
shake.init_cshake_256(&ctx, domainsep)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shake.write(&ctx, data)
|
shake.write(&ctx, data)
|
||||||
@@ -478,7 +531,8 @@ test_shake :: proc(t: ^testing.T) {
|
|||||||
t,
|
t,
|
||||||
dst_str == v.output,
|
dst_str == v.output,
|
||||||
fmt.tprintf(
|
fmt.tprintf(
|
||||||
"SHAKE%d: Expected: %s for input of %s, but got %s instead",
|
"%sSHAKE%d: Expected: %s for input of %s, but got %s instead",
|
||||||
|
alg_prefix,
|
||||||
v.sec_strength,
|
v.sec_strength,
|
||||||
v.output,
|
v.output,
|
||||||
v.str,
|
v.str,
|
||||||
|
|||||||
Reference in New Issue
Block a user