From 81aaedec7916d29b54be61c72191c5c367b16482 Mon Sep 17 00:00:00 2001 From: Aria Wisp Date: Sat, 6 Sep 2025 19:26:16 -0600 Subject: [PATCH] providers/base: add accumulating sign/verify helpers; add unwrapSubjectPublicKeyInfo(Set) helper; update ABI --- .../base/api/cryptography-provider-base.api | 19 ++++ .../api/cryptography-provider-base.klib.api | 21 +++++ .../src/commonMain/kotlin/materials/keys.kt | 9 ++ .../AccumulatingSignatureFunctions.kt | 92 +++++++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 cryptography-providers/base/src/commonMain/kotlin/operations/AccumulatingSignatureFunctions.kt diff --git a/cryptography-providers/base/api/cryptography-provider-base.api b/cryptography-providers/base/api/cryptography-provider-base.api index c1c6f324..b425a9e1 100644 --- a/cryptography-providers/base/api/cryptography-provider-base.api +++ b/cryptography-providers/base/api/cryptography-provider-base.api @@ -66,6 +66,7 @@ public final class dev/whyoleg/cryptography/providers/base/algorithms/EcKt { public final class dev/whyoleg/cryptography/providers/base/materials/KeysKt { public static final fun unwrapPem-unSj4pc (Ljava/lang/String;[B)[B public static final fun unwrapPrivateKeyInfo-4RESAxk (Ljava/lang/String;[B)[B + public static final fun unwrapSubjectPublicKeyInfo (Ljava/util/Set;[B)[B public static final fun unwrapSubjectPublicKeyInfo-4RESAxk (Ljava/lang/String;[B)[B public static final fun wrapPem-unSj4pc (Ljava/lang/String;[B)[B public static final fun wrapPrivateKeyInfo (ILdev/whyoleg/cryptography/serialization/asn1/modules/KeyAlgorithmIdentifier;[B)[B @@ -77,6 +78,24 @@ public final class dev/whyoleg/cryptography/providers/base/operations/Accumulati public fun close ()V } +public final class dev/whyoleg/cryptography/providers/base/operations/AccumulatingSignFunction : dev/whyoleg/cryptography/operations/SignFunction { + public fun (Lkotlin/jvm/functions/Function1;)V + public fun close ()V + public fun reset ()V + public fun signIntoByteArray ([BI)I + public fun signToByteArray ()[B + public fun update ([BII)V +} + +public final class dev/whyoleg/cryptography/providers/base/operations/AccumulatingVerifyFunction : dev/whyoleg/cryptography/operations/VerifyFunction { + public fun (Lkotlin/jvm/functions/Function4;)V + public fun close ()V + public fun reset ()V + public fun tryVerify ([BII)Z + public fun update ([BII)V + public fun verify ([BII)V +} + public abstract interface class dev/whyoleg/cryptography/providers/base/operations/BaseAuthenticatedCipher : dev/whyoleg/cryptography/operations/AuthenticatedCipher, dev/whyoleg/cryptography/providers/base/operations/BaseAuthenticatedDecryptor, dev/whyoleg/cryptography/providers/base/operations/BaseAuthenticatedEncryptor, dev/whyoleg/cryptography/providers/base/operations/BaseCipher { } diff --git a/cryptography-providers/base/api/cryptography-provider-base.klib.api b/cryptography-providers/base/api/cryptography-provider-base.klib.api index 900d5746..26983b3a 100644 --- a/cryptography-providers/base/api/cryptography-provider-base.klib.api +++ b/cryptography-providers/base/api/cryptography-provider-base.klib.api @@ -134,6 +134,26 @@ final class dev.whyoleg.cryptography.providers.base.operations/AccumulatingCiphe final fun close() // dev.whyoleg.cryptography.providers.base.operations/AccumulatingCipherFunction.close|close(){}[0] } +final class dev.whyoleg.cryptography.providers.base.operations/AccumulatingSignFunction : dev.whyoleg.cryptography.operations/SignFunction { // dev.whyoleg.cryptography.providers.base.operations/AccumulatingSignFunction|null[0] + constructor (kotlin/Function1) // dev.whyoleg.cryptography.providers.base.operations/AccumulatingSignFunction.|(kotlin.Function1){}[0] + + final fun close() // dev.whyoleg.cryptography.providers.base.operations/AccumulatingSignFunction.close|close(){}[0] + final fun reset() // dev.whyoleg.cryptography.providers.base.operations/AccumulatingSignFunction.reset|reset(){}[0] + final fun signIntoByteArray(kotlin/ByteArray, kotlin/Int): kotlin/Int // dev.whyoleg.cryptography.providers.base.operations/AccumulatingSignFunction.signIntoByteArray|signIntoByteArray(kotlin.ByteArray;kotlin.Int){}[0] + final fun signToByteArray(): kotlin/ByteArray // dev.whyoleg.cryptography.providers.base.operations/AccumulatingSignFunction.signToByteArray|signToByteArray(){}[0] + final fun update(kotlin/ByteArray, kotlin/Int, kotlin/Int) // dev.whyoleg.cryptography.providers.base.operations/AccumulatingSignFunction.update|update(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] +} + +final class dev.whyoleg.cryptography.providers.base.operations/AccumulatingVerifyFunction : dev.whyoleg.cryptography.operations/VerifyFunction { // dev.whyoleg.cryptography.providers.base.operations/AccumulatingVerifyFunction|null[0] + constructor (kotlin/Function4) // dev.whyoleg.cryptography.providers.base.operations/AccumulatingVerifyFunction.|(kotlin.Function4){}[0] + + final fun close() // dev.whyoleg.cryptography.providers.base.operations/AccumulatingVerifyFunction.close|close(){}[0] + final fun reset() // dev.whyoleg.cryptography.providers.base.operations/AccumulatingVerifyFunction.reset|reset(){}[0] + final fun tryVerify(kotlin/ByteArray, kotlin/Int, kotlin/Int): kotlin/Boolean // dev.whyoleg.cryptography.providers.base.operations/AccumulatingVerifyFunction.tryVerify|tryVerify(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] + final fun update(kotlin/ByteArray, kotlin/Int, kotlin/Int) // dev.whyoleg.cryptography.providers.base.operations/AccumulatingVerifyFunction.update|update(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] + final fun verify(kotlin/ByteArray, kotlin/Int, kotlin/Int) // dev.whyoleg.cryptography.providers.base.operations/AccumulatingVerifyFunction.verify|verify(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] +} + final val dev.whyoleg.cryptography.providers.base/EmptyByteArray // dev.whyoleg.cryptography.providers.base/EmptyByteArray|{}EmptyByteArray[0] final fun (): kotlin/ByteArray // dev.whyoleg.cryptography.providers.base/EmptyByteArray.|(){}[0] @@ -143,6 +163,7 @@ final fun dev.whyoleg.cryptography.providers.base.algorithms/convertEcPrivateKey final fun dev.whyoleg.cryptography.providers.base.materials/unwrapPem(dev.whyoleg.cryptography.serialization.pem/PemLabel, kotlin/ByteArray): kotlin/ByteArray // dev.whyoleg.cryptography.providers.base.materials/unwrapPem|unwrapPem(dev.whyoleg.cryptography.serialization.pem.PemLabel;kotlin.ByteArray){}[0] final fun dev.whyoleg.cryptography.providers.base.materials/unwrapPrivateKeyInfo(dev.whyoleg.cryptography.serialization.asn1/ObjectIdentifier, kotlin/ByteArray): kotlin/ByteArray // dev.whyoleg.cryptography.providers.base.materials/unwrapPrivateKeyInfo|unwrapPrivateKeyInfo(dev.whyoleg.cryptography.serialization.asn1.ObjectIdentifier;kotlin.ByteArray){}[0] final fun dev.whyoleg.cryptography.providers.base.materials/unwrapSubjectPublicKeyInfo(dev.whyoleg.cryptography.serialization.asn1/ObjectIdentifier, kotlin/ByteArray): kotlin/ByteArray // dev.whyoleg.cryptography.providers.base.materials/unwrapSubjectPublicKeyInfo|unwrapSubjectPublicKeyInfo(dev.whyoleg.cryptography.serialization.asn1.ObjectIdentifier;kotlin.ByteArray){}[0] +final fun dev.whyoleg.cryptography.providers.base.materials/unwrapSubjectPublicKeyInfo(kotlin.collections/Set, kotlin/ByteArray): kotlin/ByteArray // dev.whyoleg.cryptography.providers.base.materials/unwrapSubjectPublicKeyInfo|unwrapSubjectPublicKeyInfo(kotlin.collections.Set;kotlin.ByteArray){}[0] final fun dev.whyoleg.cryptography.providers.base.materials/wrapPem(dev.whyoleg.cryptography.serialization.pem/PemLabel, kotlin/ByteArray): kotlin/ByteArray // dev.whyoleg.cryptography.providers.base.materials/wrapPem|wrapPem(dev.whyoleg.cryptography.serialization.pem.PemLabel;kotlin.ByteArray){}[0] final fun dev.whyoleg.cryptography.providers.base.materials/wrapPrivateKeyInfo(kotlin/Int, dev.whyoleg.cryptography.serialization.asn1.modules/KeyAlgorithmIdentifier, kotlin/ByteArray): kotlin/ByteArray // dev.whyoleg.cryptography.providers.base.materials/wrapPrivateKeyInfo|wrapPrivateKeyInfo(kotlin.Int;dev.whyoleg.cryptography.serialization.asn1.modules.KeyAlgorithmIdentifier;kotlin.ByteArray){}[0] final fun dev.whyoleg.cryptography.providers.base.materials/wrapSubjectPublicKeyInfo(dev.whyoleg.cryptography.serialization.asn1.modules/KeyAlgorithmIdentifier, kotlin/ByteArray): kotlin/ByteArray // dev.whyoleg.cryptography.providers.base.materials/wrapSubjectPublicKeyInfo|wrapSubjectPublicKeyInfo(dev.whyoleg.cryptography.serialization.asn1.modules.KeyAlgorithmIdentifier;kotlin.ByteArray){}[0] diff --git a/cryptography-providers/base/src/commonMain/kotlin/materials/keys.kt b/cryptography-providers/base/src/commonMain/kotlin/materials/keys.kt index 97aa69aa..438a42c2 100644 --- a/cryptography-providers/base/src/commonMain/kotlin/materials/keys.kt +++ b/cryptography-providers/base/src/commonMain/kotlin/materials/keys.kt @@ -32,6 +32,15 @@ public fun unwrapSubjectPublicKeyInfo(algorithm: ObjectIdentifier, key: ByteArra }.subjectPublicKey.byteArray } +@CryptographyProviderApi +public fun unwrapSubjectPublicKeyInfo(algorithms: Set, key: ByteArray): ByteArray { + return Der.decodeFromByteArray(SubjectPublicKeyInfo.serializer(), key).also { + check(it.algorithm.algorithm in algorithms) { + "Expected one of algorithms '${algorithms.joinToString { a -> a.value }}', received: '${it.algorithm.algorithm}'" + } + }.subjectPublicKey.byteArray +} + @CryptographyProviderApi public fun wrapSubjectPublicKeyInfo(identifier: KeyAlgorithmIdentifier, key: ByteArray): ByteArray { return Der.encodeToByteArray( diff --git a/cryptography-providers/base/src/commonMain/kotlin/operations/AccumulatingSignatureFunctions.kt b/cryptography-providers/base/src/commonMain/kotlin/operations/AccumulatingSignatureFunctions.kt new file mode 100644 index 00000000..e39d1d97 --- /dev/null +++ b/cryptography-providers/base/src/commonMain/kotlin/operations/AccumulatingSignatureFunctions.kt @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2025 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.whyoleg.cryptography.providers.base.operations + +import dev.whyoleg.cryptography.* +import dev.whyoleg.cryptography.operations.* +import dev.whyoleg.cryptography.providers.base.* + +@CryptographyProviderApi +public class AccumulatingSignFunction( + private val oneShot: (data: ByteArray) -> ByteArray, +) : SignFunction { + private var isClosed = false + private var accumulator = EmptyByteArray + + private fun ensureNotClosed() { check(!isClosed) { "Already closed" } } + private fun accumulate(source: ByteArray, startIndex: Int, endIndex: Int) { + ensureNotClosed() + checkBounds(source.size, startIndex, endIndex) + accumulator += source.copyOfRange(startIndex, endIndex) + } + + override fun update(source: ByteArray, startIndex: Int, endIndex: Int) { + accumulate(source, startIndex, endIndex) + } + + override fun signIntoByteArray(destination: ByteArray, destinationOffset: Int): Int { + val sig = signToByteArray() + sig.copyInto(destination, destinationOffset) + return sig.size + } + + override fun signToByteArray(): ByteArray { + ensureNotClosed() + val out = oneShot(accumulator) + reset() + return out + } + + override fun reset() { + ensureNotClosed() + accumulator = EmptyByteArray + } + + override fun close() { + isClosed = true + accumulator = EmptyByteArray + } +} + +@CryptographyProviderApi +public class AccumulatingVerifyFunction( + private val oneShot: (data: ByteArray, signature: ByteArray, startIndex: Int, endIndex: Int) -> Boolean, +) : VerifyFunction { + private var isClosed = false + private var accumulator = EmptyByteArray + + private fun ensureNotClosed() { check(!isClosed) { "Already closed" } } + private fun accumulate(source: ByteArray, startIndex: Int, endIndex: Int) { + ensureNotClosed() + checkBounds(source.size, startIndex, endIndex) + accumulator += source.copyOfRange(startIndex, endIndex) + } + + override fun update(source: ByteArray, startIndex: Int, endIndex: Int) { + accumulate(source, startIndex, endIndex) + } + + override fun tryVerify(signature: ByteArray, startIndex: Int, endIndex: Int): Boolean { + ensureNotClosed() + checkBounds(signature.size, startIndex, endIndex) + val ok = oneShot(accumulator, signature, startIndex, endIndex) + reset() + return ok + } + + override fun verify(signature: ByteArray, startIndex: Int, endIndex: Int) { + check(tryVerify(signature, startIndex, endIndex)) { "Invalid signature" } + } + + override fun reset() { + ensureNotClosed() + accumulator = EmptyByteArray + } + + override fun close() { + isClosed = true + accumulator = EmptyByteArray + } +}