diff --git a/app/src/main/java/com/kunzisoft/keepass/credentialprovider/passkey/data/FidoDataTypes.kt b/app/src/main/java/com/kunzisoft/keepass/credentialprovider/passkey/data/FidoDataTypes.kt index 62a70a485..0fc2b2a65 100644 --- a/app/src/main/java/com/kunzisoft/keepass/credentialprovider/passkey/data/FidoDataTypes.kt +++ b/app/src/main/java/com/kunzisoft/keepass/credentialprovider/passkey/data/FidoDataTypes.kt @@ -16,7 +16,25 @@ package com.kunzisoft.keepass.credentialprovider.passkey.data -data class PublicKeyCredentialRpEntity(val name: String, val id: String) +import com.kunzisoft.encrypt.Base64Helper +import org.json.JSONObject + +data class PublicKeyCredentialRpEntity( + val name: String, + val id: String +) { + companion object { + fun JSONObject.getPublicKeyCredentialRpEntity( + parameterName: String + ): PublicKeyCredentialRpEntity { + val rpJson = this.getJSONObject(parameterName) + return PublicKeyCredentialRpEntity( + rpJson.getString("name"), + rpJson.getString("id") + ) + } + } +} data class PublicKeyCredentialUserEntity( val name: String, @@ -42,9 +60,41 @@ data class PublicKeyCredentialUserEntity( result = 31 * result + displayName.hashCode() return result } + + companion object { + fun JSONObject.getPublicKeyCredentialUserEntity( + parameterName: String + ): PublicKeyCredentialUserEntity { + val rpUser = this.getJSONObject(parameterName) + return PublicKeyCredentialUserEntity( + rpUser.getString("name"), + Base64Helper.b64Decode(rpUser.getString("id")), + rpUser.getString("displayName") + ) + } + } } -data class PublicKeyCredentialParameters(val type: String, val alg: Long) +data class PublicKeyCredentialParameters( + val type: String, + val alg: Long +) { + companion object { + fun JSONObject.getPublicKeyCredentialParametersList( + parameterName: String + ): List { + val pubKeyCredParamsJson = this.getJSONArray(parameterName) + val pubKeyCredParamsTmp: MutableList = mutableListOf() + for (i in 0 until pubKeyCredParamsJson.length()) { + val e = pubKeyCredParamsJson.getJSONObject(i) + pubKeyCredParamsTmp.add( + PublicKeyCredentialParameters(e.getString("type"), e.getLong("alg")) + ) + } + return pubKeyCredParamsTmp.toList() + } + } +} data class PublicKeyCredentialDescriptor( val type: String, @@ -70,11 +120,97 @@ data class PublicKeyCredentialDescriptor( result = 31 * result + transports.hashCode() return result } + + companion object { + fun JSONObject.getPublicKeyCredentialDescriptorList( + parameterName: String + ): List { + val credentialsJson = this.getJSONArray(parameterName) + val credentialsTmp: MutableList = mutableListOf() + for (i in 0 until credentialsJson.length()) { + val credentialJson = credentialsJson.getJSONObject(i) + + val transports: MutableList = mutableListOf() + val transportsJson = credentialJson.getJSONArray("transports") + for (j in 0 until transportsJson.length()) { + transports.add(transportsJson.getString(j)) + } + credentialsTmp.add( + PublicKeyCredentialDescriptor( + type = credentialJson.getString("type"), + id = Base64Helper.b64Decode(credentialJson.getString("id")), + transports = transports + ) + ) + } + return credentialsTmp.toList() + } + } } data class AuthenticatorSelectionCriteria( - val authenticatorAttachment: String, - val residentKey: String, - val requireResidentKey: Boolean = false, - val userVerification: String = "preferred" -) + val authenticatorAttachment: String? = null, + val residentKey: ResidentKeyRequirement? = null, + val requireResidentKey: Boolean?, + val userVerification: UserVerificationRequirement? = UserVerificationRequirement.PREFERRED +) { + companion object { + fun JSONObject.getAuthenticatorSelectionCriteria( + parameterName: String + ): AuthenticatorSelectionCriteria { + val authenticatorSelection = this.optJSONObject(parameterName) + ?: return AuthenticatorSelectionCriteria(requireResidentKey = null) + val authenticatorAttachment = if (!authenticatorSelection.isNull("authenticatorAttachment")) + authenticatorSelection.getString("authenticatorAttachment") else null + var residentKey = if (!authenticatorSelection.isNull("residentKey")) + ResidentKeyRequirement.fromString(authenticatorSelection.getString("residentKey")) + else null + val requireResidentKey = authenticatorSelection.optBoolean("requireResidentKey", false) + val userVerification = UserVerificationRequirement.fromString(authenticatorSelection.optString("userVerification", "preferred")) + // https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement + if (residentKey == null) { + residentKey = if (requireResidentKey) { + ResidentKeyRequirement.REQUIRED + } else { + ResidentKeyRequirement.DISCOURAGED + } + } + return AuthenticatorSelectionCriteria( + authenticatorAttachment = authenticatorAttachment, + residentKey = residentKey, + requireResidentKey = requireResidentKey, + userVerification = userVerification + ) + } + } +} + +// https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement +enum class ResidentKeyRequirement(val value: String) { + DISCOURAGED("discouraged"), + PREFERRED("preferred"), + REQUIRED("required"); + override fun toString(): String { + return value + } + companion object { + fun fromString(value: String): ResidentKeyRequirement? { + return ResidentKeyRequirement.entries.firstOrNull { it.value == value } + } + } +} + +// https://www.w3.org/TR/webauthn-3/#enumdef-userverificationrequirement +enum class UserVerificationRequirement(val value: String) { + REQUIRED("required"), + PREFERRED("preferred"), + DISCOURAGED("discouraged"); + override fun toString(): String { + return value + } + companion object { + fun fromString(value: String): UserVerificationRequirement? { + return UserVerificationRequirement.entries.firstOrNull { it.value == value } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/credentialprovider/passkey/data/PublicKeyCredentialCreationOptions.kt b/app/src/main/java/com/kunzisoft/keepass/credentialprovider/passkey/data/PublicKeyCredentialCreationOptions.kt index 75c19b626..67dc3a9ac 100644 --- a/app/src/main/java/com/kunzisoft/keepass/credentialprovider/passkey/data/PublicKeyCredentialCreationOptions.kt +++ b/app/src/main/java/com/kunzisoft/keepass/credentialprovider/passkey/data/PublicKeyCredentialCreationOptions.kt @@ -20,51 +20,42 @@ package com.kunzisoft.keepass.credentialprovider.passkey.data import com.kunzisoft.encrypt.Base64Helper +import com.kunzisoft.keepass.credentialprovider.passkey.data.AuthenticatorSelectionCriteria.Companion.getAuthenticatorSelectionCriteria +import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialDescriptor.Companion.getPublicKeyCredentialDescriptorList +import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialParameters.Companion.getPublicKeyCredentialParametersList +import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialRpEntity.Companion.getPublicKeyCredentialRpEntity +import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialUserEntity.Companion.getPublicKeyCredentialUserEntity import org.json.JSONObject class PublicKeyCredentialCreationOptions( requestJson: String, var clientDataHash: ByteArray? ) { - val json: JSONObject = JSONObject(requestJson) + private val json: JSONObject = JSONObject(requestJson) - val relyingPartyEntity: PublicKeyCredentialRpEntity - val userEntity: PublicKeyCredentialUserEntity - val challenge: ByteArray - val pubKeyCredParams: List + val relyingPartyEntity: PublicKeyCredentialRpEntity = + json.getPublicKeyCredentialRpEntity("rp") - var timeout: Long - var excludeCredentials: List - var authenticatorSelection: AuthenticatorSelectionCriteria - var attestation: String + val userEntity: PublicKeyCredentialUserEntity = + json.getPublicKeyCredentialUserEntity("user") - init { - val rpJson = json.getJSONObject("rp") - relyingPartyEntity = PublicKeyCredentialRpEntity(rpJson.getString("name"), rpJson.getString("id")) - val rpUser = json.getJSONObject("user") - userEntity = - PublicKeyCredentialUserEntity( - rpUser.getString("name"), - Base64Helper.b64Decode(rpUser.getString("id")), - rpUser.getString("displayName") - ) - challenge = Base64Helper.b64Decode(json.getString("challenge")) - val pubKeyCredParamsJson = json.getJSONArray("pubKeyCredParams") - val pubKeyCredParamsTmp: MutableList = mutableListOf() - for (i in 0 until pubKeyCredParamsJson.length()) { - val e = pubKeyCredParamsJson.getJSONObject(i) - pubKeyCredParamsTmp.add( - PublicKeyCredentialParameters(e.getString("type"), e.getLong("alg")) - ) - } - pubKeyCredParams = pubKeyCredParamsTmp.toList() + val challenge: ByteArray = + Base64Helper.b64Decode(json.getString("challenge")) - timeout = json.optLong("timeout", 0) - // TODO: Fix excludeCredentials and authenticatorSelection - excludeCredentials = emptyList() - authenticatorSelection = AuthenticatorSelectionCriteria("platform", "required") - attestation = json.optString("attestation", "none") - } + val pubKeyCredParams: List = + json.getPublicKeyCredentialParametersList("pubKeyCredParams") + + var timeout: Long = + json.optLong("timeout", 0) + + var excludeCredentials: List = + json.getPublicKeyCredentialDescriptorList("excludeCredentials") + + var authenticatorSelection: AuthenticatorSelectionCriteria = + json.getAuthenticatorSelectionCriteria("authenticatorSelection") + + var attestation: String = + json.optString("attestation", "none") companion object { private val TAG = PublicKeyCredentialCreationOptions::class.simpleName diff --git a/app/src/main/java/com/kunzisoft/keepass/credentialprovider/passkey/data/PublicKeyCredentialRequestOptions.kt b/app/src/main/java/com/kunzisoft/keepass/credentialprovider/passkey/data/PublicKeyCredentialRequestOptions.kt index 99a31caea..f3e295eaa 100644 --- a/app/src/main/java/com/kunzisoft/keepass/credentialprovider/passkey/data/PublicKeyCredentialRequestOptions.kt +++ b/app/src/main/java/com/kunzisoft/keepass/credentialprovider/passkey/data/PublicKeyCredentialRequestOptions.kt @@ -20,35 +20,33 @@ package com.kunzisoft.keepass.credentialprovider.passkey.data import com.kunzisoft.encrypt.Base64Helper +import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialDescriptor.Companion.getPublicKeyCredentialDescriptorList import org.json.JSONObject +// https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement class PublicKeyCredentialRequestOptions(requestJson: String) { private val json: JSONObject = JSONObject(requestJson) - val challenge: ByteArray = Base64Helper.b64Decode(json.getString("challenge")) - val timeout: Long = json.optLong("timeout", 0) - val rpId: String = json.optString("rpId", "") - val allowCredentials: List - val userVerification: String = json.optString("userVerification", "preferred") - init { - val allowCredentialsJson = json.getJSONArray("allowCredentials") - val allowCredentialsTmp: MutableList = mutableListOf() - for (i in 0 until allowCredentialsJson.length()) { - val allowCredentialJson = allowCredentialsJson.getJSONObject(i) + val challenge: ByteArray = + Base64Helper.b64Decode(json.getString("challenge")) - val transports: MutableList = mutableListOf() - val transportsJson = allowCredentialJson.getJSONArray("transports") - for (j in 0 until transportsJson.length()) { - transports.add(transportsJson.getString(j)) - } - allowCredentialsTmp.add( - PublicKeyCredentialDescriptor( - type = allowCredentialJson.getString("type"), - id = Base64Helper.b64Decode(allowCredentialJson.getString("id")), - transports = transports - ) - ) - } - allowCredentials = allowCredentialsTmp.toList() - } + val timeout: Long = + json.optLong("timeout", 0) + + val rpId: String = + json.optString("rpId", "") + + val allowCredentials: List = + json.getPublicKeyCredentialDescriptorList("allowCredentials") + + val userVerification: UserVerificationRequirement = + UserVerificationRequirement.fromString( + json.optString("userVerification", "preferred")) + ?: UserVerificationRequirement.PREFERRED + + // TODO Hints + val hints: List = listOf() + + // TODO Extensions + // val extensions: AuthenticationExtensionsClientInputs } \ No newline at end of file