Skip to content

Commit 1a2339c

Browse files
committed
cryptokit: implement EdDSA/XDH; add SwiftEd; wire provider; decode unwrap-to-RAW+decodeRaw; encode hoists raw; rename *Key; use ObjectIdentifier OIDs
1 parent 1d0f98f commit 1a2339c

File tree

4 files changed

+394
-0
lines changed

4 files changed

+394
-0
lines changed

cryptography-providers/cryptokit/src/commonMain/kotlin/CryptoKitCryptographyProvider.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ internal object CryptoKitCryptographyProvider : CryptographyProvider() {
3030
AES.GCM -> CryptoKitAesGcm
3131
ECDSA -> CryptoKitEcdsa
3232
ECDH -> CryptoKitEcdh
33+
EdDSA -> CryptoKitEdDSA
34+
XDH -> CryptoKitXDH
3335
else -> null
3436
} as A?
3537
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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.cryptokit.algorithms
6+
7+
import dev.whyoleg.cryptography.algorithms.*
8+
import dev.whyoleg.cryptography.materials.key.*
9+
import dev.whyoleg.cryptography.operations.*
10+
import dev.whyoleg.cryptography.providers.cryptokit.internal.*
11+
import dev.whyoleg.cryptography.providers.cryptokit.internal.swiftinterop.*
12+
import dev.whyoleg.cryptography.providers.base.*
13+
import dev.whyoleg.cryptography.providers.base.materials.*
14+
import dev.whyoleg.cryptography.providers.base.operations.*
15+
import dev.whyoleg.cryptography.serialization.asn1.*
16+
import dev.whyoleg.cryptography.serialization.asn1.modules.*
17+
import dev.whyoleg.cryptography.serialization.pem.*
18+
import kotlinx.cinterop.*
19+
import platform.Foundation.*
20+
21+
internal object CryptoKitEdDSA : EdDSA {
22+
override fun publicKeyDecoder(curve: EdDSA.Curve): KeyDecoder<EdDSA.PublicKey.Format, EdDSA.PublicKey> {
23+
check(curve == EdDSA.Curve.Ed25519) { "CryptoKit supports Ed25519 only" }
24+
return object : KeyDecoder<EdDSA.PublicKey.Format, EdDSA.PublicKey> {
25+
override fun decodeFromByteArrayBlocking(format: EdDSA.PublicKey.Format, bytes: ByteArray): EdDSA.PublicKey = when (format) {
26+
EdDSA.PublicKey.Format.RAW -> EdPublicKey(
27+
swiftTry<SwiftEdDsaPublicKey> { error -> bytes.useNSData { SwiftEdDsaPublicKey.decodeRawWithRawRepresentation(it, error) } }
28+
)
29+
EdDSA.PublicKey.Format.DER -> {
30+
val raw = unwrapSubjectPublicKeyInfo(ObjectIdentifier.Ed25519, bytes)
31+
EdPublicKey(swiftTry { error -> raw.useNSData { SwiftEdDsaPublicKey.decodeRawWithRawRepresentation(it, error) } })
32+
}
33+
EdDSA.PublicKey.Format.PEM -> {
34+
val der = unwrapPem(PemLabel.PublicKey, bytes)
35+
val raw = unwrapSubjectPublicKeyInfo(ObjectIdentifier.Ed25519, der)
36+
EdPublicKey(swiftTry { error -> raw.useNSData { SwiftEdDsaPublicKey.decodeRawWithRawRepresentation(it, error) } })
37+
}
38+
else -> error("$format is not supported by CryptoKit EdDSA")
39+
}
40+
}
41+
}
42+
43+
override fun privateKeyDecoder(curve: EdDSA.Curve): KeyDecoder<EdDSA.PrivateKey.Format, EdDSA.PrivateKey> {
44+
check(curve == EdDSA.Curve.Ed25519) { "CryptoKit supports Ed25519 only" }
45+
return object : KeyDecoder<EdDSA.PrivateKey.Format, EdDSA.PrivateKey> {
46+
override fun decodeFromByteArrayBlocking(format: EdDSA.PrivateKey.Format, bytes: ByteArray): EdDSA.PrivateKey = when (format) {
47+
EdDSA.PrivateKey.Format.RAW -> EdPrivateKey(
48+
swiftTry<SwiftEdDsaPrivateKey> { error -> bytes.useNSData { SwiftEdDsaPrivateKey.decodeRawWithRawRepresentation(it, error) } }
49+
)
50+
EdDSA.PrivateKey.Format.DER -> {
51+
val raw = unwrapPrivateKeyInfo(ObjectIdentifier.Ed25519, bytes)
52+
EdPrivateKey(swiftTry { error -> raw.useNSData { SwiftEdDsaPrivateKey.decodeRawWithRawRepresentation(it, error) } })
53+
}
54+
EdDSA.PrivateKey.Format.PEM -> {
55+
val der = unwrapPem(PemLabel.PrivateKey, bytes)
56+
val raw = unwrapPrivateKeyInfo(ObjectIdentifier.Ed25519, der)
57+
EdPrivateKey(swiftTry { error -> raw.useNSData { SwiftEdDsaPrivateKey.decodeRawWithRawRepresentation(it, error) } })
58+
}
59+
else -> error("$format is not supported by CryptoKit EdDSA")
60+
}
61+
}
62+
}
63+
64+
override fun keyPairGenerator(curve: EdDSA.Curve): KeyGenerator<EdDSA.KeyPair> {
65+
check(curve == EdDSA.Curve.Ed25519) { "CryptoKit supports Ed25519 only" }
66+
return object : KeyGenerator<EdDSA.KeyPair> {
67+
override fun generateKeyBlocking(): EdDSA.KeyPair {
68+
val p = SwiftEdDsaPrivateKey.generate()
69+
return EdKeyPair(EdPublicKey(p.publicKey()), EdPrivateKey(p))
70+
}
71+
}
72+
}
73+
74+
private class EdKeyPair(
75+
override val publicKey: EdDSA.PublicKey,
76+
override val privateKey: EdDSA.PrivateKey,
77+
) : EdDSA.KeyPair
78+
79+
private class EdPublicKey(
80+
val key: SwiftEdDsaPublicKey,
81+
) : EdDSA.PublicKey {
82+
override fun encodeToByteArrayBlocking(format: EdDSA.PublicKey.Format): ByteArray = when (format) {
83+
EdDSA.PublicKey.Format.RAW -> {
84+
val raw = key.rawRepresentation().toByteArray()
85+
raw
86+
}
87+
EdDSA.PublicKey.Format.DER -> {
88+
val raw = key.rawRepresentation().toByteArray()
89+
wrapSubjectPublicKeyInfo(
90+
UnknownKeyAlgorithmIdentifier(ObjectIdentifier.Ed25519),
91+
raw
92+
)
93+
}
94+
EdDSA.PublicKey.Format.PEM -> {
95+
val raw = key.rawRepresentation().toByteArray()
96+
wrapPem(
97+
PemLabel.PublicKey,
98+
wrapSubjectPublicKeyInfo(
99+
UnknownKeyAlgorithmIdentifier(ObjectIdentifier.Ed25519),
100+
raw
101+
)
102+
)
103+
}
104+
else -> error("$format is not supported by CryptoKit EdDSA")
105+
}
106+
107+
override fun signatureVerifier(): SignatureVerifier = object : SignatureVerifier {
108+
override fun createVerifyFunction(): VerifyFunction =
109+
AccumulatingVerifyFunction { data, signature, startIndex, endIndex ->
110+
val sig = signature.copyOfRange(startIndex, endIndex)
111+
val ok = data.useNSData { dataNs -> sig.useNSData { sigNs ->
112+
key.verifyWithSignature(sigNs, message = dataNs)
113+
} } as Boolean
114+
ok
115+
}
116+
}
117+
}
118+
119+
private class EdPrivateKey(
120+
val key: SwiftEdDsaPrivateKey,
121+
) : EdDSA.PrivateKey {
122+
override fun encodeToByteArrayBlocking(format: EdDSA.PrivateKey.Format): ByteArray = when (format) {
123+
EdDSA.PrivateKey.Format.RAW -> {
124+
val raw = key.rawRepresentation().toByteArray()
125+
raw
126+
}
127+
EdDSA.PrivateKey.Format.DER -> {
128+
val raw = key.rawRepresentation().toByteArray()
129+
wrapPrivateKeyInfo(
130+
0,
131+
UnknownKeyAlgorithmIdentifier(ObjectIdentifier.Ed25519),
132+
raw
133+
)
134+
}
135+
EdDSA.PrivateKey.Format.PEM -> {
136+
val raw = key.rawRepresentation().toByteArray()
137+
wrapPem(
138+
PemLabel.PrivateKey,
139+
wrapPrivateKeyInfo(
140+
0,
141+
UnknownKeyAlgorithmIdentifier(ObjectIdentifier.Ed25519),
142+
raw
143+
)
144+
)
145+
}
146+
else -> error("$format is not supported by CryptoKit EdDSA")
147+
}
148+
149+
override fun signatureGenerator(): SignatureGenerator = object : SignatureGenerator {
150+
override fun createSignFunction(): SignFunction =
151+
AccumulatingSignFunction { data ->
152+
swiftTry { error -> data.useNSData { key.signWithMessage(it, error) } }.toByteArray()
153+
}
154+
override fun generateSignatureBlocking(data: ByteArray): ByteArray =
155+
swiftTry { error -> data.useNSData { key.signWithMessage(it, error) } }.toByteArray()
156+
}
157+
}
158+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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.cryptokit.algorithms
6+
7+
import dev.whyoleg.cryptography.algorithms.*
8+
import dev.whyoleg.cryptography.materials.key.*
9+
import dev.whyoleg.cryptography.operations.*
10+
import dev.whyoleg.cryptography.providers.cryptokit.internal.*
11+
import dev.whyoleg.cryptography.providers.cryptokit.internal.swiftinterop.*
12+
import dev.whyoleg.cryptography.providers.base.*
13+
import dev.whyoleg.cryptography.providers.base.materials.*
14+
import dev.whyoleg.cryptography.serialization.asn1.*
15+
import dev.whyoleg.cryptography.serialization.asn1.modules.*
16+
import dev.whyoleg.cryptography.serialization.pem.*
17+
import kotlinx.cinterop.*
18+
import platform.Foundation.*
19+
20+
internal object CryptoKitXDH : XDH {
21+
override fun publicKeyDecoder(curve: XDH.Curve): KeyDecoder<XDH.PublicKey.Format, XDH.PublicKey> {
22+
check(curve == XDH.Curve.X25519) { "CryptoKit supports X25519 only" }
23+
return object : KeyDecoder<XDH.PublicKey.Format, XDH.PublicKey> {
24+
override fun decodeFromByteArrayBlocking(format: XDH.PublicKey.Format, bytes: ByteArray): XDH.PublicKey = when (format) {
25+
XDH.PublicKey.Format.RAW -> XPublicKey(
26+
swiftTry<SwiftXdhPublicKey> { error -> bytes.useNSData { SwiftXdhPublicKey.decodeRawWithRawRepresentation(it, error) } }
27+
)
28+
XDH.PublicKey.Format.DER -> {
29+
val raw = unwrapSubjectPublicKeyInfo(ObjectIdentifier.X25519, bytes)
30+
XPublicKey(swiftTry { error -> raw.useNSData { SwiftXdhPublicKey.decodeRawWithRawRepresentation(it, error) } })
31+
}
32+
XDH.PublicKey.Format.PEM -> {
33+
val der = unwrapPem(PemLabel.PublicKey, bytes)
34+
val raw = unwrapSubjectPublicKeyInfo(ObjectIdentifier.X25519, der)
35+
XPublicKey(swiftTry { error -> raw.useNSData { SwiftXdhPublicKey.decodeRawWithRawRepresentation(it, error) } })
36+
}
37+
else -> error("$format is not supported by CryptoKit XDH")
38+
}
39+
}
40+
}
41+
42+
override fun privateKeyDecoder(curve: XDH.Curve): KeyDecoder<XDH.PrivateKey.Format, XDH.PrivateKey> {
43+
check(curve == XDH.Curve.X25519) { "CryptoKit supports X25519 only" }
44+
return object : KeyDecoder<XDH.PrivateKey.Format, XDH.PrivateKey> {
45+
override fun decodeFromByteArrayBlocking(format: XDH.PrivateKey.Format, bytes: ByteArray): XDH.PrivateKey = when (format) {
46+
XDH.PrivateKey.Format.RAW -> XPrivate(
47+
swiftTry<SwiftXdhPrivateKey> { error -> bytes.useNSData { SwiftXdhPrivateKey.decodeRawWithRawRepresentation(it, error) } }
48+
)
49+
XDH.PrivateKey.Format.DER -> {
50+
val raw = unwrapPrivateKeyInfo(ObjectIdentifier.X25519, bytes)
51+
XPrivate(swiftTry { error -> raw.useNSData { SwiftXdhPrivateKey.decodeRawWithRawRepresentation(it, error) } })
52+
}
53+
XDH.PrivateKey.Format.PEM -> {
54+
val der = unwrapPem(PemLabel.PrivateKey, bytes)
55+
val raw = unwrapPrivateKeyInfo(ObjectIdentifier.X25519, der)
56+
XPrivate(swiftTry { error -> raw.useNSData { SwiftXdhPrivateKey.decodeRawWithRawRepresentation(it, error) } })
57+
}
58+
else -> error("$format is not supported by CryptoKit XDH")
59+
}
60+
}
61+
}
62+
63+
override fun keyPairGenerator(curve: XDH.Curve): KeyGenerator<XDH.KeyPair> {
64+
check(curve == XDH.Curve.X25519) { "CryptoKit supports X25519 only" }
65+
return object : KeyGenerator<XDH.KeyPair> {
66+
override fun generateKeyBlocking(): XDH.KeyPair {
67+
val p = SwiftXdhPrivateKey.generate()
68+
return XKeyPair(XPublicKey(p.publicKey()), XPrivateKey(p))
69+
}
70+
}
71+
}
72+
73+
private class XKeyPair(
74+
override val publicKey: XDH.PublicKey,
75+
override val privateKey: XDH.PrivateKey,
76+
) : XDH.KeyPair
77+
78+
private class XPublicKey(
79+
val key: SwiftXdhPublicKey,
80+
) : XDH.PublicKey, SharedSecretGenerator<XDH.PrivateKey> {
81+
override fun encodeToByteArrayBlocking(format: XDH.PublicKey.Format): ByteArray = when (format) {
82+
XDH.PublicKey.Format.RAW -> {
83+
val raw = key.rawRepresentation().toByteArray()
84+
raw
85+
}
86+
XDH.PublicKey.Format.DER -> {
87+
val raw = key.rawRepresentation().toByteArray()
88+
wrapSubjectPublicKeyInfo(
89+
UnknownKeyAlgorithmIdentifier(ObjectIdentifier.X25519),
90+
raw
91+
)
92+
}
93+
XDH.PublicKey.Format.PEM -> {
94+
val raw = key.rawRepresentation().toByteArray()
95+
wrapPem(
96+
PemLabel.PublicKey,
97+
wrapSubjectPublicKeyInfo(
98+
UnknownKeyAlgorithmIdentifier(ObjectIdentifier.X25519),
99+
raw
100+
)
101+
)
102+
}
103+
else -> error("$format is not supported by CryptoKit XDH")
104+
}
105+
override fun sharedSecretGenerator(): SharedSecretGenerator<XDH.PrivateKey> = this
106+
override fun generateSharedSecretToByteArrayBlocking(other: XDH.PrivateKey): ByteArray {
107+
require(other is XPrivateKey)
108+
return swiftTry { error -> other.key.deriveSecretWith(key, error) }.toByteArray()
109+
}
110+
}
111+
112+
private class XPrivateKey(
113+
val key: SwiftXdhPrivateKey,
114+
) : XDH.PrivateKey, SharedSecretGenerator<XDH.PublicKey> {
115+
override fun encodeToByteArrayBlocking(format: XDH.PrivateKey.Format): ByteArray = when (format) {
116+
XDH.PrivateKey.Format.RAW -> {
117+
val raw = key.rawRepresentation().toByteArray()
118+
raw
119+
}
120+
XDH.PrivateKey.Format.DER -> {
121+
val raw = key.rawRepresentation().toByteArray()
122+
wrapPrivateKeyInfo(
123+
0,
124+
UnknownKeyAlgorithmIdentifier(ObjectIdentifier.X25519),
125+
raw
126+
)
127+
}
128+
XDH.PrivateKey.Format.PEM -> {
129+
val raw = key.rawRepresentation().toByteArray()
130+
wrapPem(
131+
PemLabel.PrivateKey,
132+
wrapPrivateKeyInfo(
133+
0,
134+
UnknownKeyAlgorithmIdentifier(ObjectIdentifier.X25519),
135+
raw
136+
)
137+
)
138+
}
139+
else -> error("$format is not supported by CryptoKit XDH")
140+
}
141+
override fun sharedSecretGenerator(): SharedSecretGenerator<XDH.PublicKey> = this
142+
override fun generateSharedSecretToByteArrayBlocking(other: XDH.PublicKey): ByteArray {
143+
require(other is XPublicKey)
144+
return swiftTry { error -> key.deriveSecretWith(other.key, error) }.toByteArray()
145+
}
146+
}
147+
}

0 commit comments

Comments
 (0)