core/crypto/tuplehash: Initial import

This commit is contained in:
Yawning Angel
2024-03-02 19:04:49 +09:00
parent 15287a771f
commit c04a53e453
5 changed files with 424 additions and 131 deletions
+14
View File
@@ -28,6 +28,20 @@ init_cshake :: proc(ctx: ^Context, n, s: []byte, sec_strength: int) {
bytepad(ctx, [][]byte{n, s}, rate)
}
final_cshake :: proc(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)
}
// 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`.
//
+66
View File
@@ -0,0 +1,66 @@
/*
package tuplehash implements the TupleHash and TupleHashXOF algorithms.
See:
- https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
*/
package tuplehash
import "../_sha3"
// Context is a TupleHash or TupleHashXOF instance.
Context :: distinct _sha3.Context
// init_128 initializes a Context for TupleHash128 or TupleHashXOF128.
init_128 :: proc(ctx: ^Context, domain_sep: []byte) {
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 128)
}
// init_256 initializes a Context for TupleHash256 or TupleHashXOF256.
init_256 :: proc(ctx: ^Context, domain_sep: []byte) {
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 256)
}
// write_element writes a tuple element into the TupleHash or TupleHashXOF
// instance. This MUST not be called after any reads have been done, and
// any attempts to do so will panic.
write_element :: proc(ctx: ^Context, data: []byte) {
_, _ = _sha3.encode_string(transmute(^_sha3.Context)(ctx), data)
}
// final finalizes the Context, writes the digest to hash, and calls
// reset on the Context.
//
// 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) {
_sha3.final_cshake(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
}
// read reads output from the TupleHashXOF instance. There is no practical
// upper limit to the amount of data that can be read from TupleHashXOF.
// After read has been called one or more times, further calls to
// write_element will panic.
read :: proc(ctx: ^Context, dst: []byte) {
ctx_ := transmute(^_sha3.Context)(ctx)
if !ctx.is_finalized {
_sha3.encode_byte_len(ctx_, 0, false) // right_encode
_sha3.shake_xof(ctx_)
}
_sha3.shake_out(ctx_, dst)
}
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^Context) {
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) {
_sha3.reset(transmute(^_sha3.Context)(ctx))
}
@(private)
N_TUPLEHASH := []byte{'T', 'u', 'p', 'l', 'e', 'H', 'a', 's', 'h'}
+2
View File
@@ -40,6 +40,7 @@ import sha2 "core:crypto/sha2"
import sha3 "core:crypto/sha3"
import shake "core:crypto/shake"
import sm3 "core:crypto/sm3"
import tuplehash "core:crypto/tuplehash"
import x25519 "core:crypto/x25519"
import pe "core:debug/pe"
@@ -159,6 +160,7 @@ _ :: sha2
_ :: sha3
_ :: shake
_ :: sm3
_ :: tuplehash
_ :: x25519
_ :: pe
_ :: dynlib
+1 -131
View File
@@ -22,7 +22,6 @@ import "core:crypto"
import "core:crypto/chacha20"
import "core:crypto/chacha20poly1305"
import "core:crypto/shake"
import "core:crypto/x25519"
TEST_count := 0
@@ -57,8 +56,8 @@ main :: proc() {
test_chacha20(&t)
test_chacha20poly1305(&t)
test_shake(&t)
test_x25519(&t)
test_sha3_variants(&t)
bench_crypto(&t)
@@ -412,132 +411,3 @@ test_rand_bytes :: proc(t: ^testing.T) {
"Expected to randomize the head and tail of the buffer within a handful of attempts",
)
}
TestXOF :: struct {
sec_strength: int,
domainsep: string,
output: string,
str: string,
}
@(test)
test_shake :: proc(t: ^testing.T) {
test_vectors := [?]TestXOF {
// SHAKE128
{
128,
"",
"7f9c2ba4e88f827d616045507605853e",
"",
},
{
128,
"",
"f4202e3c5852f9182a0430fd8144f0a7",
"The quick brown fox jumps over the lazy dog",
},
{
128,
"",
"853f4538be0db9621a6cea659a06c110",
"The quick brown fox jumps over the lazy dof",
},
// SHAKE256
{
256,
"",
"46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f",
"",
},
{
256,
"",
"2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca",
"The quick brown fox jumps over the lazy dog",
},
{
256,
"",
"46b1ebb2e142c38b9ac9081bef72877fe4723959640fa57119b366ce6899d401",
"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 {
dst := make([]byte, len(v.output)/2, context.temp_allocator)
data := transmute([]byte)(v.str)
domainsep := transmute([]byte)(v.domainsep)
alg_prefix := ""
ctx: shake.Context
if len(domainsep) == 0 {
switch v.sec_strength {
case 128:
shake.init_128(&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.read(&ctx, dst)
dst_str := string(hex.encode(dst, context.temp_allocator))
expect(
t,
dst_str == v.output,
fmt.tprintf(
"%sSHAKE%d: Expected: %s for input of %s, but got %s instead",
alg_prefix,
v.sec_strength,
v.output,
v.str,
dst_str,
),
)
}
}
@@ -0,0 +1,341 @@
package test_core_crypto
import "core:encoding/hex"
import "core:fmt"
import "core:testing"
import "core:crypto/shake"
import "core:crypto/tuplehash"
@(test)
test_sha3_variants :: proc(t: ^testing.T) {
log(t, "Testing SHA3 derived functions")
test_shake(t)
test_cshake(t)
test_tuplehash(t)
}
@(test)
test_shake :: proc(t: ^testing.T) {
log(t, "Testing SHAKE")
test_vectors := []struct {
sec_strength: int,
output: string,
str: string,
} {
// SHAKE128
{128, "7f9c2ba4e88f827d616045507605853e", ""},
{128, "f4202e3c5852f9182a0430fd8144f0a7", "The quick brown fox jumps over the lazy dog"},
{128, "853f4538be0db9621a6cea659a06c110", "The quick brown fox jumps over the lazy dof"},
// SHAKE256
{256, "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f", ""},
{
256,
"2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca",
"The quick brown fox jumps over the lazy dog",
},
{
256,
"46b1ebb2e142c38b9ac9081bef72877fe4723959640fa57119b366ce6899d401",
"The quick brown fox jumps over the lazy dof",
},
}
for v in test_vectors {
dst := make([]byte, len(v.output) / 2, context.temp_allocator)
ctx: shake.Context
switch v.sec_strength {
case 128:
shake.init_128(&ctx)
case 256:
shake.init_256(&ctx)
}
shake.write(&ctx, transmute([]byte)(v.str))
shake.read(&ctx, dst)
dst_str := string(hex.encode(dst, context.temp_allocator))
expect(
t,
dst_str == v.output,
fmt.tprintf(
"SHAKE%d: Expected: %s for input of %s, but got %s instead",
v.sec_strength,
v.output,
v.str,
dst_str,
),
)
}
}
@(test)
test_cshake :: proc(t: ^testing.T) {
log(t, "Testing cSHAKE")
test_vectors := []struct {
sec_strength: int,
domainsep: string,
output: string,
str: string,
} {
// 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 {
dst := make([]byte, len(v.output) / 2, context.temp_allocator)
domainsep := transmute([]byte)(v.domainsep)
ctx: shake.Context
switch v.sec_strength {
case 128:
shake.init_cshake_128(&ctx, domainsep)
case 256:
shake.init_cshake_256(&ctx, domainsep)
}
data, _ := hex.decode(transmute([]byte)(v.str))
shake.write(&ctx, data)
shake.read(&ctx, dst)
dst_str := string(hex.encode(dst, context.temp_allocator))
expect(
t,
dst_str == v.output,
fmt.tprintf(
"cSHAKE%d: Expected: %s for input of %s, but got %s instead",
v.sec_strength,
v.output,
v.str,
dst_str,
),
)
}
}
@(test)
test_tuplehash :: proc(t: ^testing.T) {
log(t, "Testing TupleHash(XOF)")
test_vectors := []struct {
sec_strength: int,
domainsep: string,
output: string,
tuple: []string,
is_xof: bool,
} {
// TupleHash128
// - https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/TupleHash_samples.pdf
{
128,
"",
"c5d8786c1afb9b82111ab34b65b2c0048fa64e6d48e263264ce1707d3ffc8ed1",
[]string{
"000102",
"101112131415",
},
false,
},
{
128,
"My Tuple App",
"75cdb20ff4db1154e841d758e24160c54bae86eb8c13e7f5f40eb35588e96dfb",
[]string{
"000102",
"101112131415",
},
false,
},
{
128,
"My Tuple App",
"e60f202c89a2631eda8d4c588ca5fd07f39e5151998deccf973adb3804bb6e84",
[]string{
"000102",
"101112131415",
"202122232425262728",
},
false,
},
// TupleHash256
// - https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/TupleHash_samples.pdf
{
256,
"",
"cfb7058caca5e668f81a12a20a2195ce97a925f1dba3e7449a56f82201ec607311ac2696b1ab5ea2352df1423bde7bd4bb78c9aed1a853c78672f9eb23bbe194",
[]string{
"000102",
"101112131415",
},
false,
},
{
256,
"My Tuple App",
"147c2191d5ed7efd98dbd96d7ab5a11692576f5fe2a5065f3e33de6bba9f3aa1c4e9a068a289c61c95aab30aee1e410b0b607de3620e24a4e3bf9852a1d4367e",
[]string{
"000102",
"101112131415",
},
false,
},
{
256,
"My Tuple App",
"45000be63f9b6bfd89f54717670f69a9bc763591a4f05c50d68891a744bcc6e7d6d5b5e82c018da999ed35b0bb49c9678e526abd8e85c13ed254021db9e790ce",
[]string{
"000102",
"101112131415",
"202122232425262728",
},
false,
},
// TupleHashXOF128
// - https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/TupleHashXOF_samples.pdf
{
128,
"",
"2f103cd7c32320353495c68de1a8129245c6325f6f2a3d608d92179c96e68488",
[]string{
"000102",
"101112131415",
},
true,
},
{
128,
"My Tuple App",
"3fc8ad69453128292859a18b6c67d7ad85f01b32815e22ce839c49ec374e9b9a",
[]string{
"000102",
"101112131415",
},
true,
},
{
128,
"My Tuple App",
"900fe16cad098d28e74d632ed852f99daab7f7df4d99e775657885b4bf76d6f8",
[]string{
"000102",
"101112131415",
"202122232425262728",
},
true,
},
// TupleHashXOF256
// - https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/TupleHashXOF_samples.pdf
{
256,
"",
"03ded4610ed6450a1e3f8bc44951d14fbc384ab0efe57b000df6b6df5aae7cd568e77377daf13f37ec75cf5fc598b6841d51dd207c991cd45d210ba60ac52eb9",
[]string{
"000102",
"101112131415",
},
true,
},
{
256,
"My Tuple App",
"6483cb3c9952eb20e830af4785851fc597ee3bf93bb7602c0ef6a65d741aeca7e63c3b128981aa05c6d27438c79d2754bb1b7191f125d6620fca12ce658b2442",
[]string{
"000102",
"101112131415",
},
true,
},
{
256,
"My Tuple App",
"0c59b11464f2336c34663ed51b2b950bec743610856f36c28d1d088d8a2446284dd09830a6a178dc752376199fae935d86cfdee5913d4922dfd369b66a53c897",
[]string{
"000102",
"101112131415",
"202122232425262728",
},
true,
},
}
for v in test_vectors {
dst := make([]byte, len(v.output) / 2, context.temp_allocator)
domainsep := transmute([]byte)(v.domainsep)
ctx: tuplehash.Context
switch v.sec_strength {
case 128:
tuplehash.init_128(&ctx, domainsep)
case 256:
tuplehash.init_256(&ctx, domainsep)
}
for e in v.tuple {
data, _ := hex.decode(transmute([]byte)(e))
tuplehash.write_element(&ctx, data)
}
suffix: string
switch v.is_xof {
case true:
suffix = "XOF"
tuplehash.read(&ctx, dst)
case false:
tuplehash.final(&ctx, dst)
}
dst_str := string(hex.encode(dst, context.temp_allocator))
expect(
t,
dst_str == v.output,
fmt.tprintf(
"TupleHash%s%d: Expected: %s for input of %v, but got %s instead",
suffix,
v.sec_strength,
v.output,
v.tuple,
dst_str,
),
)
}
}