浏览代码

core/crypto/shake: SHAKE is an XOF, not a hash

Yawning Angel 1 年之前
父节点
当前提交
b02b85d242

+ 0 - 27
core/crypto/hash/low_level.odin

@@ -4,7 +4,6 @@ import "core:crypto/blake2b"
 import "core:crypto/blake2s"
 import "core:crypto/sha2"
 import "core:crypto/sha3"
-import "core:crypto/shake"
 import "core:crypto/sm3"
 import "core:crypto/legacy/keccak"
 import "core:crypto/legacy/md5"
@@ -26,8 +25,6 @@ Algorithm :: enum {
 	SHA3_256,
 	SHA3_384,
 	SHA3_512,
-	SHAKE_128,
-	SHAKE_256,
 	SM3,
 	Legacy_KECCAK_224,
 	Legacy_KECCAK_256,
@@ -51,8 +48,6 @@ ALGORITHM_NAMES := [Algorithm]string {
 	.SHA3_256          = "SHA3-256",
 	.SHA3_384          = "SHA3-384",
 	.SHA3_512          = "SHA3-512",
-	.SHAKE_128         = "SHAKE-128",
-	.SHAKE_256         = "SHAKE-256",
 	.SM3               = "SM3",
 	.Legacy_KECCAK_224 = "Keccak-224",
 	.Legacy_KECCAK_256 = "Keccak-256",
@@ -76,8 +71,6 @@ DIGEST_SIZES := [Algorithm]int {
 	.SHA3_256          = sha3.DIGEST_SIZE_256,
 	.SHA3_384          = sha3.DIGEST_SIZE_384,
 	.SHA3_512          = sha3.DIGEST_SIZE_512,
-	.SHAKE_128         = shake.DIGEST_SIZE_128,
-	.SHAKE_256         = shake.DIGEST_SIZE_256,
 	.SM3               = sm3.DIGEST_SIZE,
 	.Legacy_KECCAK_224 = keccak.DIGEST_SIZE_224,
 	.Legacy_KECCAK_256 = keccak.DIGEST_SIZE_256,
@@ -96,7 +89,6 @@ Context :: struct {
 		^sha2.Context_256,
 		^sha2.Context_512,
 		^sha3.Context,
-		^shake.Context,
 		^sm3.Context,
 		^keccak.Context,
 		^md5.Context,
@@ -159,14 +151,6 @@ init :: proc(ctx: ^Context, algorithm: Algorithm, allocator := context.allocator
 		impl := new(sha3.Context, allocator)
 		sha3.init_512(impl)
 		ctx._impl = impl
-	case .SHAKE_128:
-		impl := new(shake.Context, allocator)
-		shake.init_128(impl)
-		ctx._impl = impl
-	case .SHAKE_256:
-		impl := new(shake.Context, allocator)
-		shake.init_256(impl)
-		ctx._impl = impl
 	case .SM3:
 		impl := new(sm3.Context, allocator)
 		sm3.init(impl)
@@ -218,8 +202,6 @@ update :: proc(ctx: ^Context, data: []byte) {
 		sha2.update(impl, data)
 	case ^sha3.Context:
 		sha3.update(impl, data)
-	case ^shake.Context:
-		shake.update(impl, data)
 	case ^sm3.Context:
 		sm3.update(impl, data)
 	case ^keccak.Context:
@@ -250,8 +232,6 @@ final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
 		sha2.final(impl, hash, finalize_clone)
 	case ^sha3.Context:
 		sha3.final(impl, hash, finalize_clone)
-	case ^shake.Context:
-		shake.final(impl, hash, finalize_clone)
 	case ^sm3.Context:
 		sm3.final(impl, hash, finalize_clone)
 	case ^keccak.Context:
@@ -304,10 +284,6 @@ clone :: proc(ctx, other: ^Context, allocator := context.allocator) {
 		impl := new(sha3.Context, allocator)
 		sha3.clone(impl, src_impl)
 		ctx._impl = impl
-	case ^shake.Context:
-		impl := new(shake.Context, allocator)
-		shake.clone(impl, src_impl)
-		ctx._impl = impl
 	case ^sm3.Context:
 		impl := new(sm3.Context, allocator)
 		sm3.clone(impl, src_impl)
@@ -348,9 +324,6 @@ reset :: proc(ctx: ^Context) {
 	case ^sha3.Context:
 		sha3.reset(impl)
 		free(impl, ctx._allocator)
-	case ^shake.Context:
-		shake.reset(impl)
-		free(impl, ctx._allocator)
 	case ^sm3.Context:
 		sm3.reset(impl)
 		free(impl, ctx._allocator)

+ 27 - 32
core/crypto/shake/shake.odin

@@ -1,3 +1,11 @@
+/*
+package shake implements the SHAKE XOF algorithm family.
+
+The SHA3 hash algorithm can be found in the crypto/sha3.
+
+See:
+- https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf
+*/
 package shake
 
 /*
@@ -6,30 +14,22 @@ package shake
 
     List of contributors:
         zhibog, dotbmp:  Initial implementation.
-
-    Interface for the SHAKE XOF.  The SHA3 hashing algorithm can be found
-    in package sha3.
-
-    TODO:
-    - This should provide an incremental squeeze interface.
-    - DIGEST_SIZE is inaccurate, SHAKE-128 and SHAKE-256 are security
-      strengths.
 */
 
 import "../_sha3"
 
-DIGEST_SIZE_128 :: 16
-DIGEST_SIZE_256 :: 32
-
+// Context is a SHAKE128 or SHAKE256 instance.
 Context :: distinct _sha3.Context
 
+// init_128 initializes a Context for SHAKE128.
 init_128 :: proc(ctx: ^Context) {
-	ctx.mdlen = DIGEST_SIZE_128
+	ctx.mdlen = 128 / 8
 	_init(ctx)
 }
 
+// init_256 initializes a Context for SHAKE256.
 init_256 :: proc(ctx: ^Context) {
-	ctx.mdlen = DIGEST_SIZE_256
+	ctx.mdlen = 256 / 8
 	_init(ctx)
 }
 
@@ -38,36 +38,31 @@ _init :: proc(ctx: ^Context) {
 	_sha3.init(transmute(^_sha3.Context)(ctx))
 }
 
-update :: proc(ctx: ^Context, data: []byte) {
+// write writes more data into the SHAKE instance.  This MUST not be called
+// after any reads have been done, and attempts to do so will panic.
+write :: proc(ctx: ^Context, data: []byte) {
 	_sha3.update(transmute(^_sha3.Context)(ctx), data)
 }
 
-final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
-	// Rolling digest support is handled here instead of in the generic
-	// _sha3 package as SHAKE is more of an XOF than a hash, so the
-	// standard notion of "final", doesn't really exist when you can
-	// squeeze an unlimited amount of data.
-	//
-	// TODO/yawning: Strongly consider getting rid of this and rigidly
-	// defining SHAKE as an XOF.
-
-	ctx := ctx
-	if finalize_clone {
-		tmp_ctx: Context
-		clone(&tmp_ctx, ctx)
-		ctx = &tmp_ctx
+// read reads output from the SHAKE instance.  There is no practical upper
+// limit to the amount of data that can be read from SHAKE.  After read has
+// been called one or more times, further calls to write will panic.
+read :: proc(ctx: ^Context, dst: []byte) {
+	ctx_ := transmute(^_sha3.Context)(ctx)
+	if !ctx.is_finalized {
+		_sha3.shake_xof(ctx_)
 	}
-	defer(reset(ctx))
 
-	ctx_ := transmute(^_sha3.Context)(ctx)
-	_sha3.shake_xof(ctx_)
-	_sha3.shake_out(ctx_, hash[:])
+	_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))
 }

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

@@ -12,11 +12,13 @@ package test_core_crypto
 	Where possible, the official test vectors are used to validate the implementation.
 */
 
+import "core:encoding/hex"
 import "core:fmt"
+import "core:os"
 import "core:testing"
 
 import "core:crypto/siphash"
-import "core:os"
+import "core:crypto/shake"
 
 TEST_count := 0
 TEST_fail := 0
@@ -43,6 +45,7 @@ main :: proc() {
 	t := testing.T{}
 	test_hash(&t)
 
+	test_shake(&t)
 	test_siphash_2_4(&t)
 
 	// "modern" crypto tests
@@ -60,6 +63,81 @@ main :: proc() {
 	}
 }
 
+TestXOF :: struct {
+	sec_strength: int,
+	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",
+		},
+	}
+	for v in test_vectors {
+		dst := make([]byte, len(v.output)/2, context.temp_allocator)
+
+		data := transmute([]byte)(v.str)
+
+		ctx: shake.Context
+		switch v.sec_strength {
+		case 128:
+			shake.init_128(&ctx)
+		case 256:
+			shake.init_256(&ctx)
+		}
+
+		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(
+				"SHAKE%d: Expected: %s for input of %s, but got %s instead",
+				v.sec_strength,
+				v.output,
+				v.str,
+				dst_str,
+			),
+		)
+	}
+}
+
 @(test)
 test_siphash_2_4 :: proc(t: ^testing.T) {
 	// Test vectors from

+ 0 - 30
tests/core/crypto/test_core_crypto_hash.odin

@@ -339,36 +339,6 @@ test_hash :: proc(t: ^testing.T) {
 			"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
 		},
 
-		// SHAKE-128
-		TestHash{hash.Algorithm.SHAKE_128, "7f9c2ba4e88f827d616045507605853e", ""},
-		TestHash {
-			hash.Algorithm.SHAKE_128,
-			"f4202e3c5852f9182a0430fd8144f0a7",
-			"The quick brown fox jumps over the lazy dog",
-		},
-		TestHash {
-			hash.Algorithm.SHAKE_128,
-			"853f4538be0db9621a6cea659a06c110",
-			"The quick brown fox jumps over the lazy dof",
-		},
-
-		// SHAKE-256
-		TestHash {
-			hash.Algorithm.SHAKE_256,
-			"46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f",
-			"",
-		},
-		TestHash {
-			hash.Algorithm.SHAKE_256,
-			"2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca",
-			"The quick brown fox jumps over the lazy dog",
-		},
-		TestHash {
-			hash.Algorithm.SHAKE_256,
-			"46b1ebb2e142c38b9ac9081bef72877fe4723959640fa57119b366ce6899d401",
-			"The quick brown fox jumps over the lazy dof",
-		},
-
 		// SM3
 		{
 			hash.Algorithm.SM3,