Browse Source

core/crypto/kmac: Initial import

Yawning Angel 1 year ago
parent
commit
b818ebc02f

+ 12 - 11
core/crypto/_sha3/sp800_185.odin

@@ -4,16 +4,6 @@ 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.
@@ -25,7 +15,7 @@ init_cshake :: proc(ctx: ^Context, n, s: []byte, sec_strength: int) {
 
 	ctx.dsbyte = DS_CSHAKE
 	init(ctx)
-	bytepad(ctx, [][]byte{n, s}, rate)
+	bytepad(ctx, [][]byte{n, s}, rate_cshake(sec_strength))
 }
 
 final_cshake :: proc(ctx: ^Context, dst: []byte, finalize_clone: bool = false) {
@@ -42,6 +32,17 @@ final_cshake :: proc(ctx: ^Context, dst: []byte, finalize_clone: bool = false) {
 	shake_out(ctx, dst)
 }
 
+rate_cshake :: #force_inline proc(sec_strength: int) -> int {
+	switch sec_strength {
+	case 128:
+		return RATE_128
+	case 256:
+		return RATE_256
+	}
+
+	panic("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`.
 //

+ 116 - 0
core/crypto/kmac/kmac.odin

@@ -0,0 +1,116 @@
+/*
+package kmac implements the KMAC MAC algorithm.
+
+See:
+- https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
+*/
+package kmac
+
+import "../_sha3"
+import "core:crypto"
+import "core:crypto/shake"
+
+// MIN_KEY_SIZE_128 is the minimum key size for KMAC128 in bytes.
+MIN_KEY_SIZE_128 :: 128 / 8
+// MIN_KEY_SIZE_256 is the minimum key size for KMAC256 in bytes.
+MIN_KEY_SIZE_256 :: 256 / 8
+
+// MIN_TAG_SIZE is the absolute minimum tag size for KMAC in bytes (8.4.2).
+// Most callers SHOULD use at least 128-bits if not 256-bits for the tag
+// size.
+MIN_TAG_SIZE :: 32 / 8
+
+// sum will compute the KMAC with the specified security strength,
+// key, and domain separator over msg, and write the computed digest to
+// dst.
+sum :: proc(sec_strength: int, dst, msg, key, domain_sep: []byte) {
+	ctx: Context
+
+	_init_kmac(&ctx, key, domain_sep, sec_strength)
+	update(&ctx, msg)
+	final(&ctx, dst)
+}
+
+// verify will verify the KMAC tag computed with the specified security
+// strength, key and domain separator over msg and return true iff the
+// tag is valid.
+verify :: proc(sec_strength: int, tag, msg, key, domain_sep: []byte, allocator := context.temp_allocator) -> bool {
+	derived_tag := make([]byte, len(tag), allocator)
+
+	sum(sec_strength, derived_tag, msg, key, domain_sep)
+
+	return crypto.compare_constant_time(derived_tag, tag) == 1
+}
+
+// Context is a KMAC instance.
+Context :: distinct shake.Context
+
+// init_128 initializes a Context for KMAC28.  This routine will panic if
+// the key length is less than MIN_KEY_SIZE_128.
+init_128 :: proc(ctx: ^Context, key, domain_sep: []byte) {
+	_init_kmac(ctx, key, domain_sep, 128)
+}
+
+// init_256 initializes a Context for KMAC256.  This routine will panic if
+// the key length is less than MIN_KEY_SIZE_256.
+init_256 :: proc(ctx: ^Context, key, domain_sep: []byte) {
+	_init_kmac(ctx, key, domain_sep, 256)
+}
+
+// update adds more data to the Context.
+update :: proc(ctx: ^Context, data: []byte) {
+	assert(ctx.is_initialized)
+
+	shake.write(transmute(^shake.Context)(ctx), data)
+}
+
+// final finalizes the Context, writes the tag to dst, and calls reset
+// on the Context.  This routine will panic if the dst length is less than
+// MIN_TAG_SIZE.
+final :: proc(ctx: ^Context, dst: []byte) {
+	assert(ctx.is_initialized)
+	defer reset(ctx)
+
+	if len(dst) < MIN_TAG_SIZE {
+		panic("crypto/kmac: invalid KMAC tag_size, too short")
+	}
+
+	_sha3.final_cshake(transmute(^_sha3.Context)(ctx), dst)
+}
+
+// clone clones the Context other into ctx.
+clone :: proc(ctx, other: ^Context) {
+	if ctx == other {
+		return
+	}
+
+	shake.clone(transmute(^shake.Context)(ctx), transmute(^shake.Context)(other))
+}
+
+// reset sanitizes the Context.  The Context must be re-initialized to
+// be used again.
+reset :: proc(ctx: ^Context) {
+	if !ctx.is_initialized {
+		return
+	}
+
+	shake.reset(transmute(^shake.Context)(ctx))
+}
+
+@(private)
+_init_kmac :: proc(ctx: ^Context, key, s: []byte, sec_strength: int) {
+	if ctx.is_initialized {
+		reset(ctx)
+	}
+
+	if len(key) < sec_strength / 8 {
+		panic("crypto/kmac: invalid KMAC key, too short")
+	}
+
+	ctx_ := transmute(^_sha3.Context)(ctx)
+	_sha3.init_cshake(ctx_, N_KMAC, s, sec_strength)
+	_sha3.bytepad(ctx_, [][]byte{key}, _sha3.rate_cshake(sec_strength))
+}
+
+@(private)
+N_KMAC := []byte{'K', 'M', 'A', 'C'}

+ 2 - 0
examples/all/all_main.odin

@@ -31,6 +31,7 @@ import chacha20poly1305 "core:crypto/chacha20poly1305"
 import crypto_hash      "core:crypto/hash"
 import hkdf             "core:crypto/hkdf"
 import hmac             "core:crypto/hmac"
+import kmac             "core:crypto/kmac"
 import keccak           "core:crypto/legacy/keccak"
 import md5              "core:crypto/legacy/md5"
 import sha1             "core:crypto/legacy/sha1"
@@ -151,6 +152,7 @@ _ :: chacha20
 _ :: chacha20poly1305
 _ :: hmac
 _ :: hkdf
+_ :: kmac
 _ :: keccak
 _ :: md5
 _ :: pbkdf2

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

@@ -4,6 +4,7 @@ import "core:encoding/hex"
 import "core:fmt"
 import "core:testing"
 
+import "core:crypto/kmac"
 import "core:crypto/shake"
 import "core:crypto/tuplehash"
 
@@ -14,6 +15,7 @@ test_sha3_variants :: proc(t: ^testing.T) {
 	test_shake(t)
 	test_cshake(t)
 	test_tuplehash(t)
+	test_kmac(t)
 }
 
 @(test)
@@ -339,3 +341,99 @@ test_tuplehash :: proc(t: ^testing.T) {
 		)
 	}
 }
+
+@(test)
+test_kmac :: proc(t:^testing.T) {
+	log(t, "Testing KMAC")
+
+	test_vectors := []struct {
+		sec_strength: int,
+		key:          string,
+		domainsep:    string,
+		msg:          string,
+		output:       string,
+	} {
+		// KMAC128
+		// - https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/KMAC_samples.pdf
+		{
+			128,
+			"404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f",
+			"",
+			"00010203",
+			"e5780b0d3ea6f7d3a429c5706aa43a00fadbd7d49628839e3187243f456ee14e",
+		},
+		{
+			128,
+			"404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f",
+			"My Tagged Application",
+			"00010203",
+			"3b1fba963cd8b0b59e8c1a6d71888b7143651af8ba0a7070c0979e2811324aa5",
+		},
+		{
+			128,
+			"404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f",
+			"My Tagged Application",
+			"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7",
+			"1f5b4e6cca02209e0dcb5ca635b89a15e271ecc760071dfd805faa38f9729230",
+		},
+
+		// KMAC256
+		// - https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/KMAC_samples.pdf
+		{
+			256,
+			"404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f",
+			"My Tagged Application",
+			"00010203",
+			"20c570c31346f703c9ac36c61c03cb64c3970d0cfc787e9b79599d273a68d2f7f69d4cc3de9d104a351689f27cf6f5951f0103f33f4f24871024d9c27773a8dd",
+		},
+		{
+			256,
+			"404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f",
+			"",
+			"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7",
+			"75358cf39e41494e949707927cee0af20a3ff553904c86b08f21cc414bcfd691589d27cf5e15369cbbff8b9a4c2eb17800855d0235ff635da82533ec6b759b69",
+		},
+		{
+			256,
+			"404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f",
+			"My Tagged Application",
+			"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7",
+			"b58618f71f92e1d56c1b8c55ddd7cd188b97b4ca4d99831eb2699a837da2e4d970fbacfde50033aea585f1a2708510c32d07880801bd182898fe476876fc8965",
+		},
+	}
+
+	for v in test_vectors {
+		dst := make([]byte, len(v.output) / 2, context.temp_allocator)
+
+		key, _ := hex.decode(transmute([]byte)(v.key))
+		domainsep := transmute([]byte)(v.domainsep)
+
+		ctx: kmac.Context
+		switch v.sec_strength {
+		case 128:
+			kmac.init_128(&ctx, key, domainsep)
+		case 256:
+			kmac.init_256(&ctx, key, domainsep)
+		}
+
+		data, _ := hex.decode(transmute([]byte)(v.msg))
+		kmac.update(&ctx, data)
+		kmac.final(&ctx, dst)
+
+		dst_str := string(hex.encode(dst, context.temp_allocator))
+
+		expect(
+			t,
+			dst_str == v.output,
+			fmt.tprintf(
+				"KMAC%d: Expected: %s for input of (%s, %s, %s), but got %s instead",
+				v.sec_strength,
+				v.output,
+				v.key,
+				v.domainsep,
+				v.msg,
+				dst_str,
+			),
+		)
+	}
+}