Skip to content

Commit 951c4e8

Browse files
committed
Support AES in OFB and CFB modes (legacy)
1 parent 644d53f commit 951c4e8

File tree

19 files changed

+578
-0
lines changed

19 files changed

+578
-0
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ abstract class GenerateProviderTestsTask : DefaultTask() {
9999
"AesCmacTestvectorsTest",
100100
"AesCtrTest",
101101
"AesCtrCompatibilityTest",
102+
"AesCfbTest",
103+
"AesCfbCompatibilityTest",
104+
"AesOfbTest",
105+
"AesOfbCompatibilityTest",
102106
"AesEcbCompatibilityTest",
103107
"AesGcmTest",
104108
"AesGcmCompatibilityTest",

cryptography-core/api/cryptography-core.api

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,18 @@ public abstract interface class dev/whyoleg/cryptography/algorithms/AES$CBC$Key
105105
public static synthetic fun cipher$default (Ldev/whyoleg/cryptography/algorithms/AES$CBC$Key;ZILjava/lang/Object;)Ldev/whyoleg/cryptography/algorithms/AES$IvCipher;
106106
}
107107

108+
public abstract interface class dev/whyoleg/cryptography/algorithms/AES$CFB : dev/whyoleg/cryptography/algorithms/AES {
109+
public static final field Companion Ldev/whyoleg/cryptography/algorithms/AES$CFB$Companion;
110+
public fun getId ()Ldev/whyoleg/cryptography/CryptographyAlgorithmId;
111+
}
112+
113+
public final class dev/whyoleg/cryptography/algorithms/AES$CFB$Companion : dev/whyoleg/cryptography/CryptographyAlgorithmId {
114+
}
115+
116+
public abstract interface class dev/whyoleg/cryptography/algorithms/AES$CFB$Key : dev/whyoleg/cryptography/algorithms/AES$Key {
117+
public abstract fun cipher ()Ldev/whyoleg/cryptography/algorithms/AES$IvCipher;
118+
}
119+
108120
public abstract interface class dev/whyoleg/cryptography/algorithms/AES$CMAC : dev/whyoleg/cryptography/algorithms/AES {
109121
public static final field Companion Ldev/whyoleg/cryptography/algorithms/AES$CMAC$Companion;
110122
public fun getId ()Ldev/whyoleg/cryptography/CryptographyAlgorithmId;
@@ -237,6 +249,18 @@ public final class dev/whyoleg/cryptography/algorithms/AES$Key$Size {
237249
public final fun getB256-XsYwlU8 ()I
238250
}
239251

252+
public abstract interface class dev/whyoleg/cryptography/algorithms/AES$OFB : dev/whyoleg/cryptography/algorithms/AES {
253+
public static final field Companion Ldev/whyoleg/cryptography/algorithms/AES$OFB$Companion;
254+
public fun getId ()Ldev/whyoleg/cryptography/CryptographyAlgorithmId;
255+
}
256+
257+
public final class dev/whyoleg/cryptography/algorithms/AES$OFB$Companion : dev/whyoleg/cryptography/CryptographyAlgorithmId {
258+
}
259+
260+
public abstract interface class dev/whyoleg/cryptography/algorithms/AES$OFB$Key : dev/whyoleg/cryptography/algorithms/AES$Key {
261+
public abstract fun cipher ()Ldev/whyoleg/cryptography/algorithms/AES$IvCipher;
262+
}
263+
240264
public abstract interface class dev/whyoleg/cryptography/algorithms/Digest : dev/whyoleg/cryptography/CryptographyAlgorithm {
241265
public abstract fun getId ()Ldev/whyoleg/cryptography/CryptographyAlgorithmId;
242266
public abstract fun hasher ()Ldev/whyoleg/cryptography/operations/Hasher;

cryptography-core/api/cryptography-core.klib.api

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ abstract interface <#A: dev.whyoleg.cryptography.algorithms/AES.Key> dev.whyoleg
3030
final object Companion : dev.whyoleg.cryptography/CryptographyAlgorithmId<dev.whyoleg.cryptography.algorithms/AES.CBC> // dev.whyoleg.cryptography.algorithms/AES.CBC.Companion|null[0]
3131
}
3232

33+
abstract interface CFB : dev.whyoleg.cryptography.algorithms/AES<dev.whyoleg.cryptography.algorithms/AES.CFB.Key> { // dev.whyoleg.cryptography.algorithms/AES.CFB|null[0]
34+
open val id // dev.whyoleg.cryptography.algorithms/AES.CFB.id|{}id[0]
35+
open fun <get-id>(): dev.whyoleg.cryptography/CryptographyAlgorithmId<dev.whyoleg.cryptography.algorithms/AES.CFB> // dev.whyoleg.cryptography.algorithms/AES.CFB.id.<get-id>|<get-id>(){}[0]
36+
37+
abstract interface Key : dev.whyoleg.cryptography.algorithms/AES.Key { // dev.whyoleg.cryptography.algorithms/AES.CFB.Key|null[0]
38+
abstract fun cipher(): dev.whyoleg.cryptography.algorithms/AES.IvCipher // dev.whyoleg.cryptography.algorithms/AES.CFB.Key.cipher|cipher(){}[0]
39+
}
40+
41+
final object Companion : dev.whyoleg.cryptography/CryptographyAlgorithmId<dev.whyoleg.cryptography.algorithms/AES.CFB> // dev.whyoleg.cryptography.algorithms/AES.CFB.Companion|null[0]
42+
}
43+
3344
abstract interface CMAC : dev.whyoleg.cryptography.algorithms/AES<dev.whyoleg.cryptography.algorithms/AES.CMAC.Key> { // dev.whyoleg.cryptography.algorithms/AES.CMAC|null[0]
3445
open val id // dev.whyoleg.cryptography.algorithms/AES.CMAC.id|{}id[0]
3546
open fun <get-id>(): dev.whyoleg.cryptography/CryptographyAlgorithmId<dev.whyoleg.cryptography.algorithms/AES.CMAC> // dev.whyoleg.cryptography.algorithms/AES.CMAC.id.<get-id>|<get-id>(){}[0]
@@ -156,6 +167,17 @@ abstract interface <#A: dev.whyoleg.cryptography.algorithms/AES.Key> dev.whyoleg
156167
final fun <get-B256>(): dev.whyoleg.cryptography/BinarySize // dev.whyoleg.cryptography.algorithms/AES.Key.Size.B256.<get-B256>|<get-B256>(){}[0]
157168
}
158169
}
170+
171+
abstract interface OFB : dev.whyoleg.cryptography.algorithms/AES<dev.whyoleg.cryptography.algorithms/AES.OFB.Key> { // dev.whyoleg.cryptography.algorithms/AES.OFB|null[0]
172+
open val id // dev.whyoleg.cryptography.algorithms/AES.OFB.id|{}id[0]
173+
open fun <get-id>(): dev.whyoleg.cryptography/CryptographyAlgorithmId<dev.whyoleg.cryptography.algorithms/AES.OFB> // dev.whyoleg.cryptography.algorithms/AES.OFB.id.<get-id>|<get-id>(){}[0]
174+
175+
abstract interface Key : dev.whyoleg.cryptography.algorithms/AES.Key { // dev.whyoleg.cryptography.algorithms/AES.OFB.Key|null[0]
176+
abstract fun cipher(): dev.whyoleg.cryptography.algorithms/AES.IvCipher // dev.whyoleg.cryptography.algorithms/AES.OFB.Key.cipher|cipher(){}[0]
177+
}
178+
179+
final object Companion : dev.whyoleg.cryptography/CryptographyAlgorithmId<dev.whyoleg.cryptography.algorithms/AES.OFB> // dev.whyoleg.cryptography.algorithms/AES.OFB.Companion|null[0]
180+
}
159181
}
160182

161183
abstract interface <#A: dev.whyoleg.cryptography.algorithms/EC.PublicKey, #B: dev.whyoleg.cryptography.algorithms/EC.PrivateKey, #C: dev.whyoleg.cryptography.algorithms/EC.KeyPair<#A, #B>> dev.whyoleg.cryptography.algorithms/EC : dev.whyoleg.cryptography/CryptographyAlgorithm { // dev.whyoleg.cryptography.algorithms/EC|null[0]

cryptography-core/src/commonMain/kotlin/algorithms/AES.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,32 @@ public interface AES<K : AES.Key> : CryptographyAlgorithm {
8585
}
8686
}
8787

88+
@DelicateCryptographyApi
89+
@SubclassOptInRequired(CryptographyProviderApi::class)
90+
public interface CFB : AES<CFB.Key> {
91+
override val id: CryptographyAlgorithmId<CFB> get() = Companion
92+
93+
public companion object : CryptographyAlgorithmId<CFB>("AES-CFB")
94+
95+
@SubclassOptInRequired(CryptographyProviderApi::class)
96+
public interface Key : AES.Key {
97+
public fun cipher(): IvCipher
98+
}
99+
}
100+
101+
@DelicateCryptographyApi
102+
@SubclassOptInRequired(CryptographyProviderApi::class)
103+
public interface OFB : AES<OFB.Key> {
104+
override val id: CryptographyAlgorithmId<OFB> get() = Companion
105+
106+
public companion object : CryptographyAlgorithmId<OFB>("AES-OFB")
107+
108+
@SubclassOptInRequired(CryptographyProviderApi::class)
109+
public interface Key : AES.Key {
110+
public fun cipher(): IvCipher
111+
}
112+
}
113+
88114
@SubclassOptInRequired(CryptographyProviderApi::class)
89115
public interface GCM : AES<GCM.Key> {
90116
override val id: CryptographyAlgorithmId<GCM> get() = Companion

cryptography-providers/apple/src/commonMain/kotlin/AppleCryptographyProvider.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ internal object AppleCryptographyProvider : CryptographyProvider() {
2727
HMAC -> CCHmac
2828
AES.CBC -> CCAesCbc
2929
AES.CTR -> CCAesCtr
30+
AES.CFB -> CCAesCfb
31+
AES.OFB -> CCAesOfb
3032
AES.ECB -> CCAesEcb
3133
RSA.PSS -> SecRsaPss
3234
RSA.PKCS1 -> SecRsaPkcs1
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2025 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.algorithms.*
8+
import kotlinx.cinterop.*
9+
import platform.CoreCrypto.*
10+
11+
internal object CCAesCfb : CCAes<AES.CFB.Key>(), AES.CFB {
12+
override fun wrapKey(key: ByteArray): AES.CFB.Key = AesCfbKey(key)
13+
14+
private class AesCfbKey(private val key: ByteArray) : AES.CFB.Key {
15+
override fun cipher(): AES.IvCipher = CCAesIvCipher(
16+
algorithm = kCCAlgorithmAES,
17+
mode = kCCModeCFB,
18+
padding = 0.convert(), // not applicable
19+
key = key,
20+
ivSize = 16
21+
)
22+
23+
override fun encodeToByteArrayBlocking(format: AES.Key.Format): ByteArray = when (format) {
24+
AES.Key.Format.RAW -> key.copyOf()
25+
AES.Key.Format.JWK -> error("JWK is not supported")
26+
}
27+
}
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2025 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.algorithms.*
8+
import kotlinx.cinterop.*
9+
import platform.CoreCrypto.*
10+
11+
internal object CCAesOfb : CCAes<AES.OFB.Key>(), AES.OFB {
12+
override fun wrapKey(key: ByteArray): AES.OFB.Key = AesOfbKey(key)
13+
14+
private class AesOfbKey(private val key: ByteArray) : AES.OFB.Key {
15+
override fun cipher(): AES.IvCipher = CCAesIvCipher(
16+
algorithm = kCCAlgorithmAES,
17+
mode = kCCModeOFB,
18+
padding = 0.convert(), // not applicable
19+
key = key,
20+
ivSize = 16
21+
)
22+
23+
override fun encodeToByteArrayBlocking(format: AES.Key.Format): ByteArray = when (format) {
24+
AES.Key.Format.RAW -> key.copyOf()
25+
AES.Key.Format.JWK -> error("JWK is not supported")
26+
}
27+
}
28+
}

cryptography-providers/jdk/src/jvmMain/kotlin/JdkCryptographyProvider.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ internal class JdkCryptographyProvider(provider: Provider?) : CryptographyProvid
125125
AES.CBC -> JdkAesCbc(state)
126126
AES.CMAC -> JdkAesCmac(state)
127127
AES.CTR -> JdkAesCtr(state)
128+
AES.CFB -> JdkAesCfb(state)
129+
AES.OFB -> JdkAesOfb(state)
128130
AES.ECB -> JdkAesEcb(state)
129131
AES.GCM -> JdkAesGcm(state)
130132
RSA.OAEP -> JdkRsaOaep(state)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2025 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package dev.whyoleg.cryptography.providers.jdk.algorithms
6+
7+
import dev.whyoleg.cryptography.*
8+
import dev.whyoleg.cryptography.algorithms.*
9+
import dev.whyoleg.cryptography.materials.key.*
10+
import dev.whyoleg.cryptography.providers.jdk.*
11+
import dev.whyoleg.cryptography.providers.jdk.materials.*
12+
13+
internal class JdkAesCfb(
14+
private val state: JdkCryptographyState,
15+
) : AES.CFB {
16+
private val keyWrapper: (JSecretKey) -> AES.CFB.Key = { key -> JdkAesCfbKey(state, key) }
17+
private val keyDecoder = JdkSecretKeyDecoder<AES.Key.Format, _>("AES", keyWrapper)
18+
19+
override fun keyDecoder(): KeyDecoder<AES.Key.Format, AES.CFB.Key> = keyDecoder
20+
override fun keyGenerator(keySize: BinarySize): KeyGenerator<AES.CFB.Key> = JdkSecretKeyGenerator(state, "AES", keyWrapper) {
21+
init(keySize.inBits, state.secureRandom)
22+
}
23+
}
24+
25+
private class JdkAesCfbKey(
26+
private val state: JdkCryptographyState,
27+
private val key: JSecretKey,
28+
) : AES.CFB.Key, JdkEncodableKey<AES.Key.Format>(key) {
29+
override fun cipher(): AES.IvCipher = JdkAesIvCipher(
30+
state = state,
31+
key = key,
32+
ivSize = 16,
33+
algorithm = "AES/CFB/NoPadding"
34+
)
35+
36+
override fun encodeToByteArrayBlocking(format: AES.Key.Format): ByteArray = when (format) {
37+
AES.Key.Format.JWK -> error("$format is not supported")
38+
AES.Key.Format.RAW -> encodeToRaw()
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2025 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package dev.whyoleg.cryptography.providers.jdk.algorithms
6+
7+
import dev.whyoleg.cryptography.*
8+
import dev.whyoleg.cryptography.algorithms.*
9+
import dev.whyoleg.cryptography.materials.key.*
10+
import dev.whyoleg.cryptography.providers.jdk.*
11+
import dev.whyoleg.cryptography.providers.jdk.materials.*
12+
13+
internal class JdkAesOfb(
14+
private val state: JdkCryptographyState,
15+
) : AES.OFB {
16+
private val keyWrapper: (JSecretKey) -> AES.OFB.Key = { key -> JdkAesOfbKey(state, key) }
17+
private val keyDecoder = JdkSecretKeyDecoder<AES.Key.Format, _>("AES", keyWrapper)
18+
19+
override fun keyDecoder(): KeyDecoder<AES.Key.Format, AES.OFB.Key> = keyDecoder
20+
override fun keyGenerator(keySize: BinarySize): KeyGenerator<AES.OFB.Key> = JdkSecretKeyGenerator(state, "AES", keyWrapper) {
21+
init(keySize.inBits, state.secureRandom)
22+
}
23+
}
24+
25+
private class JdkAesOfbKey(
26+
private val state: JdkCryptographyState,
27+
private val key: JSecretKey,
28+
) : AES.OFB.Key, JdkEncodableKey<AES.Key.Format>(key) {
29+
override fun cipher(): AES.IvCipher = JdkAesIvCipher(
30+
state = state,
31+
key = key,
32+
ivSize = 16,
33+
algorithm = "AES/OFB/NoPadding"
34+
)
35+
36+
override fun encodeToByteArrayBlocking(format: AES.Key.Format): ByteArray = when (format) {
37+
AES.Key.Format.JWK -> error("$format is not supported")
38+
AES.Key.Format.RAW -> encodeToRaw()
39+
}
40+
}

0 commit comments

Comments
 (0)