mirror of
https://github.com/microg/android_packages_apps_GmsCore
synced 2026-08-06 14:26:05 -04:00
Fido: Fix credential assertion with Chromium-based browsers (add missing public key) (#3545)
* Fido: Fix algorithm from Cbor We were maintaining 2 different list of algorithmInt to Algorithm which led to missing alg (-8). By using EC2Algorithm and RSAAlgorithm enums directly we avoid further missing algs. * Fido: Fix algorithm parameter spec with x25519/x448 The parameter must be retrieved using the NamedParameterSpec cf. https://docs.oracle.com/en/java/javase/25/docs/specs/security/standard-names.html#namedparameterspec * Fix AlgSpec.XDH requiring SDK >= 33 Also use version ints instead of code names for consistency --------- Co-authored-by: Marvin W <git@larma.de>
This commit is contained in:
parent
0b12b1c970
commit
65f04ed083
3 changed files with 96 additions and 49 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -5,19 +5,22 @@
|
|||
|
||||
package org.microg.gms.fido.core.protocol
|
||||
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
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,
|
||||
|
|
@ -40,25 +43,95 @@ class CoseKey(
|
|||
}
|
||||
}
|
||||
|
||||
sealed class AlgSpec(val keyAlg: String, val agreementAlg: String, val paramSpec: AlgorithmParameterSpec) {
|
||||
class EC(algName: String): AlgSpec(
|
||||
"EC",
|
||||
"ECDH",
|
||||
ECGenParameterSpec(algName)
|
||||
)
|
||||
@RequiresApi(33)
|
||||
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 (SDK_INT >= 33) {
|
||||
// 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
|
||||
if (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)
|
||||
} else if (SDK_INT >= 33 && algSpec is AlgSpec.XDH) {
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.*
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue