瀏覽代碼

tests/core/crypto: Reorganize

All of our crypto is modern now unless exiled to the legacy sub-package,
so move the test cases for the currently un-unified algorithms into the
main test driver file, and rename the benchmark driver to reflect
reality.
Yawning Angel 1 年之前
父節點
當前提交
bc160d2eb7

+ 1 - 1
.gitignore

@@ -39,7 +39,7 @@ tests/core/test_core_net
 tests/core/test_core_os_exit
 tests/core/test_core_os_exit
 tests/core/test_core_reflect
 tests/core/test_core_reflect
 tests/core/test_core_strings
 tests/core/test_core_strings
-tests/core/test_crypto_hash
+tests/core/test_crypto
 tests/core/test_hash
 tests/core/test_hash
 tests/core/test_hxa
 tests/core/test_hxa
 tests/core/test_json
 tests/core/test_json

+ 1 - 1
tests/core/Makefile

@@ -39,7 +39,7 @@ hash_test:
 	$(ODIN) run hash -o:speed -no-bounds-check -out:test_hash 
 	$(ODIN) run hash -o:speed -no-bounds-check -out:test_hash 
 
 
 crypto_test:
 crypto_test:
-	$(ODIN) run crypto -o:speed -no-bounds-check -out:test_crypto_hash 
+	$(ODIN) run crypto -o:speed -no-bounds-check -out:test_crypto
 
 
 noise_test:
 noise_test:
 	$(ODIN) run math/noise -out:test_noise
 	$(ODIN) run math/noise -out:test_noise

+ 2 - 2
tests/core/build.bat

@@ -29,9 +29,9 @@ echo ---
 %PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_core_odin.exe || exit /b
 %PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_core_odin.exe || exit /b
 
 
 echo ---
 echo ---
-echo Running core:crypto hash tests
+echo Running core:crypto tests
 echo ---
 echo ---
-%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto_hash.exe || exit /b
+%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto.exe || exit /b
 
 
 echo ---
 echo ---
 echo Running core:encoding tests
 echo Running core:encoding tests

+ 435 - 8
tests/core/crypto/test_core_crypto.odin

@@ -8,17 +8,23 @@ package test_core_crypto
 		zhibog, dotbmp:  Initial implementation.
 		zhibog, dotbmp:  Initial implementation.
 		Jeroen van Rijn: Test runner setup.
 		Jeroen van Rijn: Test runner setup.
 
 
-	Tests for the hashing algorithms within the crypto library.
+	Tests for the various algorithms within the crypto library.
 	Where possible, the official test vectors are used to validate the implementation.
 	Where possible, the official test vectors are used to validate the implementation.
 */
 */
 
 
 import "core:encoding/hex"
 import "core:encoding/hex"
 import "core:fmt"
 import "core:fmt"
+import "core:mem"
 import "core:os"
 import "core:os"
 import "core:testing"
 import "core:testing"
 
 
+import "core:crypto"
+import "core:crypto/chacha20"
+import "core:crypto/chacha20poly1305"
+import "core:crypto/poly1305"
 import "core:crypto/siphash"
 import "core:crypto/siphash"
 import "core:crypto/shake"
 import "core:crypto/shake"
+import "core:crypto/x25519"
 
 
 TEST_count := 0
 TEST_count := 0
 TEST_fail := 0
 TEST_fail := 0
@@ -43,19 +49,19 @@ when ODIN_TEST {
 
 
 main :: proc() {
 main :: proc() {
 	t := testing.T{}
 	t := testing.T{}
-	test_hash(&t)
 
 
-	test_shake(&t)
-	test_siphash_2_4(&t)
+	test_rand_bytes(&t)
+
+	test_hash(&t)
 
 
-	// "modern" crypto tests
 	test_chacha20(&t)
 	test_chacha20(&t)
-	test_poly1305(&t)
 	test_chacha20poly1305(&t)
 	test_chacha20poly1305(&t)
+	test_poly1305(&t)
+	test_shake(&t)
+	test_siphash_2_4(&t)
 	test_x25519(&t)
 	test_x25519(&t)
-	test_rand_bytes(&t)
 
 
-	bench_modern(&t)
+	bench_crypto(&t)
 
 
 	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
 	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
 	if TEST_fail > 0 {
 	if TEST_fail > 0 {
@@ -63,6 +69,427 @@ main :: proc() {
 	}
 	}
 }
 }
 
 
+_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")
+
+	// Test cases taken from RFC 8439, and draft-irtf-cfrg-xchacha-03
+	plaintext := transmute([]byte)(_PLAINTEXT_SUNSCREEN_STR)
+
+	key := [chacha20.KEY_SIZE]byte {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+	}
+
+	nonce := [chacha20.NONCE_SIZE]byte {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a,
+		0x00, 0x00, 0x00, 0x00,
+	}
+
+	ciphertext := [114]byte {
+		0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80,
+		0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81,
+		0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2,
+		0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b,
+		0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab,
+		0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
+		0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab,
+		0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8,
+		0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61,
+		0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e,
+		0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06,
+		0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36,
+		0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6,
+		0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
+		0x87, 0x4d,
+	}
+	ciphertext_str := string(hex.encode(ciphertext[:], context.temp_allocator))
+
+	derived_ciphertext: [114]byte
+	ctx: chacha20.Context = ---
+	chacha20.init(&ctx, key[:], nonce[:])
+	chacha20.seek(&ctx, 1) // The test vectors start the counter at 1.
+	chacha20.xor_bytes(&ctx, derived_ciphertext[:], plaintext[:])
+
+	derived_ciphertext_str := string(hex.encode(derived_ciphertext[:], context.temp_allocator))
+	expect(
+		t,
+		derived_ciphertext_str == ciphertext_str,
+		fmt.tprintf(
+			"Expected %s for xor_bytes(plaintext_str), but got %s instead",
+			ciphertext_str,
+			derived_ciphertext_str,
+		),
+	)
+
+	xkey := [chacha20.KEY_SIZE]byte {
+		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+		0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+		0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+		0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+	}
+
+	xnonce := [chacha20.XNONCE_SIZE]byte {
+		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+		0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+		0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+	}
+
+	xciphertext := [114]byte {
+		0xbd, 0x6d, 0x17, 0x9d, 0x3e, 0x83, 0xd4, 0x3b,
+		0x95, 0x76, 0x57, 0x94, 0x93, 0xc0, 0xe9, 0x39,
+		0x57, 0x2a, 0x17, 0x00, 0x25, 0x2b, 0xfa, 0xcc,
+		0xbe, 0xd2, 0x90, 0x2c, 0x21, 0x39, 0x6c, 0xbb,
+		0x73, 0x1c, 0x7f, 0x1b, 0x0b, 0x4a, 0xa6, 0x44,
+		0x0b, 0xf3, 0xa8, 0x2f, 0x4e, 0xda, 0x7e, 0x39,
+		0xae, 0x64, 0xc6, 0x70, 0x8c, 0x54, 0xc2, 0x16,
+		0xcb, 0x96, 0xb7, 0x2e, 0x12, 0x13, 0xb4, 0x52,
+		0x2f, 0x8c, 0x9b, 0xa4, 0x0d, 0xb5, 0xd9, 0x45,
+		0xb1, 0x1b, 0x69, 0xb9, 0x82, 0xc1, 0xbb, 0x9e,
+		0x3f, 0x3f, 0xac, 0x2b, 0xc3, 0x69, 0x48, 0x8f,
+		0x76, 0xb2, 0x38, 0x35, 0x65, 0xd3, 0xff, 0xf9,
+		0x21, 0xf9, 0x66, 0x4c, 0x97, 0x63, 0x7d, 0xa9,
+		0x76, 0x88, 0x12, 0xf6, 0x15, 0xc6, 0x8b, 0x13,
+		0xb5, 0x2e,
+	}
+	xciphertext_str := string(hex.encode(xciphertext[:], context.temp_allocator))
+
+	chacha20.init(&ctx, xkey[:], xnonce[:])
+	chacha20.seek(&ctx, 1)
+	chacha20.xor_bytes(&ctx, derived_ciphertext[:], plaintext[:])
+
+	derived_ciphertext_str = string(hex.encode(derived_ciphertext[:], context.temp_allocator))
+	expect(
+		t,
+		derived_ciphertext_str == xciphertext_str,
+		fmt.tprintf(
+			"Expected %s for xor_bytes(plaintext_str), but got %s instead",
+			xciphertext_str,
+			derived_ciphertext_str,
+		),
+	)
+}
+
+@(test)
+test_poly1305 :: proc(t: ^testing.T) {
+	log(t, "Testing poly1305")
+
+	// Test cases taken from poly1305-donna.
+	key := [poly1305.KEY_SIZE]byte {
+		0xee, 0xa6, 0xa7, 0x25, 0x1c, 0x1e, 0x72, 0x91,
+		0x6d, 0x11, 0xc2, 0xcb, 0x21, 0x4d, 0x3c, 0x25,
+		0x25, 0x39, 0x12, 0x1d, 0x8e, 0x23, 0x4e, 0x65,
+		0x2d, 0x65, 0x1f, 0xa4, 0xc8, 0xcf, 0xf8, 0x80,
+	}
+
+	msg := [131]byte {
+		0x8e, 0x99, 0x3b, 0x9f, 0x48, 0x68, 0x12, 0x73,
+		0xc2, 0x96, 0x50, 0xba, 0x32, 0xfc, 0x76, 0xce,
+		0x48, 0x33, 0x2e, 0xa7, 0x16, 0x4d, 0x96, 0xa4,
+		0x47, 0x6f, 0xb8, 0xc5, 0x31, 0xa1, 0x18, 0x6a,
+		0xc0, 0xdf, 0xc1, 0x7c, 0x98, 0xdc, 0xe8, 0x7b,
+		0x4d, 0xa7, 0xf0, 0x11, 0xec, 0x48, 0xc9, 0x72,
+		0x71, 0xd2, 0xc2, 0x0f, 0x9b, 0x92, 0x8f, 0xe2,
+		0x27, 0x0d, 0x6f, 0xb8, 0x63, 0xd5, 0x17, 0x38,
+		0xb4, 0x8e, 0xee, 0xe3, 0x14, 0xa7, 0xcc, 0x8a,
+		0xb9, 0x32, 0x16, 0x45, 0x48, 0xe5, 0x26, 0xae,
+		0x90, 0x22, 0x43, 0x68, 0x51, 0x7a, 0xcf, 0xea,
+		0xbd, 0x6b, 0xb3, 0x73, 0x2b, 0xc0, 0xe9, 0xda,
+		0x99, 0x83, 0x2b, 0x61, 0xca, 0x01, 0xb6, 0xde,
+		0x56, 0x24, 0x4a, 0x9e, 0x88, 0xd5, 0xf9, 0xb3,
+		0x79, 0x73, 0xf6, 0x22, 0xa4, 0x3d, 0x14, 0xa6,
+		0x59, 0x9b, 0x1f, 0x65, 0x4c, 0xb4, 0x5a, 0x74,
+		0xe3, 0x55, 0xa5,
+	}
+
+	tag := [poly1305.TAG_SIZE]byte {
+		0xf3, 0xff, 0xc7, 0x70, 0x3f, 0x94, 0x00, 0xe5,
+		0x2a, 0x7d, 0xfb, 0x4b, 0x3d, 0x33, 0x05, 0xd9,
+	}
+	tag_str := string(hex.encode(tag[:], context.temp_allocator))
+
+	// Verify - oneshot + compare
+	ok := poly1305.verify(tag[:], msg[:], key[:])
+	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(
+		t,
+		derived_tag_str == tag_str,
+		fmt.tprintf("Expected %s for sum(msg, key), but got %s instead", tag_str, derived_tag_str),
+	)
+
+	// Incremental
+	mem.zero(&derived_tag, size_of(derived_tag))
+	ctx: poly1305.Context = ---
+	poly1305.init(&ctx, key[:])
+	read_lengths := [11]int{32, 64, 16, 8, 4, 2, 1, 1, 1, 1, 1}
+	off := 0
+	for read_length in read_lengths {
+		to_read := msg[off:off + read_length]
+		poly1305.update(&ctx, to_read)
+		off = off + read_length
+	}
+	poly1305.final(&ctx, derived_tag[:])
+	derived_tag_str = string(hex.encode(derived_tag[:], context.temp_allocator))
+	expect(
+		t,
+		derived_tag_str == tag_str,
+		fmt.tprintf(
+			"Expected %s for init/update/final - incremental, but got %s instead",
+			tag_str,
+			derived_tag_str,
+		),
+	)
+}
+
+@(test)
+test_chacha20poly1305 :: proc(t: ^testing.T) {
+	log(t, "Testing chacha20poly1205")
+
+	plaintext := transmute([]byte)(_PLAINTEXT_SUNSCREEN_STR)
+
+	aad := [12]byte {
+		0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3,
+		0xc4, 0xc5, 0xc6, 0xc7,
+	}
+
+	key := [chacha20poly1305.KEY_SIZE]byte {
+		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+		0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+		0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+		0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+	}
+
+	nonce := [chacha20poly1305.NONCE_SIZE]byte {
+		0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43,
+		0x44, 0x45, 0x46, 0x47,
+	}
+
+	ciphertext := [114]byte {
+		0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb,
+		0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
+		0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
+		0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
+		0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12,
+		0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
+		0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29,
+		0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36,
+		0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
+		0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58,
+		0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94,
+		0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
+		0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d,
+		0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b,
+		0x61, 0x16,
+	}
+	ciphertext_str := string(hex.encode(ciphertext[:], context.temp_allocator))
+
+	tag := [chacha20poly1305.TAG_SIZE]byte {
+		0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a,
+		0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91,
+	}
+	tag_str := string(hex.encode(tag[:], context.temp_allocator))
+
+	derived_tag: [chacha20poly1305.TAG_SIZE]byte
+	derived_ciphertext: [114]byte
+
+	chacha20poly1305.encrypt(
+		derived_ciphertext[:],
+		derived_tag[:],
+		key[:],
+		nonce[:],
+		aad[:],
+		plaintext,
+	)
+
+	derived_ciphertext_str := string(hex.encode(derived_ciphertext[:], context.temp_allocator))
+	expect(
+		t,
+		derived_ciphertext_str == ciphertext_str,
+		fmt.tprintf(
+			"Expected ciphertext %s for encrypt(aad, plaintext), but got %s instead",
+			ciphertext_str,
+			derived_ciphertext_str,
+		),
+	)
+
+	derived_tag_str := string(hex.encode(derived_tag[:], context.temp_allocator))
+	expect(
+		t,
+		derived_tag_str == tag_str,
+		fmt.tprintf(
+			"Expected tag %s for encrypt(aad, plaintext), but got %s instead",
+			tag_str,
+			derived_tag_str,
+		),
+	)
+
+	derived_plaintext: [114]byte
+	ok := chacha20poly1305.decrypt(
+		derived_plaintext[:],
+		tag[:],
+		key[:],
+		nonce[:],
+		aad[:],
+		ciphertext[:],
+	)
+	derived_plaintext_str := string(derived_plaintext[:])
+	expect(t, ok, "Expected true for decrypt(tag, aad, ciphertext)")
+	expect(
+		t,
+		derived_plaintext_str == _PLAINTEXT_SUNSCREEN_STR,
+		fmt.tprintf(
+			"Expected plaintext %s for decrypt(tag, aad, ciphertext), but got %s instead",
+			_PLAINTEXT_SUNSCREEN_STR,
+			derived_plaintext_str,
+		),
+	)
+
+	derived_ciphertext[0] ~= 0xa5
+	ok = chacha20poly1305.decrypt(
+		derived_plaintext[:],
+		tag[:],
+		key[:],
+		nonce[:],
+		aad[:],
+		derived_ciphertext[:],
+	)
+	expect(t, !ok, "Expected false for decrypt(tag, aad, corrupted_ciphertext)")
+
+	aad[0] ~= 0xa5
+	ok = chacha20poly1305.decrypt(
+		derived_plaintext[:],
+		tag[:],
+		key[:],
+		nonce[:],
+		aad[:],
+		ciphertext[:],
+	)
+	expect(t, !ok, "Expected false for decrypt(tag, corrupted_aad, ciphertext)")
+}
+
+TestECDH :: struct {
+	scalar:  string,
+	point:   string,
+	product: string,
+}
+
+@(test)
+test_x25519 :: proc(t: ^testing.T) {
+	log(t, "Testing X25519")
+
+	// Local copy of this so that the base point doesn't need to be exported.
+	_BASE_POINT: [32]byte =  {
+		9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	}
+
+	test_vectors := [?]TestECDH {
+		// Test vectors from RFC 7748
+		{
+			"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
+			"e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c",
+			"c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552",
+		},
+		{
+			"4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d",
+			"e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493",
+			"95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957",
+		},
+	}
+	for v, _ in test_vectors {
+		scalar, _ := hex.decode(transmute([]byte)(v.scalar), context.temp_allocator)
+		point, _ := hex.decode(transmute([]byte)(v.point), context.temp_allocator)
+
+		derived_point: [x25519.POINT_SIZE]byte
+		x25519.scalarmult(derived_point[:], scalar[:], point[:])
+		derived_point_str := string(hex.encode(derived_point[:], context.temp_allocator))
+
+		expect(
+			t,
+			derived_point_str == v.product,
+			fmt.tprintf(
+				"Expected %s for %s * %s, but got %s instead",
+				v.product,
+				v.scalar,
+				v.point,
+				derived_point_str,
+			),
+		)
+
+		// Abuse the test vectors to sanity-check the scalar-basepoint multiply.
+		p1, p2: [x25519.POINT_SIZE]byte
+		x25519.scalarmult_basepoint(p1[:], scalar[:])
+		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(
+			t,
+			p1_str == p2_str,
+			fmt.tprintf(
+				"Expected %s for %s * basepoint, but got %s instead",
+				p2_str,
+				v.scalar,
+				p1_str,
+			),
+		)
+	}
+
+	// TODO/tests: Run the wycheproof test vectors, once I figure out
+	// how to work with JSON.
+}
+
+@(test)
+test_rand_bytes :: proc(t: ^testing.T) {
+	log(t, "Testing rand_bytes")
+
+	if ODIN_OS != .Linux {
+		log(t, "rand_bytes not supported - skipping")
+		return
+	}
+
+	allocator := context.allocator
+
+	buf := make([]byte, 1 << 25, allocator)
+	defer delete(buf)
+
+	// Testing a CSPRNG for correctness is incredibly involved and
+	// beyond the scope of an implementation that offloads
+	// responsibility for correctness to the OS.
+	//
+	// Just attempt to randomize a sufficiently large buffer, where
+	// sufficiently large is:
+	//  * Larger than the maximum getentropy request size (256 bytes).
+	//  * Larger than the maximum getrandom request size (2^25 - 1 bytes).
+	//
+	// While theoretically non-deterministic, if this fails, chances
+	// are the CSPRNG is busted.
+	seems_ok := false
+	for i := 0; i < 256; i = i + 1 {
+		mem.zero_explicit(raw_data(buf), len(buf))
+		crypto.rand_bytes(buf)
+
+		if buf[0] != 0 && buf[len(buf) - 1] != 0 {
+			seems_ok = true
+			break
+		}
+	}
+
+	expect(
+		t,
+		seems_ok,
+		"Expected to randomize the head and tail of the buffer within a handful of attempts",
+	)
+}
+
 TestXOF :: struct {
 TestXOF :: struct {
 	sec_strength: int,
 	sec_strength: int,
 	output:       string,
 	output:       string,

+ 0 - 657
tests/core/crypto/test_core_crypto_modern.odin

@@ -1,657 +0,0 @@
-package test_core_crypto
-
-import "core:crypto"
-import "core:encoding/hex"
-import "core:fmt"
-import "core:mem"
-import "core:testing"
-import "core:time"
-
-import "core:crypto/chacha20"
-import "core:crypto/chacha20poly1305"
-import "core:crypto/poly1305"
-import "core:crypto/x25519"
-
-_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")
-
-	// Test cases taken from RFC 8439, and draft-irtf-cfrg-xchacha-03
-	plaintext := transmute([]byte)(_PLAINTEXT_SUNSCREEN_STR)
-
-	key := [chacha20.KEY_SIZE]byte {
-		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
-		0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-	}
-
-	nonce := [chacha20.NONCE_SIZE]byte {
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a,
-		0x00, 0x00, 0x00, 0x00,
-	}
-
-	ciphertext := [114]byte {
-		0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80,
-		0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81,
-		0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2,
-		0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b,
-		0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab,
-		0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
-		0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab,
-		0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8,
-		0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61,
-		0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e,
-		0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06,
-		0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36,
-		0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6,
-		0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
-		0x87, 0x4d,
-	}
-	ciphertext_str := string(hex.encode(ciphertext[:], context.temp_allocator))
-
-	derived_ciphertext: [114]byte
-	ctx: chacha20.Context = ---
-	chacha20.init(&ctx, key[:], nonce[:])
-	chacha20.seek(&ctx, 1) // The test vectors start the counter at 1.
-	chacha20.xor_bytes(&ctx, derived_ciphertext[:], plaintext[:])
-
-	derived_ciphertext_str := string(hex.encode(derived_ciphertext[:], context.temp_allocator))
-	expect(
-		t,
-		derived_ciphertext_str == ciphertext_str,
-		fmt.tprintf(
-			"Expected %s for xor_bytes(plaintext_str), but got %s instead",
-			ciphertext_str,
-			derived_ciphertext_str,
-		),
-	)
-
-	xkey := [chacha20.KEY_SIZE]byte {
-		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
-		0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
-		0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
-		0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
-	}
-
-	xnonce := [chacha20.XNONCE_SIZE]byte {
-		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
-		0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
-		0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
-	}
-
-	xciphertext := [114]byte {
-		0xbd, 0x6d, 0x17, 0x9d, 0x3e, 0x83, 0xd4, 0x3b,
-		0x95, 0x76, 0x57, 0x94, 0x93, 0xc0, 0xe9, 0x39,
-		0x57, 0x2a, 0x17, 0x00, 0x25, 0x2b, 0xfa, 0xcc,
-		0xbe, 0xd2, 0x90, 0x2c, 0x21, 0x39, 0x6c, 0xbb,
-		0x73, 0x1c, 0x7f, 0x1b, 0x0b, 0x4a, 0xa6, 0x44,
-		0x0b, 0xf3, 0xa8, 0x2f, 0x4e, 0xda, 0x7e, 0x39,
-		0xae, 0x64, 0xc6, 0x70, 0x8c, 0x54, 0xc2, 0x16,
-		0xcb, 0x96, 0xb7, 0x2e, 0x12, 0x13, 0xb4, 0x52,
-		0x2f, 0x8c, 0x9b, 0xa4, 0x0d, 0xb5, 0xd9, 0x45,
-		0xb1, 0x1b, 0x69, 0xb9, 0x82, 0xc1, 0xbb, 0x9e,
-		0x3f, 0x3f, 0xac, 0x2b, 0xc3, 0x69, 0x48, 0x8f,
-		0x76, 0xb2, 0x38, 0x35, 0x65, 0xd3, 0xff, 0xf9,
-		0x21, 0xf9, 0x66, 0x4c, 0x97, 0x63, 0x7d, 0xa9,
-		0x76, 0x88, 0x12, 0xf6, 0x15, 0xc6, 0x8b, 0x13,
-		0xb5, 0x2e,
-	}
-	xciphertext_str := string(hex.encode(xciphertext[:], context.temp_allocator))
-
-	chacha20.init(&ctx, xkey[:], xnonce[:])
-	chacha20.seek(&ctx, 1)
-	chacha20.xor_bytes(&ctx, derived_ciphertext[:], plaintext[:])
-
-	derived_ciphertext_str = string(hex.encode(derived_ciphertext[:], context.temp_allocator))
-	expect(
-		t,
-		derived_ciphertext_str == xciphertext_str,
-		fmt.tprintf(
-			"Expected %s for xor_bytes(plaintext_str), but got %s instead",
-			xciphertext_str,
-			derived_ciphertext_str,
-		),
-	)
-}
-
-@(test)
-test_poly1305 :: proc(t: ^testing.T) {
-	log(t, "Testing poly1305")
-
-	// Test cases taken from poly1305-donna.
-	key := [poly1305.KEY_SIZE]byte {
-		0xee, 0xa6, 0xa7, 0x25, 0x1c, 0x1e, 0x72, 0x91,
-		0x6d, 0x11, 0xc2, 0xcb, 0x21, 0x4d, 0x3c, 0x25,
-		0x25, 0x39, 0x12, 0x1d, 0x8e, 0x23, 0x4e, 0x65,
-		0x2d, 0x65, 0x1f, 0xa4, 0xc8, 0xcf, 0xf8, 0x80,
-	}
-
-	msg := [131]byte {
-		0x8e, 0x99, 0x3b, 0x9f, 0x48, 0x68, 0x12, 0x73,
-		0xc2, 0x96, 0x50, 0xba, 0x32, 0xfc, 0x76, 0xce,
-		0x48, 0x33, 0x2e, 0xa7, 0x16, 0x4d, 0x96, 0xa4,
-		0x47, 0x6f, 0xb8, 0xc5, 0x31, 0xa1, 0x18, 0x6a,
-		0xc0, 0xdf, 0xc1, 0x7c, 0x98, 0xdc, 0xe8, 0x7b,
-		0x4d, 0xa7, 0xf0, 0x11, 0xec, 0x48, 0xc9, 0x72,
-		0x71, 0xd2, 0xc2, 0x0f, 0x9b, 0x92, 0x8f, 0xe2,
-		0x27, 0x0d, 0x6f, 0xb8, 0x63, 0xd5, 0x17, 0x38,
-		0xb4, 0x8e, 0xee, 0xe3, 0x14, 0xa7, 0xcc, 0x8a,
-		0xb9, 0x32, 0x16, 0x45, 0x48, 0xe5, 0x26, 0xae,
-		0x90, 0x22, 0x43, 0x68, 0x51, 0x7a, 0xcf, 0xea,
-		0xbd, 0x6b, 0xb3, 0x73, 0x2b, 0xc0, 0xe9, 0xda,
-		0x99, 0x83, 0x2b, 0x61, 0xca, 0x01, 0xb6, 0xde,
-		0x56, 0x24, 0x4a, 0x9e, 0x88, 0xd5, 0xf9, 0xb3,
-		0x79, 0x73, 0xf6, 0x22, 0xa4, 0x3d, 0x14, 0xa6,
-		0x59, 0x9b, 0x1f, 0x65, 0x4c, 0xb4, 0x5a, 0x74,
-		0xe3, 0x55, 0xa5,
-	}
-
-	tag := [poly1305.TAG_SIZE]byte {
-		0xf3, 0xff, 0xc7, 0x70, 0x3f, 0x94, 0x00, 0xe5,
-		0x2a, 0x7d, 0xfb, 0x4b, 0x3d, 0x33, 0x05, 0xd9,
-	}
-	tag_str := string(hex.encode(tag[:], context.temp_allocator))
-
-	// Verify - oneshot + compare
-	ok := poly1305.verify(tag[:], msg[:], key[:])
-	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(
-		t,
-		derived_tag_str == tag_str,
-		fmt.tprintf("Expected %s for sum(msg, key), but got %s instead", tag_str, derived_tag_str),
-	)
-
-	// Incremental
-	mem.zero(&derived_tag, size_of(derived_tag))
-	ctx: poly1305.Context = ---
-	poly1305.init(&ctx, key[:])
-	read_lengths := [11]int{32, 64, 16, 8, 4, 2, 1, 1, 1, 1, 1}
-	off := 0
-	for read_length in read_lengths {
-		to_read := msg[off:off + read_length]
-		poly1305.update(&ctx, to_read)
-		off = off + read_length
-	}
-	poly1305.final(&ctx, derived_tag[:])
-	derived_tag_str = string(hex.encode(derived_tag[:], context.temp_allocator))
-	expect(
-		t,
-		derived_tag_str == tag_str,
-		fmt.tprintf(
-			"Expected %s for init/update/final - incremental, but got %s instead",
-			tag_str,
-			derived_tag_str,
-		),
-	)
-}
-
-@(test)
-test_chacha20poly1305 :: proc(t: ^testing.T) {
-	log(t, "Testing chacha20poly1205")
-
-	plaintext := transmute([]byte)(_PLAINTEXT_SUNSCREEN_STR)
-
-	aad := [12]byte {
-		0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3,
-		0xc4, 0xc5, 0xc6, 0xc7,
-	}
-
-	key := [chacha20poly1305.KEY_SIZE]byte {
-		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
-		0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
-		0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
-		0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
-	}
-
-	nonce := [chacha20poly1305.NONCE_SIZE]byte {
-		0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43,
-		0x44, 0x45, 0x46, 0x47,
-	}
-
-	ciphertext := [114]byte {
-		0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb,
-		0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
-		0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
-		0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
-		0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12,
-		0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
-		0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29,
-		0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36,
-		0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
-		0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58,
-		0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94,
-		0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
-		0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d,
-		0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b,
-		0x61, 0x16,
-	}
-	ciphertext_str := string(hex.encode(ciphertext[:], context.temp_allocator))
-
-	tag := [chacha20poly1305.TAG_SIZE]byte {
-		0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a,
-		0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91,
-	}
-	tag_str := string(hex.encode(tag[:], context.temp_allocator))
-
-	derived_tag: [chacha20poly1305.TAG_SIZE]byte
-	derived_ciphertext: [114]byte
-
-	chacha20poly1305.encrypt(
-		derived_ciphertext[:],
-		derived_tag[:],
-		key[:],
-		nonce[:],
-		aad[:],
-		plaintext,
-	)
-
-	derived_ciphertext_str := string(hex.encode(derived_ciphertext[:], context.temp_allocator))
-	expect(
-		t,
-		derived_ciphertext_str == ciphertext_str,
-		fmt.tprintf(
-			"Expected ciphertext %s for encrypt(aad, plaintext), but got %s instead",
-			ciphertext_str,
-			derived_ciphertext_str,
-		),
-	)
-
-	derived_tag_str := string(hex.encode(derived_tag[:], context.temp_allocator))
-	expect(
-		t,
-		derived_tag_str == tag_str,
-		fmt.tprintf(
-			"Expected tag %s for encrypt(aad, plaintext), but got %s instead",
-			tag_str,
-			derived_tag_str,
-		),
-	)
-
-	derived_plaintext: [114]byte
-	ok := chacha20poly1305.decrypt(
-		derived_plaintext[:],
-		tag[:],
-		key[:],
-		nonce[:],
-		aad[:],
-		ciphertext[:],
-	)
-	derived_plaintext_str := string(derived_plaintext[:])
-	expect(t, ok, "Expected true for decrypt(tag, aad, ciphertext)")
-	expect(
-		t,
-		derived_plaintext_str == _PLAINTEXT_SUNSCREEN_STR,
-		fmt.tprintf(
-			"Expected plaintext %s for decrypt(tag, aad, ciphertext), but got %s instead",
-			_PLAINTEXT_SUNSCREEN_STR,
-			derived_plaintext_str,
-		),
-	)
-
-	derived_ciphertext[0] ~= 0xa5
-	ok = chacha20poly1305.decrypt(
-		derived_plaintext[:],
-		tag[:],
-		key[:],
-		nonce[:],
-		aad[:],
-		derived_ciphertext[:],
-	)
-	expect(t, !ok, "Expected false for decrypt(tag, aad, corrupted_ciphertext)")
-
-	aad[0] ~= 0xa5
-	ok = chacha20poly1305.decrypt(
-		derived_plaintext[:],
-		tag[:],
-		key[:],
-		nonce[:],
-		aad[:],
-		ciphertext[:],
-	)
-	expect(t, !ok, "Expected false for decrypt(tag, corrupted_aad, ciphertext)")
-}
-
-TestECDH :: struct {
-	scalar:  string,
-	point:   string,
-	product: string,
-}
-
-@(test)
-test_x25519 :: proc(t: ^testing.T) {
-	log(t, "Testing X25519")
-
-	// Local copy of this so that the base point doesn't need to be exported.
-	_BASE_POINT: [32]byte =  {
-		9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	}
-
-	test_vectors := [?]TestECDH {
-		// Test vectors from RFC 7748
-		TestECDH {
-			"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
-			"e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c",
-			"c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552",
-		},
-		TestECDH {
-			"4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d",
-			"e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493",
-			"95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957",
-		},
-	}
-	for v, _ in test_vectors {
-		scalar, _ := hex.decode(transmute([]byte)(v.scalar), context.temp_allocator)
-		point, _ := hex.decode(transmute([]byte)(v.point), context.temp_allocator)
-
-		derived_point: [x25519.POINT_SIZE]byte
-		x25519.scalarmult(derived_point[:], scalar[:], point[:])
-		derived_point_str := string(hex.encode(derived_point[:], context.temp_allocator))
-
-		expect(
-			t,
-			derived_point_str == v.product,
-			fmt.tprintf(
-				"Expected %s for %s * %s, but got %s instead",
-				v.product,
-				v.scalar,
-				v.point,
-				derived_point_str,
-			),
-		)
-
-		// Abuse the test vectors to sanity-check the scalar-basepoint multiply.
-		p1, p2: [x25519.POINT_SIZE]byte
-		x25519.scalarmult_basepoint(p1[:], scalar[:])
-		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(
-			t,
-			p1_str == p2_str,
-			fmt.tprintf(
-				"Expected %s for %s * basepoint, but got %s instead",
-				p2_str,
-				v.scalar,
-				p1_str,
-			),
-		)
-	}
-
-	// TODO/tests: Run the wycheproof test vectors, once I figure out
-	// how to work with JSON.
-}
-
-@(test)
-test_rand_bytes :: proc(t: ^testing.T) {
-	log(t, "Testing rand_bytes")
-
-	if ODIN_OS != .Linux {
-		log(t, "rand_bytes not supported - skipping")
-		return
-	}
-
-	allocator := context.allocator
-
-	buf := make([]byte, 1 << 25, allocator)
-	defer delete(buf)
-
-	// Testing a CSPRNG for correctness is incredibly involved and
-	// beyond the scope of an implementation that offloads
-	// responsibility for correctness to the OS.
-	//
-	// Just attempt to randomize a sufficiently large buffer, where
-	// sufficiently large is:
-	//  * Larger than the maximum getentropy request size (256 bytes).
-	//  * Larger than the maximum getrandom request size (2^25 - 1 bytes).
-	//
-	// While theoretically non-deterministic, if this fails, chances
-	// are the CSPRNG is busted.
-	seems_ok := false
-	for i := 0; i < 256; i = i + 1 {
-		mem.zero_explicit(raw_data(buf), len(buf))
-		crypto.rand_bytes(buf)
-
-		if buf[0] != 0 && buf[len(buf) - 1] != 0 {
-			seems_ok = true
-			break
-		}
-	}
-
-	expect(
-		t,
-		seems_ok,
-		"Expected to randomize the head and tail of the buffer within a handful of attempts",
-	)
-}
-
-@(test)
-bench_modern :: proc(t: ^testing.T) {
-	fmt.println("Starting benchmarks:")
-
-	bench_chacha20(t)
-	bench_poly1305(t)
-	bench_chacha20poly1305(t)
-	bench_x25519(t)
-}
-
-_setup_sized_buf :: proc(
-	options: ^time.Benchmark_Options,
-	allocator := context.allocator,
-) -> (
-	err: time.Benchmark_Error,
-) {
-	assert(options != nil)
-
-	options.input = make([]u8, options.bytes, allocator)
-	return nil if len(options.input) == options.bytes else .Allocation_Error
-}
-
-_teardown_sized_buf :: proc(
-	options: ^time.Benchmark_Options,
-	allocator := context.allocator,
-) -> (
-	err: time.Benchmark_Error,
-) {
-	assert(options != nil)
-
-	delete(options.input)
-	return nil
-}
-
-_benchmark_chacha20 :: proc(
-	options: ^time.Benchmark_Options,
-	allocator := context.allocator,
-) -> (
-	err: time.Benchmark_Error,
-) {
-	buf := options.input
-	key := [chacha20.KEY_SIZE]byte {
-		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
-		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
-		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
-		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
-	}
-	nonce := [chacha20.NONCE_SIZE]byte {
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00,
-	}
-
-	ctx: chacha20.Context = ---
-	chacha20.init(&ctx, key[:], nonce[:])
-
-	for _ in 0 ..= options.rounds {
-		chacha20.xor_bytes(&ctx, buf, buf)
-	}
-	options.count = options.rounds
-	options.processed = options.rounds * options.bytes
-	return nil
-}
-
-_benchmark_poly1305 :: proc(
-	options: ^time.Benchmark_Options,
-	allocator := context.allocator,
-) -> (
-	err: time.Benchmark_Error,
-) {
-	buf := options.input
-	key := [poly1305.KEY_SIZE]byte {
-		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
-		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
-		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
-		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
-	}
-
-	tag: [poly1305.TAG_SIZE]byte = ---
-	for _ in 0 ..= options.rounds {
-		poly1305.sum(tag[:], buf, key[:])
-	}
-	options.count = options.rounds
-	options.processed = options.rounds * options.bytes
-	//options.hash      = u128(h)
-	return nil
-}
-
-_benchmark_chacha20poly1305 :: proc(
-	options: ^time.Benchmark_Options,
-	allocator := context.allocator,
-) -> (
-	err: time.Benchmark_Error,
-) {
-	buf := options.input
-	key := [chacha20.KEY_SIZE]byte {
-		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
-		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
-		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
-		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
-	}
-	nonce := [chacha20.NONCE_SIZE]byte {
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00,
-	}
-
-	tag: [chacha20poly1305.TAG_SIZE]byte = ---
-
-	for _ in 0 ..= options.rounds {
-		chacha20poly1305.encrypt(buf, tag[:], key[:], nonce[:], nil, buf)
-	}
-	options.count = options.rounds
-	options.processed = options.rounds * options.bytes
-	return nil
-}
-
-benchmark_print :: proc(name: string, options: ^time.Benchmark_Options) {
-	fmt.printf(
-		"\t[%v] %v rounds, %v bytes processed in %v ns\n\t\t%5.3f rounds/s, %5.3f MiB/s\n",
-		name,
-		options.rounds,
-		options.processed,
-		time.duration_nanoseconds(options.duration),
-		options.rounds_per_second,
-		options.megabytes_per_second,
-	)
-}
-
-bench_chacha20 :: proc(t: ^testing.T) {
-	name := "ChaCha20 64 bytes"
-	options := &time.Benchmark_Options {
-		rounds = 1_000,
-		bytes = 64,
-		setup = _setup_sized_buf,
-		bench = _benchmark_chacha20,
-		teardown = _teardown_sized_buf,
-	}
-
-	err := time.benchmark(options, context.allocator)
-	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)
-	benchmark_print(name, options)
-
-	name = "ChaCha20 65536 bytes"
-	options.bytes = 65536
-	err = time.benchmark(options, context.allocator)
-	expect(t, err == nil, name)
-	benchmark_print(name, options)
-}
-
-bench_poly1305 :: proc(t: ^testing.T) {
-	name := "Poly1305 64 zero bytes"
-	options := &time.Benchmark_Options {
-		rounds = 1_000,
-		bytes = 64,
-		setup = _setup_sized_buf,
-		bench = _benchmark_poly1305,
-		teardown = _teardown_sized_buf,
-	}
-
-	err := time.benchmark(options, context.allocator)
-	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)
-	benchmark_print(name, options)
-}
-
-bench_chacha20poly1305 :: proc(t: ^testing.T) {
-	name := "chacha20poly1305 64 bytes"
-	options := &time.Benchmark_Options {
-		rounds = 1_000,
-		bytes = 64,
-		setup = _setup_sized_buf,
-		bench = _benchmark_chacha20poly1305,
-		teardown = _teardown_sized_buf,
-	}
-
-	err := time.benchmark(options, context.allocator)
-	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)
-	benchmark_print(name, options)
-
-	name = "chacha20poly1305 65536 bytes"
-	options.bytes = 65536
-	err = time.benchmark(options, context.allocator)
-	expect(t, err == nil, name)
-	benchmark_print(name, options)
-}
-
-bench_x25519 :: proc(t: ^testing.T) {
-	point_str := "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
-	scalar_str := "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"
-
-	point, _ := hex.decode(transmute([]byte)(point_str), context.temp_allocator)
-	scalar, _ := hex.decode(transmute([]byte)(scalar_str), context.temp_allocator)
-	out: [x25519.POINT_SIZE]byte = ---
-
-	iters :: 10000
-	start := time.now()
-	for i := 0; i < iters; i = i + 1 {
-		x25519.scalarmult(out[:], scalar[:], point[:])
-	}
-	elapsed := time.since(start)
-
-	log(
-		t,
-		fmt.tprintf("x25519.scalarmult: ~%f us/op", time.duration_microseconds(elapsed) / iters),
-	)
-}

+ 236 - 0
tests/core/crypto/test_crypto_benchmark.odin

@@ -0,0 +1,236 @@
+package test_core_crypto
+
+import "core:encoding/hex"
+import "core:fmt"
+import "core:testing"
+import "core:time"
+
+import "core:crypto/chacha20"
+import "core:crypto/chacha20poly1305"
+import "core:crypto/poly1305"
+import "core:crypto/x25519"
+
+// Cryptographic primitive benchmarks.
+
+@(test)
+bench_crypto :: proc(t: ^testing.T) {
+	fmt.println("Starting benchmarks:")
+
+	bench_chacha20(t)
+	bench_poly1305(t)
+	bench_chacha20poly1305(t)
+	bench_x25519(t)
+}
+
+_setup_sized_buf :: proc(
+	options: ^time.Benchmark_Options,
+	allocator := context.allocator,
+) -> (
+	err: time.Benchmark_Error,
+) {
+	assert(options != nil)
+
+	options.input = make([]u8, options.bytes, allocator)
+	return nil if len(options.input) == options.bytes else .Allocation_Error
+}
+
+_teardown_sized_buf :: proc(
+	options: ^time.Benchmark_Options,
+	allocator := context.allocator,
+) -> (
+	err: time.Benchmark_Error,
+) {
+	assert(options != nil)
+
+	delete(options.input)
+	return nil
+}
+
+_benchmark_chacha20 :: proc(
+	options: ^time.Benchmark_Options,
+	allocator := context.allocator,
+) -> (
+	err: time.Benchmark_Error,
+) {
+	buf := options.input
+	key := [chacha20.KEY_SIZE]byte {
+		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
+		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
+		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
+		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
+	}
+	nonce := [chacha20.NONCE_SIZE]byte {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00,
+	}
+
+	ctx: chacha20.Context = ---
+	chacha20.init(&ctx, key[:], nonce[:])
+
+	for _ in 0 ..= options.rounds {
+		chacha20.xor_bytes(&ctx, buf, buf)
+	}
+	options.count = options.rounds
+	options.processed = options.rounds * options.bytes
+	return nil
+}
+
+_benchmark_poly1305 :: proc(
+	options: ^time.Benchmark_Options,
+	allocator := context.allocator,
+) -> (
+	err: time.Benchmark_Error,
+) {
+	buf := options.input
+	key := [poly1305.KEY_SIZE]byte {
+		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
+		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
+		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
+		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
+	}
+
+	tag: [poly1305.TAG_SIZE]byte = ---
+	for _ in 0 ..= options.rounds {
+		poly1305.sum(tag[:], buf, key[:])
+	}
+	options.count = options.rounds
+	options.processed = options.rounds * options.bytes
+	//options.hash      = u128(h)
+	return nil
+}
+
+_benchmark_chacha20poly1305 :: proc(
+	options: ^time.Benchmark_Options,
+	allocator := context.allocator,
+) -> (
+	err: time.Benchmark_Error,
+) {
+	buf := options.input
+	key := [chacha20.KEY_SIZE]byte {
+		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
+		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
+		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
+		0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
+	}
+	nonce := [chacha20.NONCE_SIZE]byte {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00,
+	}
+
+	tag: [chacha20poly1305.TAG_SIZE]byte = ---
+
+	for _ in 0 ..= options.rounds {
+		chacha20poly1305.encrypt(buf, tag[:], key[:], nonce[:], nil, buf)
+	}
+	options.count = options.rounds
+	options.processed = options.rounds * options.bytes
+	return nil
+}
+
+benchmark_print :: proc(name: string, options: ^time.Benchmark_Options) {
+	fmt.printf(
+		"\t[%v] %v rounds, %v bytes processed in %v ns\n\t\t%5.3f rounds/s, %5.3f MiB/s\n",
+		name,
+		options.rounds,
+		options.processed,
+		time.duration_nanoseconds(options.duration),
+		options.rounds_per_second,
+		options.megabytes_per_second,
+	)
+}
+
+bench_chacha20 :: proc(t: ^testing.T) {
+	name := "ChaCha20 64 bytes"
+	options := &time.Benchmark_Options {
+		rounds = 1_000,
+		bytes = 64,
+		setup = _setup_sized_buf,
+		bench = _benchmark_chacha20,
+		teardown = _teardown_sized_buf,
+	}
+
+	err := time.benchmark(options, context.allocator)
+	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)
+	benchmark_print(name, options)
+
+	name = "ChaCha20 65536 bytes"
+	options.bytes = 65536
+	err = time.benchmark(options, context.allocator)
+	expect(t, err == nil, name)
+	benchmark_print(name, options)
+}
+
+bench_poly1305 :: proc(t: ^testing.T) {
+	name := "Poly1305 64 zero bytes"
+	options := &time.Benchmark_Options {
+		rounds = 1_000,
+		bytes = 64,
+		setup = _setup_sized_buf,
+		bench = _benchmark_poly1305,
+		teardown = _teardown_sized_buf,
+	}
+
+	err := time.benchmark(options, context.allocator)
+	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)
+	benchmark_print(name, options)
+}
+
+bench_chacha20poly1305 :: proc(t: ^testing.T) {
+	name := "chacha20poly1305 64 bytes"
+	options := &time.Benchmark_Options {
+		rounds = 1_000,
+		bytes = 64,
+		setup = _setup_sized_buf,
+		bench = _benchmark_chacha20poly1305,
+		teardown = _teardown_sized_buf,
+	}
+
+	err := time.benchmark(options, context.allocator)
+	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)
+	benchmark_print(name, options)
+
+	name = "chacha20poly1305 65536 bytes"
+	options.bytes = 65536
+	err = time.benchmark(options, context.allocator)
+	expect(t, err == nil, name)
+	benchmark_print(name, options)
+}
+
+bench_x25519 :: proc(t: ^testing.T) {
+	point_str := "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+	scalar_str := "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"
+
+	point, _ := hex.decode(transmute([]byte)(point_str), context.temp_allocator)
+	scalar, _ := hex.decode(transmute([]byte)(scalar_str), context.temp_allocator)
+	out: [x25519.POINT_SIZE]byte = ---
+
+	iters :: 10000
+	start := time.now()
+	for i := 0; i < iters; i = i + 1 {
+		x25519.scalarmult(out[:], scalar[:], point[:])
+	}
+	elapsed := time.since(start)
+
+	log(
+		t,
+		fmt.tprintf("x25519.scalarmult: ~%f us/op", time.duration_microseconds(elapsed) / iters),
+	)
+}