package com.peachpasswords.vault import org.bouncycastle.crypto.digests.Blake3Digest import org.bouncycastle.crypto.generators.Argon2BytesGenerator import org.bouncycastle.crypto.params.Argon2Parameters import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Assert.assertThrows import org.junit.Test class OfficialCryptoVectorsTest { @Test fun rfc_9106_argon2id_vector_matches_bouncy_castle() { val parameters = Argon2Parameters.Builder(Argon2Parameters.ARGON2_id) .withVersion(Argon2Parameters.ARGON2_VERSION_13) .withMemoryAsKB(32) .withIterations(3) .withParallelism(4) .withSalt(ByteArray(16) { 0x02 }) .withSecret(ByteArray(8) { 0x03 }) .withAdditional(ByteArray(12) { 0x04 }) .build() val generator = Argon2BytesGenerator().apply { init(parameters) } val output = ByteArray(32) generator.generateBytes(ByteArray(32) { 0x01 }, output) assertEquals( "0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659", output.toHex(), ) } @Test fun production_argon2id_profile_matches_browser_hash_wasm() { assertEquals( "df187c8d241e8f3fd42810e1379660605aad1119dcb133d8ff916f87160d6a33", VaultCrypto.deriveKey("testpassword", ByteArray(16) { 0xab.toByte() }).toHex(), ) } @Test fun nist_aes_256_gcm_cavp_vector_decrypts_and_rejects_bad_tag() { val key = "4c8ebfe1444ec1b2d503c6986659af2c94fafe945f72c1e8486a5acfedb8a0f8".hexBytes() val iv = "473360e0ad24889959858995".hexBytes() val ciphertext = "d2c78110ac7e8f107c0df0570bd7c90c".hexBytes() val tag = "c26a379b6d98ef2852ead8ce83a833a7".hexBytes() val packed = iv + ciphertext + tag assertEquals( "7789b41cb3ee548814ca0b388c10b343", VaultCrypto.decrypt(packed, key, ByteArray(0)).toHex(), ) val tampered = packed.copyOf().also { it[it.lastIndex] = (it.last().toInt() xor 0x01).toByte() } assertThrows(Throwable::class.java) { VaultCrypto.decrypt(tampered, key, ByteArray(0)) } } @Test fun blake3_reference_vector_matches_bouncy_castle_across_chunk_boundary() { val input = ByteArray(1024) { (it % 251).toByte() } val digest = Blake3Digest(256) digest.update(input, 0, input.size) val output = ByteArray(32) digest.doFinal(output, 0) assertArrayEquals( "42214739f095a406f3fc83deb889744ac00df831c10daa55189b5d121c855af7".hexBytes(), output, ) } private fun String.hexBytes(): ByteArray = chunked(2).map { it.toInt(16).toByte() }.toByteArray() private fun ByteArray.toHex(): String = joinToString("") { "%02x".format(it.toInt() and 0xff) } }