blob: c12e3b488aeb0003823e70076955bd8ce4c2ae3b [file] [log] [blame]
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.credentials.webauthn
import androidx.annotation.RestrictTo
import java.security.MessageDigest
import org.json.JSONArray
import org.json.JSONObject
@RestrictTo(RestrictTo.Scope.LIBRARY)
class AuthenticatorAttestationResponse(
private val requestOptions: PublicKeyCredentialCreationOptions,
private val credentialId: ByteArray,
private val credentialPublicKey: ByteArray,
private val origin: String,
private val up: Boolean,
private val uv: Boolean,
private val be: Boolean,
private val bs: Boolean,
private val packageName: String? = null,
private val clientDataHash: ByteArray? = null,
) : AuthenticatorResponse {
override var clientJson = JSONObject()
var attestationObject: ByteArray
init {
clientJson.put("type", "webauthn.create")
clientJson.put("challenge", WebAuthnUtils.b64Encode(requestOptions.challenge))
clientJson.put("origin", origin)
if (packageName != null) {
clientJson.put("androidPackageName", packageName)
}
attestationObject = defaultAttestationObject()
}
private fun authData(): ByteArray {
val md = MessageDigest.getInstance("SHA-256")
val rpHash = md.digest(requestOptions.rp.id.toByteArray())
var flags: Int = 0
if (up) {
flags = flags or 0x01
}
if (uv) {
flags = flags or 0x04
}
if (be) {
flags = flags or 0x08
}
if (bs) {
flags = flags or 0x10
}
flags = flags or 0x40
val aaguid = ByteArray(16) { 0 }
val credIdLen = byteArrayOf((credentialId.size shr 8).toByte(), credentialId.size.toByte())
val ret =
rpHash +
byteArrayOf(flags.toByte()) +
byteArrayOf(0, 0, 0, 0) +
aaguid +
credIdLen +
credentialId +
credentialPublicKey
return ret
}
internal fun defaultAttestationObject(): ByteArray {
val ao = mutableMapOf<String, Any>()
ao.put("fmt", "none")
ao.put("attStmt", emptyMap<Any, Any>())
ao.put("authData", authData())
return Cbor().encode(ao)
}
override fun json(): JSONObject {
// See AuthenticatorAttestationResponseJSON at
// https://w3c.github.io/webauthn/#ref-for-dom-publickeycredential-tojson
val clientData = clientJson.toString().toByteArray()
val response = JSONObject()
if (clientDataHash == null) {
response.put("clientDataJSON", WebAuthnUtils.b64Encode(clientData))
}
response.put("attestationObject", WebAuthnUtils.b64Encode(attestationObject))
response.put("transports", JSONArray(listOf("internal", "hybrid")))
return response
}
}