From a3c105af9adb2b45ad52fe47218cc8827741dca9 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Tue, 3 Nov 2026 09:48:36 +0100 Subject: fix(git): get sshj's Ed25519 keys from Bouncy Castle Recent Conscrypt (Android 14 mainline updates) registers a KeyFactory for Ed25519. With sshj leaving provider selection to the JCA, its Ed25519KeyFactory now gets Conscrypt's key class, whose algorithm name sshj does not map to ssh-ed25519: KeyType.fromKey returns UNKNOWN and every Ed25519 host key fails with "Don't know how to encode key", so clone and sync over SSH break. 0.1.4 is affected since the sshj 0.41.2 update. Register a provider at position 1 that serves only KeyFactory.Ed25519 from Bouncy Castle (kept by the existing R8 rule). Everything else, including signatures with Android Keystore keys, still goes through normal JCA provider selection. Verified on an Android 14 tablet: clone over SSH failed before and works with the fix. Co-Authored-By: Claude Opus 5.5 (cherry picked from commit ad3c63f665e6264ed1db42d6ae2dfc0947e33393) --- .../java/app/passwordstore/util/git/sshj/SshjConfig.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'app/src/main/java') diff --git a/app/src/main/java/app/passwordstore/util/git/sshj/SshjConfig.kt b/app/src/main/java/app/passwordstore/util/git/sshj/SshjConfig.kt index b43d0007..3a3538ac 100644 --- a/app/src/main/java/app/passwordstore/util/git/sshj/SshjConfig.kt +++ b/app/src/main/java/app/passwordstore/util/git/sshj/SshjConfig.kt @@ -12,6 +12,7 @@ import com.hierynomus.sshj.transport.cipher.GcmCiphers import com.hierynomus.sshj.transport.kex.ExtInfoClientFactory import com.hierynomus.sshj.transport.mac.Macs import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile +import java.security.Provider import java.security.Security import logcat.LogPriority.VERBOSE import logcat.logcat @@ -32,6 +33,18 @@ import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile import org.bouncycastle.jce.provider.BouncyCastleProvider import org.slf4j.Logger +private object Ed25519KeyFactoryProvider : + Provider("HardenedPassEd25519", 1.0, "Ed25519 KeyFactory from Bouncy Castle") { + private fun readResolve(): Any = Ed25519KeyFactoryProvider + + init { + put( + "KeyFactory.Ed25519", + "org.bouncycastle.jcajce.provider.asymmetric.edec.KeyFactorySpi\$Ed25519", + ) + } +} + fun setUpBouncyCastleForSshj() { // Replace the Android BC provider with the Java BouncyCastle provider since the former does // not include all the required algorithms. @@ -48,6 +61,11 @@ fun setUpBouncyCastleForSshj() { runCatching { Class.forName("sun.security.jca.Providers") } Security.insertProviderAt(BouncyCastleProvider(), bcIndex + 1) } + // Recent Conscrypt (Android 14 mainline updates) serves KeyFactory "Ed25519" with keys whose + // algorithm name sshj does not map to ssh-ed25519 (KeyType.UNKNOWN, "Don't know how to encode + // key"), which breaks every Ed25519 host and user key. Pin only that factory to Bouncy Castle, + // ahead of every other provider; signatures still pick a provider per key. + Security.insertProviderAt(Ed25519KeyFactoryProvider, 1) logcat("setUpBouncyCastleForSshj", priority = VERBOSE) { "JCE providers: ${Security.getProviders().joinToString { "${it.name} (${it.version})" }}" } -- cgit v1.2.3