Skip to content
Open
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
2 changes: 1 addition & 1 deletion Core
3 changes: 3 additions & 0 deletions multiplatform-lib/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(core.plugins.android.kmp.library)
alias(core.plugins.kotlin.serialization)
alias(libs.plugins.skie)
alias(libs.plugins.androidx.room)
alias(libs.plugins.ksp)
Expand Down Expand Up @@ -52,7 +53,9 @@ kotlin {
commonMain {
dependencies {
implementation(core.kotlinx.coroutines.core)
implementation(core.kotlinx.serialization.core)
implementation(core.kotlinx.serialization.cbor)
implementation(core.okio)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.content.negociation)
implementation(libs.ktor.client.json)
Expand Down
65 changes: 65 additions & 0 deletions multiplatform-lib/src/androidDeviceTest/kotlin/WebAuthnTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Infomaniak Authenticator - Android
* Copyright (C) 2026 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.auth

import com.infomaniak.auth.lib.RegisterPasskeyBuilder
import com.infomaniak.auth.lib.internal.KeyPairManagerImpl
import com.infomaniak.auth.lib.network.models.PasskeysOptions
import com.infomaniak.auth.lib.network.models.PubKeyCredParam
import com.infomaniak.auth.lib.network.models.RelyingParty
import com.infomaniak.auth.lib.network.models.User
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.ExperimentalSerializationApi
import kotlin.test.Test

class WebAuthnTest {

@OptIn(ExperimentalSerializationApi::class)
@Test
fun registerPasskeyGeneration() = runTest {
// This is sent from [GET] /api/authenticator/passkeys/options
val passkeysOptions = PasskeysOptions(
challenge = "U3NkRnF6RlVwUnpKRGhVMw",
relyingParty = RelyingParty(
id = "infomaniak.com",
name = "Infomaniak",
icon = null,
),
user = User(
id = "MQ",
name = "test@user.com",
displayName = "Test"
),
pubKeyCredParams = listOf(
PubKeyCredParam(
type = "public-key",
algorithm = -7 // ES256
)
),
excludeCredentials = emptyList(),
)

// Just getting the public key to generate RegisterPasskey object
val keyPairManager = KeyPairManagerImpl()
keyPairManager.generateNewKey()
val publicKeyAsByteArray = keyPairManager.retrievePublicKey().firstOrNull()!!

// Nothing to test on the generated object for now
RegisterPasskeyBuilder(passkeysOptions, publicKeyAsByteArray).build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package internal
package com.infomaniak.auth.internal

import com.infomaniak.auth.lib.internal.KeyPairManagerImpl
import com.infomaniak.auth.lib.internal.Xor
Expand Down
61 changes: 61 additions & 0 deletions multiplatform-lib/src/androidMain/kotlin/PublicKeyUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Infomaniak Authenticator - Android
* Copyright (C) 2026 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.auth.lib

import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.encodeToByteArray
import java.security.KeyFactory
import java.security.PublicKey
import java.security.interfaces.ECPublicKey
import java.security.spec.X509EncodedKeySpec

actual object PublicKeyUtils {

@OptIn(ExperimentalSerializationApi::class)
actual fun getPublicKeyCose(publicKeyByteArray: ByteArray): ByteArray {
val publicKey = getPublicKeyFromByteArray(publicKeyByteArray) as ECPublicKey
val w = publicKey.w
val x = w.affineX.toByteArray().padTo32Bytes()
val y = w.affineY.toByteArray().padTo32Bytes()
val coseKey = CoseKey(
kty = 2, // kty: EC2
alg = -7,// alg: ES256
crv = -1,// crv: P-2
x = x, // x coord
y = y // y coord
)
return Cbor.encodeToByteArray(coseKey)
}

private fun getPublicKeyFromByteArray(bytes: ByteArray): PublicKey {
val keySpec = X509EncodedKeySpec(bytes)
val keyFactory = KeyFactory.getInstance("EC")
return keyFactory.generatePublic(keySpec)
}

private fun ByteArray.padTo32Bytes(): ByteArray {
return if (this.size == 32) {
this
} else if (this.size > 32) {
this.copyOfRange(this.size - 32, this.size)
} else {
ByteArray(32 - this.size) { 0x00 } + this
}
}
}
1 change: 0 additions & 1 deletion multiplatform-lib/src/commonMain/kotlin/Account.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.infomaniak.auth.lib

@ConsistentCopyVisibility
Expand Down
1 change: 0 additions & 1 deletion multiplatform-lib/src/commonMain/kotlin/AppStatus.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.infomaniak.auth.lib

sealed interface AppStatus {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.infomaniak.auth.lib

import kotlinx.coroutines.flow.Flow
Expand Down
22 changes: 3 additions & 19 deletions multiplatform-lib/src/commonMain/kotlin/AuthenticatorInjection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,20 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.auth.lib

import com.infomaniak.auth.lib.network.ApiClientProvider
import com.infomaniak.auth.lib.network.interfaces.CrashReportInterface
import network.repositories.WebAuthnRepository
import network.utils.ApiEnvironment

/*
Infomaniak Authenticator - Android
Copyright (C) 2026 Infomaniak Network SA

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

class AuthenticatorInjection(
private val environment: ApiEnvironment,
private val userAgent: String,
private val databaseRootDirectory: String? = null,
private val crashReport: CrashReportInterface,
) {
private val apiClientProvider by lazy { ApiClientProvider(userAgent, crashReport) }

private val webAuthnRepository by lazy { WebAuthnRepository(apiClientProvider, environment) }
}
72 changes: 72 additions & 0 deletions multiplatform-lib/src/commonMain/kotlin/CoseKey.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Infomaniak Authenticator - Android
* Copyright (C) 2026 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.auth.lib

import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlin.io.encoding.Base64

@Serializable
data class CoseKey(
val kty: Int,
val alg: Int,
val crv: Int,
@Serializable(with = ByteArrayAsByteListBase64Serializer::class)
val x: ByteArray,
@Serializable(with = ByteArrayAsByteListBase64Serializer::class)
val y: ByteArray,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false

other as CoseKey

if (kty != other.kty) return false
if (alg != other.alg) return false
if (crv != other.crv) return false
if (!x.contentEquals(other.x)) return false
if (!y.contentEquals(other.y)) return false

return true
}

override fun hashCode(): Int {
var result = kty
result = 31 * result + alg
result = 31 * result + crv
result = 31 * result + x.contentHashCode()
result = 31 * result + y.contentHashCode()
return result
}
}

object ByteArrayAsByteListBase64Serializer : KSerializer<ByteArray> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ByteArrayAsByteListBase64", PrimitiveKind.STRING)

override fun serialize(encoder: Encoder, value: ByteArray) {
encoder.encodeString(Base64.UrlSafe.encode(value))
}

override fun deserialize(decoder: Decoder) = Base64.UrlSafe.decode(decoder.decodeString())
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.infomaniak.auth.lib

data class CredentialsForMigration(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.infomaniak.auth.lib

sealed interface NotConnectedAction {
Expand Down
23 changes: 23 additions & 0 deletions multiplatform-lib/src/commonMain/kotlin/PublicKeyUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Infomaniak Authenticator - Android
* Copyright (C) 2026 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.auth.lib

expect object PublicKeyUtils {

fun getPublicKeyCose(publicKeyByteArray: ByteArray): ByteArray
}
Loading