From b6a44a9797e61c619f2590c22b758f9c78365e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernd=20Pr=C3=BCnster?= Date: Tue, 14 Jan 2025 15:12:56 +0100 Subject: [PATCH] fix key agreement on lower android versions for SW keys --- .../kotlin/at/asitplus/cryptotest/App.kt | 35 ++++++++++++++----- docs/docs/features.md | 7 +++- .../supreme/agreement/KeyAgreement.android.kt | 2 +- .../signum/supreme/agreement/KeyAgreement.kt | 6 ++-- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/demoapp/composeApp/src/commonMain/kotlin/at/asitplus/cryptotest/App.kt b/demoapp/composeApp/src/commonMain/kotlin/at/asitplus/cryptotest/App.kt index 55c17b66..fd29c220 100644 --- a/demoapp/composeApp/src/commonMain/kotlin/at/asitplus/cryptotest/App.kt +++ b/demoapp/composeApp/src/commonMain/kotlin/at/asitplus/cryptotest/App.kt @@ -54,8 +54,10 @@ import at.asitplus.signum.supreme.sign.makeVerifier import at.asitplus.signum.supreme.sign.verify import at.asitplus.cryptotest.theme.AppTheme import at.asitplus.cryptotest.theme.LocalThemeIsDark +import at.asitplus.signum.indispensable.CryptoPrivateKey import at.asitplus.signum.indispensable.CryptoPublicKey import at.asitplus.signum.indispensable.jsonEncoded +import at.asitplus.signum.supreme.SecretExposure import at.asitplus.signum.supreme.agreement.keyAgreement import at.asitplus.signum.supreme.asKmmResult import at.asitplus.signum.supreme.os.PlatformSignerConfigurationBase @@ -460,22 +462,37 @@ internal fun App() { Napier.w { "input: $inputData" } Napier.w { "signingKey: $currentKey" } CoroutineScope(context).launch { - val alg= keyAlgorithm.algorithm as SignatureAlgorithm.ECDSA - val eph= Signer.Ephemeral { - ec { - curve = alg.requiredCurve - ?: ECCurve.entries.find { it.nativeDigest == alg.digest }!! - digests = setOf(alg.digest) + val alg = keyAlgorithm.algorithm as SignatureAlgorithm.ECDSA + val eph = Signer.Ephemeral { + ec { + curve = alg.requiredCurve + ?: ECCurve.entries.find { it.nativeDigest == alg.digest }!! + digests = setOf(alg.digest) } }.getOrThrow() val pub = eph.publicKey as CryptoPublicKey.EC Napier.i { "Got Pubkey: $pub" } - val agreed= pub.keyAgreement( currentSigner!!.getOrThrow() as Signer.ECDSA).getOrThrow() + + val agreed3 = + (currentSigner!!.getOrThrow().publicKey as CryptoPublicKey.EC).keyAgreement( + @OptIn(SecretExposure::class) + eph.exportPrivateKey() + .getOrThrow() as CryptoPrivateKey.WithPublicKey + ).getOrThrow() + Napier.i { "AGREED3: ${agreed3.encodeBase64()}" } + + + val agreed = + pub.keyAgreement(currentSigner!!.getOrThrow() as Signer.ECDSA) + .getOrThrow() Napier.i { "AGREED1: ${agreed.encodeBase64()}" } - val agreed2 = ( currentSigner!!.getOrThrow() as Signer.ECDSA).keyAgreement(pub).getOrThrow() + val agreed2 = + (currentSigner!!.getOrThrow() as Signer.ECDSA).keyAgreement(pub) + .getOrThrow() Napier.i { "AGREED2: ${agreed2.encodeBase64()}" } - } + + } }, diff --git a/docs/docs/features.md b/docs/docs/features.md index 23ec7329..71d10d8e 100644 --- a/docs/docs/features.md +++ b/docs/docs/features.md @@ -52,7 +52,12 @@ from certain (otherwise) hard requirements for Devices launched with later Andro Hence, a device launched with Android 10, and later updated to Android 12 may still not support key agreement in hardware. The Supreme crypto provider will return a failure, in if key agreement is not supported in hardware. -TODO: will fail for auth on every use! Bug in Android; fix hidden behind a feature flag + +In addition, all Android versions supporting key agreement contain a bug, which makes it impossible +to perform key agreement using an auth-on-every-use key. The bugfix is hidden behind a disabled-by-default +feature flag in the Android source code. +**Hence, either don't require biometric authentication for key you want to use for key agreement or +use a timeout of at leas one second!** ## Supported Algorithms diff --git a/supreme/src/androidMain/kotlin/at/asitplus/signum/supreme/agreement/KeyAgreement.android.kt b/supreme/src/androidMain/kotlin/at/asitplus/signum/supreme/agreement/KeyAgreement.android.kt index a6cdae1f..21396b54 100644 --- a/supreme/src/androidMain/kotlin/at/asitplus/signum/supreme/agreement/KeyAgreement.android.kt +++ b/supreme/src/androidMain/kotlin/at/asitplus/signum/supreme/agreement/KeyAgreement.android.kt @@ -42,7 +42,7 @@ actual suspend fun Signer.ECDSA.performAgreement( agreement.doPhase(publicKey.toJcaPublicKey().getOrThrow(), true) agreement.generateSecret() } else { - javax.crypto.KeyAgreement.getInstance("ECDH", "AndroidKeyStore").also { + javax.crypto.KeyAgreement.getInstance("ECDH").also { @OptIn(HazardousMaterials::class) it.init(jcaPrivateKey) it.doPhase(publicKey.toJcaPublicKey().getOrThrow(), true) diff --git a/supreme/src/commonMain/kotlin/at/asitplus/signum/supreme/agreement/KeyAgreement.kt b/supreme/src/commonMain/kotlin/at/asitplus/signum/supreme/agreement/KeyAgreement.kt index a14fb067..c9449e63 100644 --- a/supreme/src/commonMain/kotlin/at/asitplus/signum/supreme/agreement/KeyAgreement.kt +++ b/supreme/src/commonMain/kotlin/at/asitplus/signum/supreme/agreement/KeyAgreement.kt @@ -42,12 +42,12 @@ suspend fun CryptoPublicKey.EC.keyAgreement( * Elliptic-curve Diffie-Hellman key agreement. * Curves of public key and signer need to match! */ -suspend fun CryptoPrivateKey.WithPublicKey.keyAgreement(publicKey: CryptoPublicKey.EC) = catching { - (SignatureAlgorithm.ECDSA(this.publicKey.curve.nativeDigest, this.publicKey.curve).signerFor(this) +suspend fun CryptoPrivateKey.WithPublicKey.keyAgreement(publicKey: CryptoPublicKey.EC) = + (SignatureAlgorithm.ECDSA(this.publicKey.curve.nativeDigest, this.publicKey.curve) + .signerFor(this) .getOrThrow() as Signer.ECDSA).keyAgreement( publicKey ) -} /** * Elliptic-curve Diffie-Hellman key agreement.