Skip to content

Commit 2f90d46

Browse files
committed
Add tests for ciphers
1 parent 86b345d commit 2f90d46

File tree

8 files changed

+170
-2
lines changed

8 files changed

+170
-2
lines changed

build-logic/src/main/kotlin/ckbuild/tests/GenerateProviderTestsTask.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ abstract class GenerateProviderTestsTask : DefaultTask() {
8787

8888
"AesCbcTest",
8989
"AesCbcCompatibilityTest",
90+
"AesCtrTest",
9091
"AesCtrCompatibilityTest",
9192
"AesEcbCompatibilityTest",
9293
"AesGcmTest",

cryptography-providers-tests-api/src/commonMain/kotlin/utils.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ fun digest(name: String): CryptographyAlgorithmId<Digest> = when (name) {
6767

6868
fun Buffer(bytes: ByteString): Buffer = Buffer().apply { write(bytes) }
6969

70+
fun Buffer.bufferedSource(): Source = (this as RawSource).buffered()
71+
fun Buffer.bufferedSink(): Sink = (this as RawSink).buffered()
72+
7073
expect fun disableJsConsoleDebug()
7174

7275
// Wasm tests on browser cannot be filtered: https://youtrack.jetbrains.com/issue/KT-58291

cryptography-providers-tests/src/commonMain/kotlin/default/AesBasedTest.kt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ package dev.whyoleg.cryptography.providers.tests.default
77
import dev.whyoleg.cryptography.*
88
import dev.whyoleg.cryptography.algorithms.*
99
import dev.whyoleg.cryptography.providers.tests.api.*
10+
import dev.whyoleg.cryptography.random.*
11+
import kotlinx.io.*
12+
import kotlinx.io.bytestring.*
1013

1114
abstract class AesBasedTest<A : AES<*>>(
1215
private val algorithmId: CryptographyAlgorithmId<A>,
1316
provider: CryptographyProvider,
14-
) : ProviderTest(provider) {
17+
) : ProviderTest(provider), CipherTest {
1518

1619
protected inner class AesTestScope(
1720
logger: TestLogger,
@@ -28,4 +31,40 @@ abstract class AesBasedTest<A : AES<*>>(
2831
block(AesTestScope(logger, context, provider, algorithm, keySize))
2932
}
3033
}
34+
35+
suspend fun AlgorithmTestScope<*>.assertCipherWithIvViaFunction(
36+
encryptor: AES.IvEncryptor,
37+
decryptor: AES.IvDecryptor,
38+
ivSize: Int,
39+
plaintext: ByteString,
40+
) {
41+
val iv = ByteString(CryptographyRandom.nextBytes(ivSize))
42+
listOf(
43+
encryptor.resetIv(context).encryptWithIv(iv, plaintext),
44+
encryptor.resetIv(context).encryptingSourceWithIv(iv, Buffer(plaintext).bufferedSource()).buffered()
45+
.use { it.readByteString() },
46+
Buffer().also { output ->
47+
encryptor.resetIv(context).encryptingSinkWithIv(iv, output.bufferedSink()).buffered().use { it.write(plaintext) }
48+
}.readByteString(),
49+
).forEach { ciphertext ->
50+
assertContentEquals(plaintext, decryptor.decryptWithIv(iv, ciphertext))
51+
assertContentEquals(
52+
plaintext,
53+
decryptor.decryptingSourceWithIv(iv, Buffer(ciphertext).bufferedSource()).buffered().use { it.readByteString() }
54+
)
55+
assertContentEquals(
56+
plaintext,
57+
Buffer().also { output ->
58+
decryptor.decryptingSinkWithIv(iv, output.bufferedSink()).buffered().use { it.write(ciphertext) }
59+
}.readByteString()
60+
)
61+
}
62+
}
63+
}
64+
65+
// GCM mode on JDK has a check which tries to prevent reuse of the same IV with the same key.
66+
// we need to set random IV first to be able to reuse IV for different plaintext for the same key
67+
private suspend fun AES.IvEncryptor.resetIv(context: TestContext): AES.IvEncryptor {
68+
if (context.provider.isJdk && this is AES.IvAuthenticatedEncryptor) encrypt(ByteString())
69+
return this
3170
}

cryptography-providers-tests/src/commonMain/kotlin/default/AesCbcTest.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import dev.whyoleg.cryptography.*
88
import dev.whyoleg.cryptography.algorithms.*
99
import dev.whyoleg.cryptography.providers.tests.api.*
1010
import dev.whyoleg.cryptography.random.*
11+
import kotlinx.io.bytestring.*
1112
import kotlin.test.*
1213

1314
private const val blockSize = 16
@@ -90,4 +91,26 @@ abstract class AesCbcTest(provider: CryptographyProvider) : AesBasedTest<AES.CBC
9091
assertNotEquals(data, it)
9192
}
9293
}
94+
95+
@Test
96+
fun testFunctions() = runTestForEachKeySize {
97+
val key = algorithm.keyGenerator(keySize).generateKey()
98+
val cipher = key.cipher()
99+
repeat(10) {
100+
val size = CryptographyRandom.nextInt(20000)
101+
val data = ByteString(CryptographyRandom.nextBytes(size))
102+
assertCipherViaFunction(cipher, cipher, data)
103+
}
104+
}
105+
106+
@Test
107+
fun testFunctionsWithIv() = runTestForEachKeySize {
108+
val key = algorithm.keyGenerator(keySize).generateKey()
109+
val cipher = key.cipher()
110+
repeat(10) {
111+
val size = CryptographyRandom.nextInt(20000)
112+
val data = ByteString(CryptographyRandom.nextBytes(size))
113+
assertCipherWithIvViaFunction(cipher, cipher, ivSize, data)
114+
}
115+
}
93116
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2024 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package dev.whyoleg.cryptography.providers.tests.default
6+
7+
import dev.whyoleg.cryptography.*
8+
import dev.whyoleg.cryptography.algorithms.*
9+
import dev.whyoleg.cryptography.random.*
10+
import kotlinx.io.bytestring.*
11+
import kotlin.test.*
12+
13+
private const val ivSize = 16
14+
15+
abstract class AesCtrTest(provider: CryptographyProvider) : AesBasedTest<AES.CTR>(AES.CTR, provider) {
16+
@Test
17+
fun testFunctions() = runTestForEachKeySize {
18+
val key = algorithm.keyGenerator(keySize).generateKey()
19+
val cipher = key.cipher()
20+
repeat(10) {
21+
val size = CryptographyRandom.nextInt(20000)
22+
val data = ByteString(CryptographyRandom.nextBytes(size))
23+
assertCipherViaFunction(cipher, cipher, data)
24+
}
25+
}
26+
27+
@Test
28+
fun testFunctionsWithIv() = runTestForEachKeySize {
29+
val key = algorithm.keyGenerator(keySize).generateKey()
30+
val cipher = key.cipher()
31+
repeat(10) {
32+
val size = CryptographyRandom.nextInt(20000)
33+
val data = ByteString(CryptographyRandom.nextBytes(size))
34+
assertCipherWithIvViaFunction(cipher, cipher, ivSize, data)
35+
}
36+
}
37+
}

cryptography-providers-tests/src/commonMain/kotlin/default/AesGcmTest.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import dev.whyoleg.cryptography.*
88
import dev.whyoleg.cryptography.BinarySize.Companion.bits
99
import dev.whyoleg.cryptography.algorithms.*
1010
import dev.whyoleg.cryptography.random.*
11+
import kotlinx.io.bytestring.*
1112
import kotlin.test.*
1213

1314
private const val ivSize = 12
@@ -51,4 +52,30 @@ abstract class AesGcmTest(provider: CryptographyProvider) : AesBasedTest<AES.GCM
5152

5253
assertFails { wrongKey.cipher().decrypt(ciphertext) }
5354
}
55+
56+
@Test
57+
fun testFunctions() = runTestForEachKeySize {
58+
val key = algorithm.keyGenerator(keySize).generateKey()
59+
listOf(96, 104, 112, 120, 128).forEach { tagSizeBits ->
60+
val cipher = key.cipher(tagSizeBits.bits)
61+
repeat(10) {
62+
val size = CryptographyRandom.nextInt(20000)
63+
val data = ByteString(CryptographyRandom.nextBytes(size))
64+
assertCipherViaFunction(cipher, cipher, data)
65+
}
66+
}
67+
}
68+
69+
@Test
70+
fun testFunctionsWithIv() = runTestForEachKeySize {
71+
val key = algorithm.keyGenerator(keySize).generateKey()
72+
listOf(96, 104, 112, 120, 128).forEach { tagSizeBits ->
73+
val cipher = key.cipher(tagSizeBits.bits)
74+
repeat(10) {
75+
val size = CryptographyRandom.nextInt(20000)
76+
val data = ByteString(CryptographyRandom.nextBytes(size))
77+
assertCipherWithIvViaFunction(cipher, cipher, ivSize, data)
78+
}
79+
}
80+
}
5481
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2024 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package dev.whyoleg.cryptography.providers.tests.default
6+
7+
import dev.whyoleg.cryptography.operations.*
8+
import dev.whyoleg.cryptography.providers.tests.api.*
9+
import kotlinx.io.*
10+
import kotlinx.io.bytestring.*
11+
12+
interface CipherTest {
13+
suspend fun AlgorithmTestScope<*>.assertCipherViaFunction(
14+
encryptor: Encryptor,
15+
decryptor: Decryptor,
16+
plaintext: ByteString,
17+
) {
18+
listOf(
19+
encryptor.encrypt(plaintext),
20+
encryptor.encryptingSource(Buffer(plaintext).bufferedSource()).buffered().use { it.readByteString() },
21+
Buffer().also { output ->
22+
encryptor.encryptingSink(output.bufferedSink()).buffered().use { it.write(plaintext) }
23+
}.readByteString(),
24+
).forEach { ciphertext ->
25+
assertContentEquals(plaintext, decryptor.decrypt(ciphertext))
26+
assertContentEquals(
27+
plaintext,
28+
decryptor.decryptingSource(Buffer(ciphertext).bufferedSource()).buffered().use { it.readByteString() }
29+
)
30+
assertContentEquals(
31+
plaintext,
32+
Buffer().also { output ->
33+
decryptor.decryptingSink(output.bufferedSink()).buffered().use { it.write(ciphertext) }
34+
}.readByteString()
35+
)
36+
}
37+
}
38+
}

cryptography-providers-tests/src/commonMain/kotlin/default/SignatureTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ interface SignatureTest {
2626
}
2727
}
2828
val viaSource: UpdateFunction. () -> Unit = {
29-
update((Buffer(data) as RawSource).buffered())
29+
update(Buffer(data).bufferedSource())
3030
}
3131
val viaSink: UpdateFunction. () -> Unit = {
3232
updatingSink(discardingSink()).buffered().use { it.write(data) }

0 commit comments

Comments
 (0)