Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,37 @@ internal actual fun createKeyPairManager(): KeyPairManager = KeyPairManagerAndro

private class KeyPairManagerAndroidImpl : KeyPairManager() {

private val keysDir by lazy {
appCtx.filesDir.resolve("passkeys").also { passkeysDir ->
passkeysDir.mkdir()
Comment thread
LouisCAD marked this conversation as resolved.
//TODO[ik-auth]: Remove the code below after the next pre-release.
appCtx.filesDir.listFiles { it.name.endsWith(".key") }!!.forEach { keyFile ->
Comment thread
LouisCAD marked this conversation as resolved.
keyFile.renameTo(passkeysDir.resolve(keyFile.name))
}
Comment thread
LouisCAD marked this conversation as resolved.
}
Comment thread
LouisCAD marked this conversation as resolved.
}

override fun ensureKeyPairsAreMoved() {
val _ = keysDir //TODO[ik-auth]: Remove this code and the super method after the next pre-release.
}
@Throws(Exception::class)
override suspend fun generateNewKey(userId: Long, keyId: String): Failure.KeyManagement.GenerationFailed? {
val keyPair = generateEcKeyPair().getOrElse {
return Failure.KeyManagement.GenerationFailed(it.toString())
}

saveFileToFilesDir("$userId-$keyId-private.key", keyPair.private.encoded)
saveFileToFilesDir("$userId-$keyId-public.key", keyPair.public.encoded)
Dispatchers.IO {
keyFile(userId = userId, keyId = keyId, isPublic = false).writeBytes(keyPair.private.encoded)
keyFile(userId = userId, keyId = keyId, isPublic = true).writeBytes(keyPair.public.encoded)
}
return null
}

override suspend fun retrievePublicKey(
userId: Long,
keyId: String,
): Xor<ByteArray, Failure.KeyManagement.KeyExtractionFailed> = Dispatchers.IO {
val file = File(appCtx.filesDir, "$userId-$keyId-public.key")
val file = keyFile(userId = userId, keyId = keyId, isPublic = true)
runCatching {
Xor.First(file.readBytes())
}.getOrElse { Xor.Second(Failure.KeyManagement.KeyExtractionFailed(it.toString())) }
Expand All @@ -56,15 +71,15 @@ private class KeyPairManagerAndroidImpl : KeyPairManager() {
userId: Long,
keyId: String,
): Xor<ByteArray, Failure.KeyManagement.KeyExtractionFailed> = Dispatchers.IO {
val file = File(appCtx.filesDir, "$userId-$keyId-private.key")
val file = keyFile(userId = userId, keyId = keyId, isPublic = false)
runCatching {
Xor.First(file.readBytes())
}.getOrElse { Xor.Second(Failure.KeyManagement.KeyExtractionFailed(it.toString())) }
}

override suspend fun getSortedKeyIds(matchOn: MatchOn): List<String> {
val files = withContext(Dispatchers.IO) {
appCtx.filesDir.listFiles()
keysDir.listFiles()
} ?: return emptyList()
return buildList {
val predicate = matchOn.asFilterPredicate()
Expand All @@ -91,19 +106,18 @@ private class KeyPairManagerAndroidImpl : KeyPairManager() {
override suspend fun findKeyIdFor(matchOn: MatchOn): String? {
val predicate = matchOn.asFilterPredicate()
val userPassKey: File = withContext(Dispatchers.IO) {
appCtx.filesDir.listFiles()
keysDir.listFiles()
}?.find {
predicate(it.name)
} ?: return null

//TODO 2: Put keys into a dedicated dir
return extractKeyIdFromFileName(userPassKey.name)
}

override suspend fun deleteKeysMatching(matchOn: MatchOn): Xor<Unit, Failure.KeyManagement.KeyNotFound> {
val predicate = matchOn.asFilterPredicate()
val keys = withContext(Dispatchers.IO) {
appCtx.filesDir.listFiles()
keysDir.listFiles()
}?.filter {
predicate(it.name)
} ?: return Xor.Second(Failure.KeyManagement.KeyNotFound("No keys"))
Expand All @@ -120,8 +134,8 @@ private class KeyPairManagerAndroidImpl : KeyPairManager() {
endIndex = name.indexOfLast { it == '-' }
)

private suspend fun saveFileToFilesDir(fileName: String, key: ByteArray) = Dispatchers.IO {
val file = File(appCtx.filesDir, fileName)
file.writeBytes(key)
private fun keyFile(userId: Long, keyId: String, isPublic: Boolean): File {
val visibility = if (isPublic) "public" else "private"
return keysDir.resolve("$userId-$keyId-$visibility.key")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ abstract class AuthenticatorFacade internal constructor() {
val authenticatorManager = AuthenticatorManager(
webAuthnRepository = webAuthnRepository,
accountsRepository = accountsRepository
)
).also { it.keyPairManager.ensureKeyPairsAreMoved() }
val migrationManager = MigrationManager(
accountsDatabase = accountsDatabase,
authenticatorManager = authenticatorManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ internal abstract class KeyPairManager protected constructor() {
operator fun invoke(): KeyPairManager = createKeyPairManager()
}

open fun ensureKeyPairsAreMoved() = Unit

/**
* Generates key pair for a new registration
* (migrating from kAuth v1 or a backup, or a fresh new login)
Expand Down
Loading