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
Original file line number Diff line number Diff line change
Expand Up @@ -65,24 +65,9 @@ fun CBORObject.decodeAsPublicKeyCredentialUserEntity() = PublicKeyCredentialUser
)

fun getAlgorithm(algorithmInt: Int): Algorithm {
return when (algorithmInt) {
-65535 -> RSAAlgorithm.RS1
-262 -> RSAAlgorithm.LEGACY_RS1
-261 -> EC2Algorithm.ED512
-260 -> EC2Algorithm.ED256
-259 -> RSAAlgorithm.RS512
-258 -> RSAAlgorithm.RS384
-257 -> RSAAlgorithm.RS256
-39 -> RSAAlgorithm.PS512
-38 -> RSAAlgorithm.PS384
-37 -> RSAAlgorithm.PS256
-36 -> EC2Algorithm.ES512
-35 -> EC2Algorithm.ES384
-25 -> EC2Algorithm.ECDH_HKDF_256
-7 -> EC2Algorithm.ES256

else -> Algorithm { algorithmInt }
}
return EC2Algorithm.entries.firstOrNull { it.algoValue == algorithmInt }
?: RSAAlgorithm.entries.firstOrNull { it.algoValue == algorithmInt }
?: Algorithm { algorithmInt }
}

fun PublicKeyCredentialParameters.encodeAsCbor() = CBORObject.NewMap().apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@

package org.microg.gms.fido.core.protocol

import android.os.Build
import androidx.annotation.RequiresApi
import com.google.android.gms.fido.fido2.api.common.Algorithm
import com.google.android.gms.fido.fido2.api.common.EC2Algorithm
import com.google.android.gms.fido.fido2.api.common.RSAAlgorithm
import com.upokecenter.cbor.CBOREncodeOptions
import com.upokecenter.cbor.CBORObject
import java.math.BigInteger
import java.security.AlgorithmParameters
import java.security.KeyFactory
import java.security.PublicKey
import java.security.spec.AlgorithmParameterSpec
import java.security.spec.ECGenParameterSpec
import java.security.spec.ECParameterSpec
import java.security.spec.ECPoint
import java.security.spec.ECPublicKeySpec
import java.security.spec.NamedParameterSpec

class CoseKey(
val algorithm: Algorithm,
Expand All @@ -38,25 +41,98 @@
set(Y, y.encodeAsCbor())
}

sealed class AlgSpec(val keyAlg: String, val agreementAlg: String, val paramSpec: AlgorithmParameterSpec) {
class EC(algName: String): AlgSpec(
"EC",
"ECDH",
ECGenParameterSpec(algName)
)
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
class XDH(algName: String, private val oid: ByteArray, private val keyLength: Int): AlgSpec(
algName,
"XDH",
NamedParameterSpec(algName)
) {
/**
* Works for key+preamble smaller than 256 bytes (x25519 is 32 + preamble = 42)
*/
val x509Preamble: ByteArray
get() {
require(keyLength + oid.size + 5 < 0x100)
val header = byteArrayOf(
0x30, oid.size.toByte() // Sequence of OID.size bytes
) + oid + byteArrayOf(
0x03, (keyLength + 1).toByte(), 0x00 // Bit string of keyLength +1 + 0x00 beginning of the key
)
return byteArrayOf(
0x30, (header.size + keyLength).toByte(), // Sequence of header + keylength bytes
) + header
}
}
}

fun getAlgSpec(): AlgSpec? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// cf. https://www.iana.org/assignments/smi-numbers/smi-numbers.xhtml#smi-numbers-1.3.101
// for OID
when (curveId) {
1 -> AlgSpec.EC("secp256r1")
2 -> AlgSpec.EC("secp384r1")
3 -> AlgSpec.EC("secp521r1")
4 -> AlgSpec.XDH(
"x25519",
byteArrayOf(0x06, 0x03, 0x2b, 0x65, 0x6e), // OID: 1.3.101.110 (X25519)
32
)
5 -> AlgSpec.XDH(
"x448",
byteArrayOf(0x06, 0x03, 0x2b, 0x65, 0x6f), // OID: 1.3.101.111 (X448)
56
)
6 -> AlgSpec.XDH(
"Ed25519",
byteArrayOf(0x06, 0x03, 0x2b, 0x65, 0x70), // OID: 1.3.101.112 (ED25519)
32
)
7 -> AlgSpec.XDH(
"Ed448",
byteArrayOf(0x06, 0x03, 0x2b, 0x65, 0x77), // OID: 1.3.101.113 (ED448)
56
)
else -> null
}
} else {
when (curveId) {
1 -> AlgSpec.EC("secp256r1")
2 -> AlgSpec.EC("secp384r1")
3 -> AlgSpec.EC("secp521r1")
else -> null
}
}
}

fun asCryptoKey(): PublicKey? {
return when(algorithm) {
is EC2Algorithm -> {
val curveName = when (curveId) {
1 -> "secp256r1"
2 -> "secp384r1"
3 -> "secp521r1"
4 -> "x25519"
5 -> "x448"
6 -> "Ed25519"
7 -> "Ed448"
else -> return null
val algSpec = getAlgSpec() ?: return null
when (algSpec) {
is AlgSpec.EC -> {
val parameters = AlgorithmParameters.getInstance("EC")
parameters.init(algSpec.paramSpec)
val parameterSpec = parameters.getParameterSpec(ECParameterSpec::class.java)
val keySpec = ECPublicKeySpec(ECPoint(BigInteger(1, x), BigInteger(1, y)), parameterSpec)
KeyFactory.getInstance("EC").generatePublic(keySpec)
}
is AlgSpec.XDH -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {

Check failure on line 126 in play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/protocol/CoseKey.kt

View workflow job for this annotation

GitHub Actions / Gradle build Debug

Class requires API level 33 (current min is 19): XDH [NewApi]

Check failure on line 126 in play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/protocol/CoseKey.kt

View workflow job for this annotation

GitHub Actions / Gradle build Debug

Class requires API level 33 (current min is 19): XDH [NewApi]
object : PublicKey {
override fun getAlgorithm(): String = algSpec.keyAlg
override fun getFormat(): String = "x.509"
override fun getEncoded(): ByteArray = algSpec.x509Preamble + x
}
} else {
null
}
}

val parameters = AlgorithmParameters.getInstance("EC")
parameters.init(ECGenParameterSpec(curveName))
val parameterSpec = parameters.getParameterSpec(ECParameterSpec::class.java)
val keySpec = ECPublicKeySpec(ECPoint(BigInteger(1, x), BigInteger(1, y)), parameterSpec)
KeyFactory.getInstance("EC").generatePublic(keySpec)
}
else -> null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ package org.microg.gms.fido.core.transport
import android.content.Context
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import android.security.keystore.KeyProperties
import android.util.Log
import androidx.annotation.RequiresApi
import com.google.android.gms.fido.fido2.api.common.*
Expand All @@ -26,7 +25,6 @@ import java.nio.charset.StandardCharsets
import java.security.KeyPairGenerator
import java.security.MessageDigest
import java.security.interfaces.ECPublicKey
import java.security.spec.ECGenParameterSpec
import javax.crypto.Cipher
import javax.crypto.KeyAgreement
import javax.crypto.Mac
Expand Down Expand Up @@ -327,24 +325,15 @@ abstract class TransportHandler(val transport: Transport, val callback: Transpor
return null;
}

val curveName = when (sharedSecretResponse.keyAgreement.curveId) {
1 -> "secp256r1"
2 -> "secp384r1"
3 -> "secp521r1"
4 -> "x25519"
5 -> "x448"
6 -> "Ed25519"
7 -> "Ed448"
else -> return null
}
val algSpec = sharedSecretResponse.keyAgreement.getAlgSpec() ?: return null

// Perform Diffie Hellman key generation
val generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC)
generator.initialize(ECGenParameterSpec(curveName))
val generator = KeyPairGenerator.getInstance(algSpec.keyAlg)
generator.initialize(algSpec.paramSpec)

val myKeyPair = generator.generateKeyPair()
val serverKey = sharedSecretResponse.keyAgreement.asCryptoKey()
val keyAgreement = KeyAgreement.getInstance("ECDH")
val keyAgreement = KeyAgreement.getInstance(algSpec.agreementAlg)
keyAgreement.init(myKeyPair.private)
keyAgreement.doPhase(serverKey, true)

Expand Down
Loading