diff options
Diffstat (limited to 'tests/core')
| -rw-r--r-- | tests/core/Makefile | 2 | ||||
| -rw-r--r-- | tests/core/build.bat | 2 | ||||
| -rw-r--r-- | tests/core/crypto/test_core_crypto.odin | 155 | ||||
| -rw-r--r-- | tests/core/crypto/test_core_crypto_hash.odin | 42 | ||||
| -rw-r--r-- | tests/core/crypto/test_core_crypto_kdf.odin | 188 | ||||
| -rw-r--r-- | tests/core/crypto/test_core_crypto_mac.odin | 20 | ||||
| -rw-r--r-- | tests/core/crypto/test_core_crypto_sha3_variants.odin | 441 | ||||
| -rw-r--r-- | tests/core/crypto/test_crypto_benchmark.odin | 20 |
8 files changed, 700 insertions, 170 deletions
diff --git a/tests/core/Makefile b/tests/core/Makefile index 1207eeec5..ecb05d002 100644 --- a/tests/core/Makefile +++ b/tests/core/Makefile @@ -45,7 +45,7 @@ hash_test: $(ODIN) run hash $(COMMON) -o:speed -no-bounds-check -out:test_hash crypto_test: - $(ODIN) run crypto $(COMMON) -o:speed -no-bounds-check -out:test_crypto + $(ODIN) run crypto $(COMMON) $(COLLECTION) -o:speed -no-bounds-check -out:test_crypto noise_test: $(ODIN) run math/noise $(COMMON) -out:test_noise diff --git a/tests/core/build.bat b/tests/core/build.bat index d5f528f0c..210760d00 100644 --- a/tests/core/build.bat +++ b/tests/core/build.bat @@ -31,7 +31,7 @@ echo --- echo --- echo Running core:crypto tests echo --- -%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto.exe || exit /b +%PATH_TO_ODIN% run crypto %COMMON% %COLLECTION% -out:test_crypto.exe || exit /b echo --- echo Running core:encoding tests diff --git a/tests/core/crypto/test_core_crypto.odin b/tests/core/crypto/test_core_crypto.odin index 4ca34fc5a..a6d399097 100644 --- a/tests/core/crypto/test_core_crypto.odin +++ b/tests/core/crypto/test_core_crypto.odin @@ -15,36 +15,14 @@ package test_core_crypto import "core:encoding/hex" import "core:fmt" import "core:mem" -import "core:os" import "core:testing" import "core:crypto" import "core:crypto/chacha20" import "core:crypto/chacha20poly1305" - -import "core:crypto/shake" import "core:crypto/x25519" -TEST_count := 0 -TEST_fail := 0 - -when ODIN_TEST { - expect :: testing.expect - log :: testing.log -} else { - expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { - TEST_count += 1 - if !condition { - TEST_fail += 1 - fmt.printf("[%v] %v\n", loc, message) - return - } - } - log :: proc(t: ^testing.T, v: any, loc := #caller_location) { - fmt.printf("[%v] ", loc) - fmt.printf("log: %v\n", v) - } -} +import tc "tests:common" main :: proc() { t := testing.T{} @@ -53,25 +31,23 @@ main :: proc() { test_hash(&t) test_mac(&t) + test_kdf(&t) // After hash/mac tests because those should pass first. test_chacha20(&t) test_chacha20poly1305(&t) - test_shake(&t) test_x25519(&t) + test_sha3_variants(&t) bench_crypto(&t) - fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) - if TEST_fail > 0 { - os.exit(1) - } + tc.report(&t) } _PLAINTEXT_SUNSCREEN_STR := "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it." @(test) test_chacha20 :: proc(t: ^testing.T) { - log(t, "Testing (X)ChaCha20") + tc.log(t, "Testing (X)ChaCha20") // Test cases taken from RFC 8439, and draft-irtf-cfrg-xchacha-03 plaintext := transmute([]byte)(_PLAINTEXT_SUNSCREEN_STR) @@ -114,7 +90,7 @@ test_chacha20 :: proc(t: ^testing.T) { chacha20.xor_bytes(&ctx, derived_ciphertext[:], plaintext[:]) derived_ciphertext_str := string(hex.encode(derived_ciphertext[:], context.temp_allocator)) - expect( + tc.expect( t, derived_ciphertext_str == ciphertext_str, fmt.tprintf( @@ -161,7 +137,7 @@ test_chacha20 :: proc(t: ^testing.T) { chacha20.xor_bytes(&ctx, derived_ciphertext[:], plaintext[:]) derived_ciphertext_str = string(hex.encode(derived_ciphertext[:], context.temp_allocator)) - expect( + tc.expect( t, derived_ciphertext_str == xciphertext_str, fmt.tprintf( @@ -174,7 +150,7 @@ test_chacha20 :: proc(t: ^testing.T) { @(test) test_chacha20poly1305 :: proc(t: ^testing.T) { - log(t, "Testing chacha20poly1205") + tc.log(t, "Testing chacha20poly1205") plaintext := transmute([]byte)(_PLAINTEXT_SUNSCREEN_STR) @@ -233,7 +209,7 @@ test_chacha20poly1305 :: proc(t: ^testing.T) { ) derived_ciphertext_str := string(hex.encode(derived_ciphertext[:], context.temp_allocator)) - expect( + tc.expect( t, derived_ciphertext_str == ciphertext_str, fmt.tprintf( @@ -244,7 +220,7 @@ test_chacha20poly1305 :: proc(t: ^testing.T) { ) derived_tag_str := string(hex.encode(derived_tag[:], context.temp_allocator)) - expect( + tc.expect( t, derived_tag_str == tag_str, fmt.tprintf( @@ -264,8 +240,8 @@ test_chacha20poly1305 :: proc(t: ^testing.T) { ciphertext[:], ) derived_plaintext_str := string(derived_plaintext[:]) - expect(t, ok, "Expected true for decrypt(tag, aad, ciphertext)") - expect( + tc.expect(t, ok, "Expected true for decrypt(tag, aad, ciphertext)") + tc.expect( t, derived_plaintext_str == _PLAINTEXT_SUNSCREEN_STR, fmt.tprintf( @@ -284,7 +260,7 @@ test_chacha20poly1305 :: proc(t: ^testing.T) { aad[:], derived_ciphertext[:], ) - expect(t, !ok, "Expected false for decrypt(tag, aad, corrupted_ciphertext)") + tc.expect(t, !ok, "Expected false for decrypt(tag, aad, corrupted_ciphertext)") aad[0] ~= 0xa5 ok = chacha20poly1305.decrypt( @@ -295,18 +271,12 @@ test_chacha20poly1305 :: proc(t: ^testing.T) { aad[:], ciphertext[:], ) - expect(t, !ok, "Expected false for decrypt(tag, corrupted_aad, ciphertext)") -} - -TestECDH :: struct { - scalar: string, - point: string, - product: string, + tc.expect(t, !ok, "Expected false for decrypt(tag, corrupted_aad, ciphertext)") } @(test) test_x25519 :: proc(t: ^testing.T) { - log(t, "Testing X25519") + tc.log(t, "Testing X25519") // Local copy of this so that the base point doesn't need to be exported. _BASE_POINT: [32]byte = { @@ -314,7 +284,11 @@ test_x25519 :: proc(t: ^testing.T) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } - test_vectors := [?]TestECDH { + test_vectors := []struct{ + scalar: string, + point: string, + product: string, + } { // Test vectors from RFC 7748 { "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4", @@ -335,7 +309,7 @@ test_x25519 :: proc(t: ^testing.T) { x25519.scalarmult(derived_point[:], scalar[:], point[:]) derived_point_str := string(hex.encode(derived_point[:], context.temp_allocator)) - expect( + tc.expect( t, derived_point_str == v.product, fmt.tprintf( @@ -353,7 +327,7 @@ test_x25519 :: proc(t: ^testing.T) { x25519.scalarmult(p2[:], scalar[:], _BASE_POINT[:]) p1_str := string(hex.encode(p1[:], context.temp_allocator)) p2_str := string(hex.encode(p2[:], context.temp_allocator)) - expect( + tc.expect( t, p1_str == p2_str, fmt.tprintf( @@ -371,16 +345,14 @@ test_x25519 :: proc(t: ^testing.T) { @(test) test_rand_bytes :: proc(t: ^testing.T) { - log(t, "Testing rand_bytes") + tc.log(t, "Testing rand_bytes") if ODIN_OS != .Linux { - log(t, "rand_bytes not supported - skipping") + tc.log(t, "rand_bytes not supported - skipping") return } - allocator := context.allocator - - buf := make([]byte, 1 << 25, allocator) + buf := make([]byte, 1 << 25, context.allocator) defer delete(buf) // Testing a CSPRNG for correctness is incredibly involved and @@ -405,84 +377,9 @@ test_rand_bytes :: proc(t: ^testing.T) { } } - expect( + tc.expect( t, seems_ok, "Expected to randomize the head and tail of the buffer within a handful of attempts", ) } - -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, - ), - ) - } -} diff --git a/tests/core/crypto/test_core_crypto_hash.odin b/tests/core/crypto/test_core_crypto_hash.odin index 7dc559681..bd40a9b23 100644 --- a/tests/core/crypto/test_core_crypto_hash.odin +++ b/tests/core/crypto/test_core_crypto_hash.odin @@ -8,22 +8,22 @@ import "core:testing" import "core:crypto/hash" -TestHash :: struct { - algo: hash.Algorithm, - hash: string, - str: string, -} +import tc "tests:common" @(test) test_hash :: proc(t: ^testing.T) { - log(t, "Testing Hashes") + tc.log(t, "Testing Hashes") // TODO: // - Stick the test vectors in a JSON file or something. data_1_000_000_a := strings.repeat("a", 1_000_000) - digest: [64]byte // 512-bits is enough for every digest for now. - test_vectors := [?]TestHash { + digest: [hash.MAX_DIGEST_SIZE]byte + test_vectors := []struct{ + algo: hash.Algorithm, + hash: string, + str: string, + } { // BLAKE2b { hash.Algorithm.BLAKE2B, @@ -424,9 +424,9 @@ test_hash :: proc(t: ^testing.T) { // MD5 (Insecure) // - https://datatracker.ietf.org/doc/html/rfc1321 - TestHash{hash.Algorithm.Insecure_MD5, "d41d8cd98f00b204e9800998ecf8427e", ""}, - TestHash{hash.Algorithm.Insecure_MD5, "0cc175b9c0f1b6a831c399e269772661", "a"}, - TestHash{hash.Algorithm.Insecure_MD5, "900150983cd24fb0d6963f7d28e17f72", "abc"}, + {hash.Algorithm.Insecure_MD5, "d41d8cd98f00b204e9800998ecf8427e", ""}, + {hash.Algorithm.Insecure_MD5, "0cc175b9c0f1b6a831c399e269772661", "a"}, + {hash.Algorithm.Insecure_MD5, "900150983cd24fb0d6963f7d28e17f72", "abc"}, { hash.Algorithm.Insecure_MD5, "f96b697d7cb7938d525a2f31aaf161d0", @@ -451,8 +451,8 @@ test_hash :: proc(t: ^testing.T) { // SHA-1 (Insecure) // - https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf // - https://www.di-mgt.com.au/sha_testvectors.html - TestHash{hash.Algorithm.Insecure_SHA1, "da39a3ee5e6b4b0d3255bfef95601890afd80709", ""}, - TestHash{hash.Algorithm.Insecure_SHA1, "a9993e364706816aba3e25717850c26c9cd0d89d", "abc"}, + {hash.Algorithm.Insecure_SHA1, "da39a3ee5e6b4b0d3255bfef95601890afd80709", ""}, + {hash.Algorithm.Insecure_SHA1, "a9993e364706816aba3e25717850c26c9cd0d89d", "abc"}, { hash.Algorithm.Insecure_SHA1, "f9537c23893d2014f365adf8ffe33b8eb0297ed1", @@ -463,7 +463,7 @@ test_hash :: proc(t: ^testing.T) { "346fb528a24b48f563cb061470bcfd23740427ad", "jkijkljklmklmnlmnomnopnopq", }, - TestHash{hash.Algorithm.Insecure_SHA1, "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "a"}, + {hash.Algorithm.Insecure_SHA1, "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "a"}, { hash.Algorithm.Insecure_SHA1, "c729c8996ee0a6f74f4f3248e8957edf704fb624", @@ -493,7 +493,7 @@ test_hash :: proc(t: ^testing.T) { dst_str := string(hex.encode(dst, context.temp_allocator)) - expect( + tc.expect( t, dst_str == v.hash, fmt.tprintf( @@ -518,7 +518,7 @@ test_hash :: proc(t: ^testing.T) { // still correct. digest_sz := hash.DIGEST_SIZES[algo] block_sz := hash.BLOCK_SIZES[algo] - expect( + tc.expect( t, digest_sz <= hash.MAX_DIGEST_SIZE, fmt.tprintf( @@ -528,7 +528,7 @@ test_hash :: proc(t: ^testing.T) { hash.MAX_DIGEST_SIZE, ), ) - expect( + tc.expect( t, block_sz <= hash.MAX_BLOCK_SIZE, fmt.tprintf( @@ -550,7 +550,7 @@ test_hash :: proc(t: ^testing.T) { a_str := string(hex.encode(digest_a, context.temp_allocator)) b_str := string(hex.encode(digest_b, context.temp_allocator)) - expect( + tc.expect( t, a_str == b_str, fmt.tprintf( @@ -568,7 +568,7 @@ test_hash :: proc(t: ^testing.T) { api_algo := hash.algorithm(&ctx) api_digest_size := hash.digest_size(&ctx) - expect( + tc.expect( t, algo == api_algo, fmt.tprintf( @@ -578,7 +578,7 @@ test_hash :: proc(t: ^testing.T) { api_algo, ), ) - expect( + tc.expect( t, hash.DIGEST_SIZES[algo] == api_digest_size, fmt.tprintf( @@ -601,7 +601,7 @@ test_hash :: proc(t: ^testing.T) { b_str = string(hex.encode(digest_b, context.temp_allocator)) c_str := string(hex.encode(digest_c, context.temp_allocator)) - expect( + tc.expect( t, a_str == b_str && b_str == c_str, fmt.tprintf( diff --git a/tests/core/crypto/test_core_crypto_kdf.odin b/tests/core/crypto/test_core_crypto_kdf.odin new file mode 100644 index 000000000..d9e9a8501 --- /dev/null +++ b/tests/core/crypto/test_core_crypto_kdf.odin @@ -0,0 +1,188 @@ +package test_core_crypto + +import "core:encoding/hex" +import "core:fmt" +import "core:testing" + +import "core:crypto/hash" +import "core:crypto/hkdf" +import "core:crypto/pbkdf2" + +import tc "tests:common" + +@(test) +test_kdf :: proc(t: ^testing.T) { + tc.log(t, "Testing KDFs") + + test_hkdf(t) + test_pbkdf2(t) +} + +@(test) +test_hkdf :: proc(t: ^testing.T) { + tc.log(t, "Testing HKDF") + + tmp: [128]byte // Good enough. + + test_vectors := []struct { + algo: hash.Algorithm, + ikm: string, + salt: string, + info: string, + okm: string, + } { + // SHA-256 + // - https://www.rfc-editor.org/rfc/rfc5869 + { + hash.Algorithm.SHA256, + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "000102030405060708090a0b0c", + "f0f1f2f3f4f5f6f7f8f9", + "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865", + }, + { + hash.Algorithm.SHA256, + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f", + "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf", + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87", + }, + { + hash.Algorithm.SHA256, + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "", + "", + "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8", + }, + } + for v, _ in test_vectors { + algo_name := hash.ALGORITHM_NAMES[v.algo] + dst := tmp[:len(v.okm) / 2] + + ikm, _ := hex.decode(transmute([]byte)(v.ikm), context.temp_allocator) + salt, _ := hex.decode(transmute([]byte)(v.salt), context.temp_allocator) + info, _ := hex.decode(transmute([]byte)(v.info), context.temp_allocator) + + hkdf.extract_and_expand(v.algo, salt, ikm, info, dst) + + dst_str := string(hex.encode(dst, context.temp_allocator)) + + tc.expect( + t, + dst_str == v.okm, + fmt.tprintf( + "HKDF-%s: Expected: %s for input of (%s, %s, %s), but got %s instead", + algo_name, + v.okm, + v.ikm, + v.salt, + v.info, + dst_str, + ), + ) + } +} + +@(test) +test_pbkdf2 :: proc(t: ^testing.T) { + tc.log(t, "Testing PBKDF2") + + tmp: [64]byte // 512-bits is enough for every output for now. + + test_vectors := []struct { + algo: hash.Algorithm, + password: string, + salt: string, + iterations: u32, + dk: string, + } { + // SHA-1 + // - https://www.rfc-editor.org/rfc/rfc2898 + { + hash.Algorithm.Insecure_SHA1, + "password", + "salt", + 1, + "0c60c80f961f0e71f3a9b524af6012062fe037a6", + }, + { + hash.Algorithm.Insecure_SHA1, + "password", + "salt", + 2, + "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957", + }, + { + hash.Algorithm.Insecure_SHA1, + "password", + "salt", + 4096, + "4b007901b765489abead49d926f721d065a429c1", + }, + // This passes but takes a about 8 seconds on a modern-ish system. + // + // { + // hash.Algorithm.Insecure_SHA1, + // "password", + // "salt", + // 16777216, + // "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984", + // }, + { + hash.Algorithm.Insecure_SHA1, + "passwordPASSWORDpassword", + "saltSALTsaltSALTsaltSALTsaltSALTsalt", + 4096, + "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038", + }, + { + hash.Algorithm.Insecure_SHA1, + "pass\x00word", + "sa\x00lt", + 4096, + "56fa6aa75548099dcc37d7f03425e0c3", + }, + + // SHA-256 + // - https://www.rfc-editor.org/rfc/rfc7914 + { + hash.Algorithm.SHA256, + "passwd", + "salt", + 1, + "55ac046e56e3089fec1691c22544b605f94185216dde0465e68b9d57c20dacbc49ca9cccf179b645991664b39d77ef317c71b845b1e30bd509112041d3a19783", + }, + { + hash.Algorithm.SHA256, + "Password", + "NaCl", + 80000, + "4ddcd8f60b98be21830cee5ef22701f9641a4418d04c0414aeff08876b34ab56a1d425a1225833549adb841b51c9b3176a272bdebba1d078478f62b397f33c8d", + }, + } + for v, _ in test_vectors { + algo_name := hash.ALGORITHM_NAMES[v.algo] + dst := tmp[:len(v.dk) / 2] + + password := transmute([]byte)(v.password) + salt := transmute([]byte)(v.salt) + + pbkdf2.derive(v.algo, password, salt, v.iterations, dst) + + dst_str := string(hex.encode(dst, context.temp_allocator)) + + tc.expect( + t, + dst_str == v.dk, + fmt.tprintf( + "HMAC-%s: Expected: %s for input of (%s, %s, %d), but got %s instead", + algo_name, + v.dk, + v.password, + v.salt, + v.iterations, + dst_str, + ), + ) + } +} diff --git a/tests/core/crypto/test_core_crypto_mac.odin b/tests/core/crypto/test_core_crypto_mac.odin index c9ee0f0f7..f0e6fa1bf 100644 --- a/tests/core/crypto/test_core_crypto_mac.odin +++ b/tests/core/crypto/test_core_crypto_mac.odin @@ -10,9 +10,11 @@ import "core:crypto/hmac" import "core:crypto/poly1305" import "core:crypto/siphash" +import tc "tests:common" + @(test) test_mac :: proc(t: ^testing.T) { - log(t, "Testing MACs") + tc.log(t, "Testing MACs") test_hmac(t) test_poly1305(t) @@ -81,7 +83,7 @@ test_hmac :: proc(t: ^testing.T) { msg_str := string(hex.encode(msg, context.temp_allocator)) dst_str := string(hex.encode(dst[:tag_len], context.temp_allocator)) - expect( + tc.expect( t, dst_str == expected_str, fmt.tprintf( @@ -97,7 +99,7 @@ test_hmac :: proc(t: ^testing.T) { hmac.sum(algo, dst, msg, key) oneshot_str := string(hex.encode(dst[:tag_len], context.temp_allocator)) - expect( + tc.expect( t, oneshot_str == expected_str, fmt.tprintf( @@ -114,7 +116,7 @@ test_hmac :: proc(t: ^testing.T) { @(test) test_poly1305 :: proc(t: ^testing.T) { - log(t, "Testing poly1305") + tc.log(t, "Testing poly1305") // Test cases taken from poly1305-donna. key := [poly1305.KEY_SIZE]byte { @@ -152,13 +154,13 @@ test_poly1305 :: proc(t: ^testing.T) { // Verify - oneshot + compare ok := poly1305.verify(tag[:], msg[:], key[:]) - expect(t, ok, "oneshot verify call failed") + tc.expect(t, ok, "oneshot verify call failed") // Sum - oneshot derived_tag: [poly1305.TAG_SIZE]byte poly1305.sum(derived_tag[:], msg[:], key[:]) derived_tag_str := string(hex.encode(derived_tag[:], context.temp_allocator)) - expect( + tc.expect( t, derived_tag_str == tag_str, fmt.tprintf("Expected %s for sum(msg, key), but got %s instead", tag_str, derived_tag_str), @@ -177,7 +179,7 @@ test_poly1305 :: proc(t: ^testing.T) { } poly1305.final(&ctx, derived_tag[:]) derived_tag_str = string(hex.encode(derived_tag[:], context.temp_allocator)) - expect( + tc.expect( t, derived_tag_str == tag_str, fmt.tprintf( @@ -190,7 +192,7 @@ test_poly1305 :: proc(t: ^testing.T) { @(test) test_siphash_2_4 :: proc(t: ^testing.T) { - log(t, "Testing SipHash-2-4") + tc.log(t, "Testing SipHash-2-4") // Test vectors from // https://github.com/veorq/SipHash/blob/master/vectors.h @@ -227,7 +229,7 @@ test_siphash_2_4 :: proc(t: ^testing.T) { vector := test_vectors[i] computed := siphash.sum_2_4(data[:], key[:]) - expect( + tc.expect( t, computed == vector, fmt.tprintf( diff --git a/tests/core/crypto/test_core_crypto_sha3_variants.odin b/tests/core/crypto/test_core_crypto_sha3_variants.odin new file mode 100644 index 000000000..ec2d24331 --- /dev/null +++ b/tests/core/crypto/test_core_crypto_sha3_variants.odin @@ -0,0 +1,441 @@ +package test_core_crypto + +import "core:encoding/hex" +import "core:fmt" +import "core:testing" + +import "core:crypto/kmac" +import "core:crypto/shake" +import "core:crypto/tuplehash" + +import tc "tests:common" + +@(test) +test_sha3_variants :: proc(t: ^testing.T) { + tc.log(t, "Testing SHA3 derived functions") + + test_shake(t) + test_cshake(t) + test_tuplehash(t) + test_kmac(t) +} + +@(test) +test_shake :: proc(t: ^testing.T) { + tc.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)) + + tc.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) { + tc.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)) + + tc.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) { + tc.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)) + + tc.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, + ), + ) + } +} + +@(test) +test_kmac :: proc(t:^testing.T) { + tc.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)) + + tc.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, + ), + ) + } +} diff --git a/tests/core/crypto/test_crypto_benchmark.odin b/tests/core/crypto/test_crypto_benchmark.odin index cadcf8bec..494913b6b 100644 --- a/tests/core/crypto/test_crypto_benchmark.odin +++ b/tests/core/crypto/test_crypto_benchmark.odin @@ -10,6 +10,8 @@ import "core:crypto/chacha20poly1305" import "core:crypto/poly1305" import "core:crypto/x25519" +import tc "tests:common" + // Cryptographic primitive benchmarks. @(test) @@ -150,19 +152,19 @@ bench_chacha20 :: proc(t: ^testing.T) { } err := time.benchmark(options, context.allocator) - expect(t, err == nil, name) + tc.expect(t, err == nil, name) benchmark_print(name, options) name = "ChaCha20 1024 bytes" options.bytes = 1024 err = time.benchmark(options, context.allocator) - expect(t, err == nil, name) + tc.expect(t, err == nil, name) benchmark_print(name, options) name = "ChaCha20 65536 bytes" options.bytes = 65536 err = time.benchmark(options, context.allocator) - expect(t, err == nil, name) + tc.expect(t, err == nil, name) benchmark_print(name, options) } @@ -177,13 +179,13 @@ bench_poly1305 :: proc(t: ^testing.T) { } err := time.benchmark(options, context.allocator) - expect(t, err == nil, name) + tc.expect(t, err == nil, name) benchmark_print(name, options) name = "Poly1305 1024 zero bytes" options.bytes = 1024 err = time.benchmark(options, context.allocator) - expect(t, err == nil, name) + tc.expect(t, err == nil, name) benchmark_print(name, options) } @@ -198,19 +200,19 @@ bench_chacha20poly1305 :: proc(t: ^testing.T) { } err := time.benchmark(options, context.allocator) - expect(t, err == nil, name) + tc.expect(t, err == nil, name) benchmark_print(name, options) name = "chacha20poly1305 1024 bytes" options.bytes = 1024 err = time.benchmark(options, context.allocator) - expect(t, err == nil, name) + tc.expect(t, err == nil, name) benchmark_print(name, options) name = "chacha20poly1305 65536 bytes" options.bytes = 65536 err = time.benchmark(options, context.allocator) - expect(t, err == nil, name) + tc.expect(t, err == nil, name) benchmark_print(name, options) } @@ -229,7 +231,7 @@ bench_x25519 :: proc(t: ^testing.T) { } elapsed := time.since(start) - log( + tc.log( t, fmt.tprintf("x25519.scalarmult: ~%f us/op", time.duration_microseconds(elapsed) / iters), ) |