diff options
Diffstat (limited to 'app/src')
3 files changed, 35 insertions, 2 deletions
diff --git a/app/src/main/java/app/passwordstore/util/extensions/Extensions.kt b/app/src/main/java/app/passwordstore/util/extensions/Extensions.kt index 55a6276e..597a286f 100644 --- a/app/src/main/java/app/passwordstore/util/extensions/Extensions.kt +++ b/app/src/main/java/app/passwordstore/util/extensions/Extensions.kt @@ -33,10 +33,11 @@ fun File.contains(other: File): Boolean { /** * Checks if this [File] is in the password repository directory as given by - * [PasswordRepository.getRepositoryDirectory] + * [PasswordRepository.getRepositoryDirectory]. Compares whole path components, so a sibling such as + * `store-evil` is not inside `store`. */ fun File.isInsideRepository(): Boolean { - return canonicalPath.contains(PasswordRepository.getRepositoryDirectory().canonicalPath) + return canonicalFile.startsWith(PasswordRepository.getRepositoryDirectory().canonicalFile) } /** Recursively lists the files in this [File], skipping any directories it encounters. */ 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})" }}" } diff --git a/app/src/test/java/app/passwordstore/util/git/sshj/SshjConfigTest.kt b/app/src/test/java/app/passwordstore/util/git/sshj/SshjConfigTest.kt index af6eee47..e4bc3eec 100644 --- a/app/src/test/java/app/passwordstore/util/git/sshj/SshjConfigTest.kt +++ b/app/src/test/java/app/passwordstore/util/git/sshj/SshjConfigTest.kt @@ -6,7 +6,11 @@ package app.passwordstore.util.git.sshj import kotlin.test.Test +import kotlin.test.assertEquals import kotlin.test.assertNull +import kotlin.test.assertTrue +import net.schmizz.sshj.common.Ed25519KeyFactory +import net.schmizz.sshj.common.KeyType import net.schmizz.sshj.common.SecurityUtils class SshjConfigTest { @@ -19,4 +23,14 @@ class SshjConfigTest { setUpBouncyCastleForSshj() assertNull(SecurityUtils.getSecurityProvider()) } + + // Conscrypt's Ed25519 keys map to KeyType.UNKNOWN in sshj, so Ed25519 keys must come from + // Bouncy Castle whatever provider the platform puts first. + @Test + fun ed25519KeysComeFromBouncyCastle() { + setUpBouncyCastleForSshj() + val key = Ed25519KeyFactory.getPublicKey(ByteArray(32) { 1 }) + assertTrue(key.javaClass.name.startsWith("org.bouncycastle."), key.javaClass.name) + assertEquals(KeyType.ED25519, KeyType.fromKey(key)) + } } |
