Skip to content

Commit e061c9c

Browse files
committed
Support encryption/decryption streaming in Apple provider
1 parent 1ae9aca commit e061c9c

File tree

16 files changed

+301
-206
lines changed

16 files changed

+301
-206
lines changed

cryptography-providers/apple/src/commonMain/kotlin/algorithms/CCAesCbc.kt

Lines changed: 10 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,63 +5,25 @@
55
package dev.whyoleg.cryptography.providers.apple.algorithms
66

77
import dev.whyoleg.cryptography.algorithms.*
8-
import dev.whyoleg.cryptography.providers.apple.internal.*
9-
import dev.whyoleg.cryptography.random.*
108
import platform.CoreCrypto.*
119

1210
internal object CCAesCbc : CCAes<AES.CBC.Key>(), AES.CBC {
1311
override fun wrapKey(key: ByteArray): AES.CBC.Key = AesCbcKey(key)
1412

1513
private class AesCbcKey(private val key: ByteArray) : AES.CBC.Key {
16-
override fun cipher(padding: Boolean): AES.IvCipher = AesCbcCipher(key, padding)
14+
override fun cipher(padding: Boolean): AES.IvCipher = CCAesIvCipher(
15+
algorithm = kCCAlgorithmAES,
16+
mode = kCCModeCBC,
17+
padding = if (padding) ccPKCS7Padding else ccNoPadding,
18+
key = key,
19+
ivSize = 16
20+
) {
21+
require(it % kCCBlockSizeAES128.toInt() == 0) { "Ciphertext is not padded" }
22+
}
23+
1724
override fun encodeToByteArrayBlocking(format: AES.Key.Format): ByteArray = when (format) {
1825
AES.Key.Format.RAW -> key.copyOf()
1926
AES.Key.Format.JWK -> error("JWK is not supported")
2027
}
2128
}
2229
}
23-
24-
private const val ivSizeBytes = 16 //bytes for CBC
25-
private const val blockSizeBytes = 16 //bytes for CBC
26-
27-
private class AesCbcCipher(key: ByteArray, padding: Boolean) : AES.IvCipher {
28-
private val cipher = CCCipher(
29-
algorithm = kCCAlgorithmAES,
30-
mode = kCCModeCBC,
31-
padding = if (padding) ccPKCS7Padding else ccNoPadding,
32-
key = key
33-
)
34-
35-
override fun encryptBlocking(plaintext: ByteArray): ByteArray {
36-
val iv = CryptographyRandom.nextBytes(ivSizeBytes)
37-
return iv + encryptWithIvBlocking(iv, plaintext)
38-
}
39-
40-
override fun encryptWithIvBlocking(iv: ByteArray, plaintext: ByteArray): ByteArray {
41-
require(iv.size == ivSizeBytes) { "IV size is wrong" }
42-
43-
return cipher.encrypt(iv, plaintext)
44-
}
45-
46-
override fun decryptBlocking(ciphertext: ByteArray): ByteArray {
47-
require(ciphertext.size >= ivSizeBytes) { "Ciphertext is too short" }
48-
require(ciphertext.size % blockSizeBytes == 0) { "Ciphertext is not padded" }
49-
50-
return cipher.decrypt(
51-
iv = ciphertext,
52-
ciphertext = ciphertext,
53-
ciphertextStartIndex = ivSizeBytes
54-
)
55-
}
56-
57-
override fun decryptWithIvBlocking(iv: ByteArray, ciphertext: ByteArray): ByteArray {
58-
require(iv.size == ivSizeBytes) { "IV size is wrong" }
59-
require(ciphertext.size % blockSizeBytes == 0) { "Ciphertext is not padded" }
60-
61-
return cipher.decrypt(
62-
iv = iv,
63-
ciphertext = ciphertext,
64-
ciphertextStartIndex = 0
65-
)
66-
}
67-
}

cryptography-providers/apple/src/commonMain/kotlin/algorithms/CCAesCtr.kt

Lines changed: 8 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,61 +5,24 @@
55
package dev.whyoleg.cryptography.providers.apple.algorithms
66

77
import dev.whyoleg.cryptography.algorithms.*
8-
import dev.whyoleg.cryptography.providers.apple.internal.*
9-
import dev.whyoleg.cryptography.random.*
108
import kotlinx.cinterop.*
119
import platform.CoreCrypto.*
1210

1311
internal object CCAesCtr : CCAes<AES.CTR.Key>(), AES.CTR {
1412
override fun wrapKey(key: ByteArray): AES.CTR.Key = AesCtrKey(key)
1513

1614
private class AesCtrKey(private val key: ByteArray) : AES.CTR.Key {
17-
override fun cipher(): AES.IvCipher = AesCtrCipher(key)
15+
override fun cipher(): AES.IvCipher = CCAesIvCipher(
16+
algorithm = kCCAlgorithmAES,
17+
mode = kCCModeCTR,
18+
padding = 0.convert(), // not applicable
19+
key = key,
20+
ivSize = 16
21+
)
22+
1823
override fun encodeToByteArrayBlocking(format: AES.Key.Format): ByteArray = when (format) {
1924
AES.Key.Format.RAW -> key.copyOf()
2025
AES.Key.Format.JWK -> error("JWK is not supported")
2126
}
2227
}
2328
}
24-
25-
private const val ivSizeBytes = 16 //bytes for CTR
26-
27-
private class AesCtrCipher(key: ByteArray) : AES.IvCipher {
28-
private val cipher = CCCipher(
29-
algorithm = kCCAlgorithmAES,
30-
mode = kCCModeCTR,
31-
padding = 0.convert(), // not applicable
32-
key = key
33-
)
34-
35-
override fun encryptBlocking(plaintext: ByteArray): ByteArray {
36-
val iv = CryptographyRandom.nextBytes(ivSizeBytes)
37-
return iv + encryptWithIvBlocking(iv, plaintext)
38-
}
39-
40-
override fun encryptWithIvBlocking(iv: ByteArray, plaintext: ByteArray): ByteArray {
41-
require(iv.size == ivSizeBytes) { "IV size is wrong" }
42-
43-
return cipher.encrypt(iv, plaintext)
44-
}
45-
46-
override fun decryptBlocking(ciphertext: ByteArray): ByteArray {
47-
require(ciphertext.size >= ivSizeBytes) { "Ciphertext is too short" }
48-
49-
return cipher.decrypt(
50-
iv = ciphertext,
51-
ciphertext = ciphertext,
52-
ciphertextStartIndex = ivSizeBytes
53-
)
54-
}
55-
56-
override fun decryptWithIvBlocking(iv: ByteArray, ciphertext: ByteArray): ByteArray {
57-
require(iv.size == ivSizeBytes) { "IV size is wrong" }
58-
59-
return cipher.decrypt(
60-
iv = iv,
61-
ciphertext = ciphertext,
62-
ciphertextStartIndex = 0
63-
)
64-
}
65-
}

cryptography-providers/apple/src/commonMain/kotlin/algorithms/CCAesEcb.kt

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,50 @@ package dev.whyoleg.cryptography.providers.apple.algorithms
77
import dev.whyoleg.cryptography.algorithms.*
88
import dev.whyoleg.cryptography.operations.*
99
import dev.whyoleg.cryptography.providers.apple.internal.*
10+
import dev.whyoleg.cryptography.providers.base.operations.*
1011
import platform.CoreCrypto.*
1112

1213
internal object CCAesEcb : CCAes<AES.ECB.Key>(), AES.ECB {
1314
override fun wrapKey(key: ByteArray): AES.ECB.Key = AesEcbKey(key)
1415

1516
private class AesEcbKey(private val key: ByteArray) : AES.ECB.Key {
16-
override fun cipher(padding: Boolean): Cipher = AesEcbCipher(key, padding)
17+
override fun cipher(padding: Boolean): Cipher = CCAesEcbCipher(key, padding)
1718
override fun encodeToByteArrayBlocking(format: AES.Key.Format): ByteArray = when (format) {
1819
AES.Key.Format.RAW -> key.copyOf()
1920
AES.Key.Format.JWK -> error("JWK is not supported")
2021
}
2122
}
2223
}
2324

24-
private const val blockSizeBytes = 16 //bytes for ECB
25-
26-
private class AesEcbCipher(key: ByteArray, padding: Boolean) : Cipher {
27-
private val cipher = CCCipher(
28-
algorithm = kCCAlgorithmAES,
29-
mode = kCCModeECB,
30-
padding = if (padding) ccPKCS7Padding else ccNoPadding,
31-
key = key
32-
)
33-
34-
override fun encryptBlocking(plaintext: ByteArray): ByteArray {
35-
return cipher.encrypt(null, plaintext)
25+
private class CCAesEcbCipher(
26+
private val key: ByteArray,
27+
private val padding: Boolean,
28+
) : BaseCipher {
29+
override fun createEncryptFunction(): CipherFunction {
30+
return CCCipherFunction(
31+
algorithm = kCCAlgorithmAES,
32+
mode = kCCModeECB,
33+
padding = if (padding) ccPKCS7Padding else ccNoPadding,
34+
operation = kCCEncrypt,
35+
blockSize = kCCBlockSizeAES128.toInt(),
36+
key = key,
37+
iv = null,
38+
ivStartIndex = 0
39+
)
3640
}
3741

38-
override fun decryptBlocking(ciphertext: ByteArray): ByteArray {
39-
require(ciphertext.size % blockSizeBytes == 0) { "Ciphertext is not padded" }
40-
41-
return cipher.decrypt(
42+
override fun createDecryptFunction(): CipherFunction {
43+
return CCCipherFunction(
44+
algorithm = kCCAlgorithmAES,
45+
mode = kCCModeECB,
46+
padding = if (padding) ccPKCS7Padding else ccNoPadding,
47+
operation = kCCDecrypt,
48+
blockSize = kCCBlockSizeAES128.toInt(),
49+
key = key,
4250
iv = null,
43-
ciphertext = ciphertext,
44-
ciphertextStartIndex = 0
45-
)
51+
ivStartIndex = 0
52+
) {
53+
require(it % kCCBlockSizeAES128.toInt() == 0) { "Ciphertext is not padded" }
54+
}
4655
}
4756
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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.apple.algorithms
6+
7+
import dev.whyoleg.cryptography.providers.apple.internal.*
8+
import dev.whyoleg.cryptography.providers.base.algorithms.*
9+
import dev.whyoleg.cryptography.providers.base.operations.*
10+
import dev.whyoleg.cryptography.random.*
11+
import platform.CoreCrypto.*
12+
13+
internal class CCAesIvCipher(
14+
private val algorithm: CCAlgorithm,
15+
private val mode: CCMode,
16+
private val padding: CCPadding,
17+
private val key: ByteArray,
18+
private val ivSize: Int,
19+
private val validateCiphertextInputSize: (Int) -> Unit = {},
20+
) : BaseAesIvCipher {
21+
override fun createEncryptFunction(): CipherFunction {
22+
val iv = CryptographyRandom.nextBytes(ivSize)
23+
return BaseAesImplicitIvEncryptFunction(iv, createEncryptFunctionWithIv(iv))
24+
}
25+
26+
override fun createDecryptFunction(): CipherFunction {
27+
return BaseAesImplicitIvDecryptFunction(ivSize, ::createDecryptFunctionWithIv)
28+
}
29+
30+
override fun createEncryptFunctionWithIv(iv: ByteArray): CipherFunction {
31+
require(iv.size == ivSize) { "IV size is wrong" }
32+
33+
return CCCipherFunction(
34+
algorithm = algorithm,
35+
mode = mode,
36+
padding = padding,
37+
operation = kCCEncrypt,
38+
blockSize = kCCBlockSizeAES128.toInt(),
39+
key = key,
40+
iv = iv,
41+
ivStartIndex = 0
42+
)
43+
}
44+
45+
private fun createDecryptFunctionWithIv(iv: ByteArray, startIndex: Int): CipherFunction {
46+
require(iv.size - startIndex >= ivSize) { "IV size is wrong" }
47+
48+
return CCCipherFunction(
49+
algorithm = algorithm,
50+
mode = mode,
51+
padding = padding,
52+
operation = kCCDecrypt,
53+
blockSize = kCCBlockSizeAES128.toInt(),
54+
key = key,
55+
iv = iv,
56+
ivStartIndex = startIndex,
57+
validateFullInputSize = validateCiphertextInputSize
58+
)
59+
}
60+
61+
override fun createDecryptFunctionWithIv(iv: ByteArray): CipherFunction {
62+
return createDecryptFunctionWithIv(iv, 0)
63+
}
64+
}

cryptography-providers/apple/src/commonMain/kotlin/algorithms/CCDigest.kt

Lines changed: 1 addition & 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.operations.*
1010
import dev.whyoleg.cryptography.providers.apple.internal.*
11+
import dev.whyoleg.cryptography.providers.base.*
1112
import kotlinx.cinterop.*
1213

1314
internal class CCDigest<CTX : CPointed>(

cryptography-providers/apple/src/commonMain/kotlin/algorithms/CCHmac.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import dev.whyoleg.cryptography.algorithms.*
99
import dev.whyoleg.cryptography.materials.key.*
1010
import dev.whyoleg.cryptography.operations.*
1111
import dev.whyoleg.cryptography.providers.apple.internal.*
12+
import dev.whyoleg.cryptography.providers.base.*
1213
import dev.whyoleg.cryptography.random.*
1314
import kotlinx.cinterop.*
1415
import platform.CoreCrypto.*

cryptography-providers/apple/src/commonMain/kotlin/algorithms/SecEcdsa.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dev.whyoleg.cryptography.bigint.*
1010
import dev.whyoleg.cryptography.materials.key.*
1111
import dev.whyoleg.cryptography.operations.*
1212
import dev.whyoleg.cryptography.providers.apple.internal.*
13+
import dev.whyoleg.cryptography.providers.base.*
1314
import dev.whyoleg.cryptography.serialization.asn1.*
1415
import dev.whyoleg.cryptography.serialization.asn1.modules.*
1516
import dev.whyoleg.cryptography.serialization.pem.*

cryptography-providers/apple/src/commonMain/kotlin/algorithms/SecRsaOaep.kt

Lines changed: 7 additions & 6 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.operations.*
1010
import dev.whyoleg.cryptography.providers.apple.internal.*
11+
import dev.whyoleg.cryptography.providers.base.operations.*
1112
import platform.Security.*
1213

1314
internal object SecRsaOaep : SecRsa<RSA.OAEP.PublicKey, RSA.OAEP.PrivateKey, RSA.OAEP.KeyPair>(), RSA.OAEP {
@@ -41,18 +42,18 @@ internal object SecRsaOaep : SecRsa<RSA.OAEP.PublicKey, RSA.OAEP.PrivateKey, RSA
4142
}
4243
}
4344

44-
private class RsaOaepEncryptor(private val publicKey: SecKeyRef, private val algorithm: SecKeyAlgorithm?) : AuthenticatedEncryptor {
45-
override fun encryptBlocking(plaintext: ByteArray, associatedData: ByteArray?): ByteArray {
45+
private class RsaOaepEncryptor(private val publicKey: SecKeyRef, private val algorithm: SecKeyAlgorithm?) : BaseAuthenticatedEncryptor {
46+
override fun createEncryptFunction(associatedData: ByteArray?): CipherFunction {
4647
require(associatedData == null) { "Associated data inclusion is not supported" }
4748

48-
return secEncrypt(publicKey, algorithm, plaintext)
49+
return SecCipherFunction(publicKey, algorithm, ::SecKeyCreateEncryptedData)
4950
}
5051
}
5152

52-
private class RsaOaepDecryptor(private val privateKey: SecKeyRef, private val algorithm: SecKeyAlgorithm?) : AuthenticatedDecryptor {
53-
override fun decryptBlocking(ciphertext: ByteArray, associatedData: ByteArray?): ByteArray {
53+
private class RsaOaepDecryptor(private val privateKey: SecKeyRef, private val algorithm: SecKeyAlgorithm?) : BaseAuthenticatedDecryptor {
54+
override fun createDecryptFunction(associatedData: ByteArray?): CipherFunction {
5455
require(associatedData == null) { "Associated data inclusion is not supported" }
5556

56-
return secDecrypt(privateKey, algorithm, ciphertext)
57+
return SecCipherFunction(privateKey, algorithm, ::SecKeyCreateDecryptedData)
5758
}
5859
}

cryptography-providers/apple/src/commonMain/kotlin/algorithms/SecRsaPkcs1.kt

Lines changed: 7 additions & 6 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.operations.*
1010
import dev.whyoleg.cryptography.providers.apple.internal.*
11+
import dev.whyoleg.cryptography.providers.base.operations.*
1112
import platform.Security.*
1213

1314
internal object SecRsaPkcs1 : SecRsa<RSA.PKCS1.PublicKey, RSA.PKCS1.PrivateKey, RSA.PKCS1.KeyPair>(), RSA.PKCS1 {
@@ -43,14 +44,14 @@ internal object SecRsaPkcs1 : SecRsa<RSA.PKCS1.PublicKey, RSA.PKCS1.PrivateKey,
4344
}
4445
}
4546

46-
private class RsaPkcs1Encryptor(private val publicKey: SecKeyRef) : Encryptor {
47-
override fun encryptBlocking(plaintext: ByteArray): ByteArray {
48-
return secEncrypt(publicKey, kSecKeyAlgorithmRSAEncryptionPKCS1, plaintext)
47+
private class RsaPkcs1Encryptor(private val publicKey: SecKeyRef) : BaseEncryptor {
48+
override fun createEncryptFunction(): CipherFunction {
49+
return SecCipherFunction(publicKey, kSecKeyAlgorithmRSAEncryptionPKCS1, ::SecKeyCreateEncryptedData)
4950
}
5051
}
5152

52-
private class RsaPkcs1Decryptor(private val privateKey: SecKeyRef) : Decryptor {
53-
override fun decryptBlocking(ciphertext: ByteArray): ByteArray {
54-
return secDecrypt(privateKey, kSecKeyAlgorithmRSAEncryptionPKCS1, ciphertext)
53+
private class RsaPkcs1Decryptor(private val privateKey: SecKeyRef) : BaseDecryptor {
54+
override fun createDecryptFunction(): CipherFunction {
55+
return SecCipherFunction(privateKey, kSecKeyAlgorithmRSAEncryptionPKCS1, ::SecKeyCreateDecryptedData)
5556
}
5657
}

cryptography-providers/apple/src/commonMain/kotlin/algorithms/SecRsaRaw.kt

Lines changed: 7 additions & 6 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.operations.*
1010
import dev.whyoleg.cryptography.providers.apple.internal.*
11+
import dev.whyoleg.cryptography.providers.base.operations.*
1112
import platform.Security.*
1213

1314
internal object SecRsaRaw : SecRsa<RSA.RAW.PublicKey, RSA.RAW.PrivateKey, RSA.RAW.KeyPair>(), RSA.RAW {
@@ -35,14 +36,14 @@ internal object SecRsaRaw : SecRsa<RSA.RAW.PublicKey, RSA.RAW.PrivateKey, RSA.RA
3536
}
3637
}
3738

38-
private class RsaRawEncryptor(private val publicKey: SecKeyRef) : Encryptor {
39-
override fun encryptBlocking(plaintext: ByteArray): ByteArray {
40-
return secEncrypt(publicKey, kSecKeyAlgorithmRSAEncryptionRaw, plaintext)
39+
private class RsaRawEncryptor(private val publicKey: SecKeyRef) : BaseEncryptor {
40+
override fun createEncryptFunction(): CipherFunction {
41+
return SecCipherFunction(publicKey, kSecKeyAlgorithmRSAEncryptionRaw, ::SecKeyCreateEncryptedData)
4142
}
4243
}
4344

44-
private class RsaRawDecryptor(private val privateKey: SecKeyRef) : Decryptor {
45-
override fun decryptBlocking(ciphertext: ByteArray): ByteArray {
46-
return secDecrypt(privateKey, kSecKeyAlgorithmRSAEncryptionRaw, ciphertext)
45+
private class RsaRawDecryptor(private val privateKey: SecKeyRef) : BaseDecryptor {
46+
override fun createDecryptFunction(): CipherFunction {
47+
return SecCipherFunction(privateKey, kSecKeyAlgorithmRSAEncryptionRaw, ::SecKeyCreateDecryptedData)
4748
}
4849
}

0 commit comments

Comments
 (0)