Skip to content

Commit

Permalink
Use BinaryData for key encoding/decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
whyoleg committed Aug 29, 2024
1 parent 8f1a56c commit 81491c8
Show file tree
Hide file tree
Showing 52 changed files with 468 additions and 308 deletions.
8 changes: 6 additions & 2 deletions cryptography-core/api/cryptography-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -584,18 +584,22 @@ public final class dev/whyoleg/cryptography/algorithms/symmetric/SymmetricKeySiz
}

public abstract interface class dev/whyoleg/cryptography/materials/key/EncodableKey : dev/whyoleg/cryptography/materials/key/Key {
public abstract fun encode-QjUYYGE (Ldev/whyoleg/cryptography/materials/key/KeyFormat;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun encodeBlocking-FbFv9R8 (Ldev/whyoleg/cryptography/materials/key/KeyFormat;)[B
public fun encodeTo (Ldev/whyoleg/cryptography/materials/key/KeyFormat;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun encodeTo$suspendImpl (Ldev/whyoleg/cryptography/materials/key/EncodableKey;Ldev/whyoleg/cryptography/materials/key/KeyFormat;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun encodeToBlocking (Ldev/whyoleg/cryptography/materials/key/KeyFormat;)[B
public fun encodeToBlocking (Ldev/whyoleg/cryptography/materials/key/KeyFormat;)[B
}

public abstract interface class dev/whyoleg/cryptography/materials/key/Key {
}

public abstract interface class dev/whyoleg/cryptography/materials/key/KeyDecoder {
public abstract fun decode-qBWNn0k (Ldev/whyoleg/cryptography/materials/key/KeyFormat;[BLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun decodeBlocking-0C8fO8g (Ldev/whyoleg/cryptography/materials/key/KeyFormat;[B)Ldev/whyoleg/cryptography/materials/key/Key;
public fun decodeFrom (Ldev/whyoleg/cryptography/materials/key/KeyFormat;[BLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun decodeFrom$suspendImpl (Ldev/whyoleg/cryptography/materials/key/KeyDecoder;Ldev/whyoleg/cryptography/materials/key/KeyFormat;[BLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun decodeFromBlocking (Ldev/whyoleg/cryptography/materials/key/KeyFormat;[B)Ldev/whyoleg/cryptography/materials/key/Key;
public fun decodeFromBlocking (Ldev/whyoleg/cryptography/materials/key/KeyFormat;[B)Ldev/whyoleg/cryptography/materials/key/Key;
}

public abstract interface class dev/whyoleg/cryptography/materials/key/KeyFormat {
Expand Down
8 changes: 6 additions & 2 deletions cryptography-core/api/cryptography-core.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -405,12 +405,16 @@ abstract interface <#A: dev.whyoleg.cryptography.materials.key/Key> dev.whyoleg.
}

abstract interface <#A: dev.whyoleg.cryptography.materials.key/KeyFormat, #B: dev.whyoleg.cryptography.materials.key/Key> dev.whyoleg.cryptography.materials.key/KeyDecoder { // dev.whyoleg.cryptography.materials.key/KeyDecoder|null[0]
abstract fun decodeFromBlocking(#A, kotlin/ByteArray): #B // dev.whyoleg.cryptography.materials.key/KeyDecoder.decodeFromBlocking|decodeFromBlocking(1:0;kotlin.ByteArray){}[0]
abstract fun decodeBlocking(#A, dev.whyoleg.cryptography.binary/BinaryData): #B // dev.whyoleg.cryptography.materials.key/KeyDecoder.decodeBlocking|decodeBlocking(1:0;dev.whyoleg.cryptography.binary.BinaryData){}[0]
abstract suspend fun decode(#A, dev.whyoleg.cryptography.binary/BinaryData): #B // dev.whyoleg.cryptography.materials.key/KeyDecoder.decode|decode(1:0;dev.whyoleg.cryptography.binary.BinaryData){}[0]
open fun decodeFromBlocking(#A, kotlin/ByteArray): #B // dev.whyoleg.cryptography.materials.key/KeyDecoder.decodeFromBlocking|decodeFromBlocking(1:0;kotlin.ByteArray){}[0]
open suspend fun decodeFrom(#A, kotlin/ByteArray): #B // dev.whyoleg.cryptography.materials.key/KeyDecoder.decodeFrom|decodeFrom(1:0;kotlin.ByteArray){}[0]
}

abstract interface <#A: dev.whyoleg.cryptography.materials.key/KeyFormat> dev.whyoleg.cryptography.materials.key/EncodableKey : dev.whyoleg.cryptography.materials.key/Key { // dev.whyoleg.cryptography.materials.key/EncodableKey|null[0]
abstract fun encodeToBlocking(#A): kotlin/ByteArray // dev.whyoleg.cryptography.materials.key/EncodableKey.encodeToBlocking|encodeToBlocking(1:0){}[0]
abstract fun encodeBlocking(#A): dev.whyoleg.cryptography.binary/BinaryData // dev.whyoleg.cryptography.materials.key/EncodableKey.encodeBlocking|encodeBlocking(1:0){}[0]
abstract suspend fun encode(#A): dev.whyoleg.cryptography.binary/BinaryData // dev.whyoleg.cryptography.materials.key/EncodableKey.encode|encode(1:0){}[0]
open fun encodeToBlocking(#A): kotlin/ByteArray // dev.whyoleg.cryptography.materials.key/EncodableKey.encodeToBlocking|encodeToBlocking(1:0){}[0]
open suspend fun encodeTo(#A): kotlin/ByteArray // dev.whyoleg.cryptography.materials.key/EncodableKey.encodeTo|encodeTo(1:0){}[0]
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
/*
* Copyright (c) 2023 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
* Copyright (c) 2023-2024 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.whyoleg.cryptography.materials.key

import dev.whyoleg.cryptography.*
import dev.whyoleg.cryptography.binary.*

@SubclassOptInRequired(CryptographyProviderApi::class)
public interface EncodableKey<KF : KeyFormat> : Key {
public suspend fun encodeTo(format: KF): ByteArray = encodeToBlocking(format)
public fun encodeToBlocking(format: KF): ByteArray
public suspend fun encode(format: KF): BinaryData
public fun encodeBlocking(format: KF): BinaryData

@Deprecated(
"Replaced with encode",
ReplaceWith("encode(format).toByteArray()"),
level = DeprecationLevel.ERROR,
)
public suspend fun encodeTo(format: KF): ByteArray = encode(format).toByteArray()

@Deprecated(
"Replaced with encodeBlocking",
ReplaceWith("encodeBlocking(format).toByteArray()"),
level = DeprecationLevel.ERROR,
)
public fun encodeToBlocking(format: KF): ByteArray = encodeBlocking(format).toByteArray()
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,30 @@
package dev.whyoleg.cryptography.materials.key

import dev.whyoleg.cryptography.*
import dev.whyoleg.cryptography.binary.*

@SubclassOptInRequired(CryptographyProviderApi::class)
public interface KeyDecoder<KF : KeyFormat, K : Key> {
public suspend fun decodeFrom(format: KF, data: ByteArray): K = decodeFromBlocking(format, data)
public fun decodeFromBlocking(format: KF, data: ByteArray): K
public suspend fun decode(format: KF, data: BinaryData): K
public fun decodeBlocking(format: KF, data: BinaryData): K

@Deprecated(
"Replaced with decode",
ReplaceWith(
"decode(format, BinaryData.fromByteArray(data))",
"dev.whyoleg.cryptography.binary.BinaryData"
),
level = DeprecationLevel.ERROR,
)
public suspend fun decodeFrom(format: KF, data: ByteArray): K = decode(format, BinaryData.fromByteArray(data))

@Deprecated(
"Replaced with decodeBlocking",
ReplaceWith(
"decodeBlocking(format, BinaryData.fromByteArray(data))",
"dev.whyoleg.cryptography.binary.BinaryData"
),
level = DeprecationLevel.ERROR,
)
public fun decodeFromBlocking(format: KF, data: ByteArray): K = decodeBlocking(format, BinaryData.fromByteArray(data))
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package dev.whyoleg.cryptography.providers.tests.api

import dev.whyoleg.cryptography.*
import dev.whyoleg.cryptography.algorithms.digest.*
import dev.whyoleg.cryptography.binary.*

import dev.whyoleg.cryptography.materials.key.*
import kotlin.test.*
Expand All @@ -14,7 +15,7 @@ suspend fun <KF : KeyFormat> EncodableKey<KF>.encodeTo(
formats: Collection<KF>,
supports: (KF) -> Boolean,
): Map<String, ByteArray> = formats.filter(supports).associate {
it.name to encodeTo(it)
it.name to encode(it).toByteArray()
}.also {
assertTrue(it.isNotEmpty(), "No supported formats")
}
Expand All @@ -31,7 +32,7 @@ suspend inline fun <KF : KeyFormat, K : EncodableKey<KF>> KeyDecoder<KF, K>.deco
.filterKeys(supports)

val keys = supportedFormats.mapNotNull {
if (supportsDecoding(it.key, it.value)) decodeFrom(it.key, it.value) else null
if (supportsDecoding(it.key, it.value)) decode(it.key, BinaryData.fromByteArray(it.value)) else null
}

keys.forEach { key ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ abstract class EcCompatibilityTest<PublicK : EC.PublicKey, PrivateK : EC.Private
EC.PublicKey.Format.DER,
EC.PublicKey.Format.PEM,
-> {
assertContentEquals(bytes, key.encodeTo(format), "Public Key $format encoding")
assertContentEquals(bytes, key.encode(format).toByteArray(), "Public Key $format encoding")
}
}
}
Expand All @@ -100,25 +100,25 @@ abstract class EcCompatibilityTest<PublicK : EC.PublicKey, PrivateK : EC.Private
when (format) {
EC.PrivateKey.Format.JWK -> {}
EC.PrivateKey.Format.RAW -> {
assertContentEquals(bytes, key.encodeTo(format))
assertContentEquals(bytes, key.encode(format).toByteArray())
}
EC.PrivateKey.Format.DER.SEC1 -> {
assertEcPrivateKeyEquals(bytes, key.encodeTo(format))
assertEcPrivateKeyEquals(bytes, key.encode(format).toByteArray())
}
EC.PrivateKey.Format.PEM.SEC1 -> {
val expected = Pem.decode(bytes)
val actual = Pem.decode(key.encodeTo(format))
val actual = Pem.decode(key.encode(format).toByteArray())

assertEquals(expected.label, actual.label)

assertEcPrivateKeyEquals(expected.bytes, actual.bytes)
}
EC.PrivateKey.Format.DER -> {
assertPkcs8EcPrivateKeyEquals(bytes, key.encodeTo(format))
assertPkcs8EcPrivateKeyEquals(bytes, key.encode(format).toByteArray())
}
EC.PrivateKey.Format.PEM -> {
val expected = Pem.decode(bytes)
val actual = Pem.decode(key.encodeTo(format))
val actual = Pem.decode(key.encode(format).toByteArray())

assertEquals(expected.label, actual.label)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ abstract class EcdsaTest(provider: CryptographyProvider) : ProviderTest(provider

val keyPair = algorithm.keyPairGenerator(curve).generateKey()

assertEquals(publicKeySize, keyPair.publicKey.encodeTo(EC.PublicKey.Format.DER).size)
assertContains(privateKeySizes, keyPair.privateKey.encodeTo(EC.PrivateKey.Format.DER).size)
assertEquals(publicKeySize, keyPair.publicKey.encode(EC.PublicKey.Format.DER).toByteArray().size)
assertContains(privateKeySizes, keyPair.privateKey.encode(EC.PrivateKey.Format.DER).toByteArray().size)

generateDigests { digest, _ ->
if (!supportsDigest(digest)) return@generateDigests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ abstract class RsaBasedCompatibilityTest<PublicK : RSA.PublicKey, PrivateK : RSA
RSA.PublicKey.Format.DER.PKCS1,
RSA.PublicKey.Format.PEM.PKCS1,
->
assertContentEquals(bytes, key.encodeTo(format), "Public Key $format encoding")
assertContentEquals(bytes, key.encode(format).toByteArray(), "Public Key $format encoding")
RSA.PublicKey.Format.JWK -> {}

}
Expand All @@ -112,7 +112,7 @@ abstract class RsaBasedCompatibilityTest<PublicK : RSA.PublicKey, PrivateK : RSA
RSA.PrivateKey.Format.DER.PKCS1,
RSA.PrivateKey.Format.PEM.PKCS1,
->
assertContentEquals(bytes, key.encodeTo(format), "Private Key $format encoding")
assertContentEquals(bytes, key.encode(format).toByteArray(), "Private Key $format encoding")
RSA.PrivateKey.Format.JWK -> {}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ abstract class RsaOaepTest(provider: CryptographyProvider) : ProviderTest(provid
val keyPair = algorithm.keyPairGenerator(keySize, digest).generateKey()

if (supportsKeyFormat(RSA.PublicKey.Format.DER)) {
assertEquals(keySize.inBytes + 38, keyPair.publicKey.encodeTo(RSA.PublicKey.Format.DER).size)
assertEquals(keySize.inBytes + 38, keyPair.publicKey.encode(RSA.PublicKey.Format.DER).toByteArray().size)
}

val maxSize = keySize.inBytes - 2 - 2 * digestSize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ abstract class RsaPkcs1Test(provider: CryptographyProvider) : ProviderTest(provi
val keyPair = algorithm.keyPairGenerator(keySize, digest).generateKey()

if (supportsKeyFormat(RSA.PublicKey.Format.DER)) {
assertEquals(keySize.inBytes + 38, keyPair.publicKey.encodeTo(RSA.PublicKey.Format.DER).size)
assertEquals(keySize.inBytes + 38, keyPair.publicKey.encode(RSA.PublicKey.Format.DER).toByteArray().size)
}

val signatureGenerator = keyPair.privateKey.signatureGenerator()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ abstract class RsaPssTest(provider: CryptographyProvider) : ProviderTest(provide
val keyPair = algorithm.keyPairGenerator(keySize, digest).generateKey()

if (supportsKeyFormat(RSA.PublicKey.Format.DER)) {
assertEquals(keySize.inBytes + 38, keyPair.publicKey.encodeTo(RSA.PublicKey.Format.DER).size)
assertEquals(keySize.inBytes + 38, keyPair.publicKey.encode(RSA.PublicKey.Format.DER).toByteArray().size)
}

val maxSaltSize = (ceil((keySize.inBits - 1) / 8.0) - digestSize - 2).toInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ abstract class AesBasedCompatibilityTest<K : AES.Key, A : AES<K>>(
supports = ::supportsKeyFormat
) { key, format, bytes ->
when (format) {
AES.Key.Format.RAW -> assertContentEquals(bytes, key.encodeTo(format), "Key $format encoding")
AES.Key.Format.RAW -> assertContentEquals(bytes, key.encode(format).toByteArray(), "Key $format encoding")
AES.Key.Format.JWK -> {} //no check for JWK yet
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ abstract class AesCbcTest(provider: CryptographyProvider) : AesBasedTest<AES.CBC
@Test
fun testSizes() = runTestForEachKeySize {
val key = algorithm.keyGenerator(keySize).generateKey()
assertEquals(keySize.inBytes, key.encodeTo(AES.Key.Format.RAW).size)
assertEquals(keySize.inBytes, key.encode(AES.Key.Format.RAW).toByteArray().size)

key.cipher(padding = true).run {
assertEquals(ivSize + blockSize * 1, encrypt(ByteArray(0)).size)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ abstract class AesGcmTest(provider: CryptographyProvider) : AesBasedTest<AES.GCM
@Test
fun testSizes() = runTestForEachKeySize {
val key = algorithm.keyGenerator(keySize).generateKey()
assertEquals(keySize.inBytes, key.encodeTo(AES.Key.Format.RAW).size)
assertEquals(keySize.inBytes, key.encode(AES.Key.Format.RAW).toByteArray().size)

listOf(96, 104, 112, 120, 128).forEach { tagSizeBits ->
val tagSize = tagSizeBits.bits.inBytes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ abstract class HmacCompatibilityTest(provider: CryptographyProvider) : Compatibi
supports = ::supportsKeyFormat
) { key, format, bytes ->
when (format) {
HMAC.Key.Format.RAW -> assertContentEquals(bytes, key.encodeTo(format), "Key $format encoding")
HMAC.Key.Format.RAW -> assertContentEquals(bytes, key.encode(format).toByteArray(), "Key $format encoding")
HMAC.Key.Format.JWK -> {} //no check for JWK yet
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ abstract class HmacTest(provider: CryptographyProvider) : ProviderTest(provider)
@Test
fun testSizes() = runTestForEachDigest {
val key = algorithm.keyGenerator(digest).generateKey()
assertEquals(digestBlockSize, key.encodeTo(HMAC.Key.Format.RAW).size)
assertEquals(digestBlockSize, key.encode(HMAC.Key.Format.RAW).toByteArray().size)
val signatureGenerator = key.signatureGenerator()

assertEquals(digestSize, signatureGenerator.generateSignature(ByteArray(0)).size)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package dev.whyoleg.cryptography.providers.apple.algorithms

import dev.whyoleg.cryptography.algorithms.symmetric.*
import dev.whyoleg.cryptography.binary.*
import dev.whyoleg.cryptography.binary.BinarySize.Companion.bits
import dev.whyoleg.cryptography.materials.key.*
import dev.whyoleg.cryptography.random.*

Expand All @@ -18,12 +19,14 @@ internal abstract class CCAes<K : AES.Key> : AES<K> {
AesCtrKeyGenerator(keySize.inBytes)

private inner class AesKeyDecoder : KeyDecoder<AES.Key.Format, K> {
override fun decodeFromBlocking(format: AES.Key.Format, data: ByteArray): K = when (format) {
override suspend fun decode(format: AES.Key.Format, data: BinaryData): K = decodeBlocking(format, data)

override fun decodeBlocking(format: AES.Key.Format, data: BinaryData): K = when (format) {
AES.Key.Format.RAW -> {
require(data.size == 16 || data.size == 24 || data.size == 32) {
require(data.size == 128.bits || data.size == 192.bits || data.size == 256.bits) {
"AES key size must be 128, 192 or 256 bits"
}
wrapKey(data.copyOf())
wrapKey(data.toByteArray())
}
AES.Key.Format.JWK -> error("JWK is not supported")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package dev.whyoleg.cryptography.providers.apple.algorithms

import dev.whyoleg.cryptography.*
import dev.whyoleg.cryptography.algorithms.symmetric.*
import dev.whyoleg.cryptography.binary.*
import dev.whyoleg.cryptography.providers.apple.internal.*
import dev.whyoleg.cryptography.random.*
import platform.CoreCrypto.*
Expand All @@ -15,8 +16,9 @@ internal object CCAesCbc : CCAes<AES.CBC.Key>(), AES.CBC {

private class AesCbcKey(private val key: ByteArray) : AES.CBC.Key {
override fun cipher(padding: Boolean): AES.IvCipher = AesCbcCipher(key, padding)
override fun encodeToBlocking(format: AES.Key.Format): ByteArray = when (format) {
AES.Key.Format.RAW -> key.copyOf()
override suspend fun encode(format: AES.Key.Format): BinaryData = encodeBlocking(format)
override fun encodeBlocking(format: AES.Key.Format): BinaryData = when (format) {
AES.Key.Format.RAW -> BinaryData.fromByteArray(key)
AES.Key.Format.JWK -> error("JWK is not supported")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package dev.whyoleg.cryptography.providers.apple.algorithms

import dev.whyoleg.cryptography.*
import dev.whyoleg.cryptography.algorithms.symmetric.*
import dev.whyoleg.cryptography.binary.*
import dev.whyoleg.cryptography.providers.apple.internal.*
import dev.whyoleg.cryptography.random.*
import kotlinx.cinterop.*
Expand All @@ -16,8 +17,9 @@ internal object CCAesCtr : CCAes<AES.CTR.Key>(), AES.CTR {

private class AesCtrKey(private val key: ByteArray) : AES.CTR.Key {
override fun cipher(): AES.IvCipher = AesCtrCipher(key)
override fun encodeToBlocking(format: AES.Key.Format): ByteArray = when (format) {
AES.Key.Format.RAW -> key.copyOf()
override suspend fun encode(format: AES.Key.Format): BinaryData = encodeBlocking(format)
override fun encodeBlocking(format: AES.Key.Format): BinaryData = when (format) {
AES.Key.Format.RAW -> BinaryData.fromByteArray(key)
AES.Key.Format.JWK -> error("JWK is not supported")
}
}
Expand Down
Loading

0 comments on commit 81491c8

Please sign in to comment.