1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.credentials.webauthn
18 
19 import androidx.annotation.RestrictTo
20 import java.security.MessageDigest
21 import org.json.JSONObject
22 
23 @RestrictTo(RestrictTo.Scope.LIBRARY)
24 class AuthenticatorAssertionResponse(
25     private val requestOptions: PublicKeyCredentialRequestOptions,
26     private val credentialId: ByteArray,
27     private val origin: String,
28     private val up: Boolean,
29     private val uv: Boolean,
30     private val be: Boolean,
31     private val bs: Boolean,
32     private var userHandle: ByteArray,
33     private val packageName: String? = null,
34     private val clientDataHash: ByteArray? = null,
35 ) : AuthenticatorResponse {
36     override var clientJson = JSONObject()
37     var authenticatorData: ByteArray
38     var signature: ByteArray = byteArrayOf()
39 
40     init {
41         clientJson.put("type", "webauthn.get")
42         clientJson.put("challenge", WebAuthnUtils.b64Encode(requestOptions.challenge))
43         clientJson.put("origin", origin)
44         if (packageName != null) {
45             clientJson.put("androidPackageName", packageName)
46         }
47 
48         authenticatorData = defaultAuthenticatorData()
49     }
50 
defaultAuthenticatorDatanull51     fun defaultAuthenticatorData(): ByteArray {
52         val md = MessageDigest.getInstance("SHA-256")
53         val rpHash = md.digest(requestOptions.rpId.toByteArray())
54         var flags: Int = 0
55         if (up) {
56             flags = flags or 0x01
57         }
58         if (uv) {
59             flags = flags or 0x04
60         }
61         if (be) {
62             flags = flags or 0x08
63         }
64         if (bs) {
65             flags = flags or 0x10
66         }
67         val ret = rpHash + byteArrayOf(flags.toByte()) + byteArrayOf(0, 0, 0, 0)
68         return ret
69     }
70 
dataToSignnull71     fun dataToSign(): ByteArray {
72         val md = MessageDigest.getInstance("SHA-256")
73         var hash: ByteArray
74         if (clientDataHash != null) {
75             hash = clientDataHash
76         } else {
77             hash = md.digest(clientJson.toString().toByteArray())
78         }
79 
80         return authenticatorData + hash
81     }
82 
jsonnull83     override fun json(): JSONObject {
84         val clientData = clientJson.toString().toByteArray()
85         val response = JSONObject()
86         if (clientDataHash == null) {
87             response.put("clientDataJSON", WebAuthnUtils.b64Encode(clientData))
88         }
89         response.put("authenticatorData", WebAuthnUtils.b64Encode(authenticatorData))
90         response.put("signature", WebAuthnUtils.b64Encode(signature))
91         response.put("userHandle", WebAuthnUtils.b64Encode(userHandle))
92         return response
93     }
94 }
95