Browse Source

core/crypto/tuplehash: Initial import

Yawning Angel 1 year ago
parent
commit
c04a53e453

+ 14 - 0
core/crypto/_sha3/sp800_185.odin

@@ -28,6 +28,20 @@ init_cshake :: proc(ctx: ^Context, n, s: []byte, sec_strength: int) {
 	bytepad(ctx, [][]byte{n, s}, rate)
 	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
 // 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`.
 // however, the largest value we will ever need to encode is `max(int) * 8`.
 //
 //

+ 66 - 0
core/crypto/tuplehash/tuplehash.odin

@@ -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 - 0
examples/all/all_main.odin

@@ -40,6 +40,7 @@ import sha2             "core:crypto/sha2"
 import sha3             "core:crypto/sha3"
 import sha3             "core:crypto/sha3"
 import shake            "core:crypto/shake"
 import shake            "core:crypto/shake"
 import sm3              "core:crypto/sm3"
 import sm3              "core:crypto/sm3"
+import tuplehash        "core:crypto/tuplehash"
 import x25519           "core:crypto/x25519"
 import x25519           "core:crypto/x25519"
 
 
 import pe               "core:debug/pe"
 import pe               "core:debug/pe"
@@ -159,6 +160,7 @@ _ :: sha2
 _ :: sha3
 _ :: sha3
 _ :: shake
 _ :: shake
 _ :: sm3
 _ :: sm3
+_ :: tuplehash
 _ :: x25519
 _ :: x25519
 _ :: pe
 _ :: pe
 _ :: dynlib
 _ :: dynlib

+ 1 - 131
tests/core/crypto/test_core_crypto.odin

@@ -22,7 +22,6 @@ import "core:crypto"
 import "core:crypto/chacha20"
 import "core:crypto/chacha20"
 import "core:crypto/chacha20poly1305"
 import "core:crypto/chacha20poly1305"
 
 
-import "core:crypto/shake"
 import "core:crypto/x25519"
 import "core:crypto/x25519"
 
 
 TEST_count := 0
 TEST_count := 0
@@ -57,8 +56,8 @@ main :: proc() {
 
 
 	test_chacha20(&t)
 	test_chacha20(&t)
 	test_chacha20poly1305(&t)
 	test_chacha20poly1305(&t)
-	test_shake(&t)
 	test_x25519(&t)
 	test_x25519(&t)
+	test_sha3_variants(&t)
 
 
 	bench_crypto(&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",
 		"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,
-			),
-		)
-	}
-}

+ 341 - 0
tests/core/crypto/test_core_crypto_sha3_variants.odin

@@ -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,
+			),
+		)
+	}
+}