You've already forked FIDO2_Bridge
mirror of
https://github.com/token2/FIDO2_Bridge.git
synced 2026-03-13 11:12:26 -07:00
Refactor authData parsing into AuthenticatorData structure
This commit is contained in:
@@ -5,6 +5,79 @@ data class AlgorithmInfo(
|
||||
val alg: Int?
|
||||
)
|
||||
|
||||
data class AttestedCredentialData(
|
||||
val aaguid: ByteArray,
|
||||
val credentialId: ByteArray,
|
||||
val credentialPublicKey: Map<*, *>
|
||||
) {
|
||||
val publicKeyAlgorithm: Int?
|
||||
get() = (credentialPublicKey[3L] as? Number)?.toInt()
|
||||
|
||||
val keyType: Int?
|
||||
get() = (credentialPublicKey[1L] as? Number)?.toInt()
|
||||
|
||||
val curve: Int?
|
||||
get() = (credentialPublicKey[-1L] as? Number)?.toInt()
|
||||
}
|
||||
|
||||
data class AuthenticatorData(
|
||||
val rpIdHash: ByteArray,
|
||||
val flags: Int,
|
||||
val signCount: Long,
|
||||
val attestedCredentialData: AttestedCredentialData?
|
||||
) {
|
||||
val userPresent: Boolean
|
||||
get() = (flags and CTAP.AUTH_DATA_FLAG_UP) != 0
|
||||
|
||||
val userVerified: Boolean
|
||||
get() = (flags and CTAP.AUTH_DATA_FLAG_UV) != 0
|
||||
|
||||
val hasAttestedCredentialData: Boolean
|
||||
get() = (flags and CTAP.AUTH_DATA_FLAG_AT) != 0
|
||||
|
||||
val hasExtensions: Boolean
|
||||
get() = (flags and CTAP.AUTH_DATA_FLAG_ED) != 0
|
||||
|
||||
companion object {
|
||||
fun parse(data: ByteArray): AuthenticatorData? {
|
||||
if (data.size < 37) return null
|
||||
|
||||
val rpIdHash = data.sliceArray(0 until 32)
|
||||
val flags = data[32].toInt() and 0xFF
|
||||
val signCount = ((data[33].toLong() and 0xFF) shl 24) or
|
||||
((data[34].toLong() and 0xFF) shl 16) or
|
||||
((data[35].toLong() and 0xFF) shl 8) or
|
||||
(data[36].toLong() and 0xFF)
|
||||
|
||||
var offset = 37
|
||||
val hasAt = (flags and CTAP.AUTH_DATA_FLAG_AT) != 0
|
||||
|
||||
val attestedCredentialData = if (hasAt) {
|
||||
if (data.size < offset + 18) return null
|
||||
|
||||
val aaguid = data.sliceArray(offset until offset + 16)
|
||||
offset += 16
|
||||
|
||||
val credIdLen = ((data[offset].toInt() and 0xFF) shl 8) or
|
||||
(data[offset + 1].toInt() and 0xFF)
|
||||
offset += 2
|
||||
|
||||
if (data.size < offset + credIdLen) return null
|
||||
val credentialId = data.sliceArray(offset until offset + credIdLen)
|
||||
offset += credIdLen
|
||||
|
||||
val credentialPublicKey = CborDecoder.decode(
|
||||
data.sliceArray(offset until data.size)
|
||||
) as? Map<*, *> ?: return null
|
||||
|
||||
AttestedCredentialData(aaguid, credentialId, credentialPublicKey)
|
||||
} else null
|
||||
|
||||
return AuthenticatorData(rpIdHash, flags, signCount, attestedCredentialData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class DeviceInfo(
|
||||
val versions: List<String> = emptyList(),
|
||||
val extensions: List<String> = emptyList(),
|
||||
|
||||
@@ -641,8 +641,9 @@ class CredentialProviderActivity : AppCompatActivity() {
|
||||
makeCredResult.authData
|
||||
)
|
||||
|
||||
// Extract credential ID from authData
|
||||
val credentialId = extractCredentialIdFromAuthData(makeCredResult.authData)
|
||||
// Parse authData structure
|
||||
val authData = AuthenticatorData.parse(makeCredResult.authData)
|
||||
val credentialId = authData?.attestedCredentialData?.credentialId ?: ByteArray(0)
|
||||
|
||||
// Check if credProps extension was requested
|
||||
val extensions = requestJson.optJSONObject("extensions")
|
||||
@@ -676,14 +677,16 @@ class CredentialProviderActivity : AppCompatActivity() {
|
||||
makeCredResult.authData,
|
||||
Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP
|
||||
))
|
||||
extractPublicKeyAlgorithm(makeCredResult.authData)?.let { alg ->
|
||||
put("publicKeyAlgorithm", alg)
|
||||
}
|
||||
extractPublicKeySpki(makeCredResult.authData)?.let { spki ->
|
||||
put("publicKey", Base64.encodeToString(
|
||||
spki,
|
||||
Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP
|
||||
))
|
||||
authData?.attestedCredentialData?.let { attCredData ->
|
||||
attCredData.publicKeyAlgorithm?.let { alg ->
|
||||
put("publicKeyAlgorithm", alg)
|
||||
}
|
||||
encodePublicKeySpki(attCredData)?.let { spki ->
|
||||
put("publicKey", Base64.encodeToString(
|
||||
spki,
|
||||
Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP
|
||||
))
|
||||
}
|
||||
}
|
||||
})
|
||||
put("clientExtensionResults", JSONObject().apply {
|
||||
@@ -980,41 +983,10 @@ class CredentialProviderActivity : AppCompatActivity() {
|
||||
return output
|
||||
}
|
||||
|
||||
private fun extractCredentialIdFromAuthData(authData: ByteArray): ByteArray {
|
||||
// authData structure:
|
||||
// rpIdHash (32 bytes) + flags (1 byte) + signCount (4 bytes) + attestedCredentialData
|
||||
// attestedCredentialData: aaguid (16 bytes) + credentialIdLength (2 bytes) + credentialId + publicKey
|
||||
|
||||
if (authData.size < 55) {
|
||||
return ByteArray(0)
|
||||
}
|
||||
|
||||
val flags = authData[32].toInt() and 0xFF
|
||||
val hasAttestedCredData = (flags and CTAP.AUTH_DATA_FLAG_AT) != 0
|
||||
|
||||
if (!hasAttestedCredData) {
|
||||
return ByteArray(0)
|
||||
}
|
||||
|
||||
// Skip rpIdHash (32) + flags (1) + signCount (4) + aaguid (16)
|
||||
val credIdLengthOffset = 32 + 1 + 4 + 16
|
||||
val credIdLength = ((authData[credIdLengthOffset].toInt() and 0xFF) shl 8) or
|
||||
(authData[credIdLengthOffset + 1].toInt() and 0xFF)
|
||||
|
||||
val credIdOffset = credIdLengthOffset + 2
|
||||
return authData.sliceArray(credIdOffset until credIdOffset + credIdLength)
|
||||
}
|
||||
|
||||
private fun extractPublicKeyAlgorithm(authData: ByteArray): Int? {
|
||||
val coseKey = extractCoseKeyFromAuthData(authData) ?: return null
|
||||
return (coseKey[3L] as? Number)?.toInt()
|
||||
}
|
||||
|
||||
private fun extractPublicKeySpki(authData: ByteArray): ByteArray? {
|
||||
val coseKey = extractCoseKeyFromAuthData(authData) ?: return null
|
||||
|
||||
val kty = (coseKey[1L] as? Number)?.toInt() ?: return null
|
||||
val crv = (coseKey[-1L] as? Number)?.toInt() ?: return null
|
||||
private fun encodePublicKeySpki(attCredData: AttestedCredentialData): ByteArray? {
|
||||
val kty = attCredData.keyType ?: return null
|
||||
val crv = attCredData.curve ?: return null
|
||||
val coseKey = attCredData.credentialPublicKey
|
||||
|
||||
return when (kty) {
|
||||
CTAP.COSE_KTY_EC2 -> encodeEc2KeyAsSpki(coseKey, crv)
|
||||
@@ -1023,20 +995,6 @@ class CredentialProviderActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractCoseKeyFromAuthData(authData: ByteArray): Map<*, *>? {
|
||||
if (authData.size < 55) return null
|
||||
|
||||
val flags = authData[32].toInt() and 0xFF
|
||||
if ((flags and CTAP.AUTH_DATA_FLAG_AT) == 0) return null
|
||||
|
||||
val credIdLenOffset = 32 + 1 + 4 + 16
|
||||
val credIdLen = ((authData[credIdLenOffset].toInt() and 0xFF) shl 8) or
|
||||
(authData[credIdLenOffset + 1].toInt() and 0xFF)
|
||||
|
||||
val coseKeyOffset = credIdLenOffset + 2 + credIdLen
|
||||
return CborDecoder.decode(authData.sliceArray(coseKeyOffset until authData.size)) as? Map<*, *>
|
||||
}
|
||||
|
||||
private fun encodeEc2KeyAsSpki(coseKey: Map<*, *>, crv: Int): ByteArray? {
|
||||
if (crv != CTAP.COSE_CRV_P256) return null
|
||||
|
||||
|
||||
Reference in New Issue
Block a user