b22874a1827a
fix: Sign browser passkey assertions consistently (v2.15.10)
- time
- 2026-09-06 14:51 UTC · 1 month ago
- machine
- koh2
- parent
- 2d43b6458f09
↓ peach-android-b22874a1.tar.gzatlas at this save
files changed3 files
VERSION+1 −1app/src/main/java/com/peachpasswords/passkeys/PasskeyAssertion.kt+3 −1app/src/test/java/com/peachpasswords/passkeys/PasskeyAssertionTest.kt+25 −0app/src/main/java/com/peachpasswords/passkeys/PasskeyAssertion.kt+3 −1
@@ -75,7 +75,9 @@7575 put("rawId", passkey.credentialId)7676 put("type", "public-key")7777 put("response", buildJsonObject {78- put("clientDataJSON", b64(clientDataJson))78+ if (clientDataHashOverride == null) {79+ put("clientDataJSON", b64(clientDataJson))80+ }7981 put("authenticatorData", b64(authData))8082 put("signature", b64(signature))8183 put("userHandle", passkey.userHandle)app/src/test/java/com/peachpasswords/passkeys/PasskeyAssertionTest.kt+25 −0
@@ -15,6 +15,7 @@1515 import kotlinx.serialization.json.jsonObject1616 import kotlinx.serialization.json.jsonPrimitive1717 import org.junit.Assert.assertEquals18+import org.junit.Assert.assertFalse1819 import org.junit.Assert.assertNotNull1920 import org.junit.Assert.assertTrue2021 import org.junit.Test@@ -85,6 +86,30 @@8586 verifier.init(false, Ed25519PublicKeyParameters(publicKey, 0))8687 verifier.update(message, 0, message.size)8788 assertTrue(verifier.verifySignature(signature))89+ }90+91+ @Test fun browser_client_data_hash_does_not_emit_conflicting_client_data_json() {92+ val keyPair = KeyPairGenerator.getInstance("EC").apply {93+ initialize(ECGenParameterSpec("secp256r1"))94+ }.generateKeyPair()95+ val clientDataHash = MessageDigest.getInstance("SHA-256")96+ .digest("framework-owned-client-data".toByteArray())97+98+ val result = PasskeyAssertion.build(99+ entry = passkeyEntry(keyPair.private as ECPrivateKey),100+ requestJson = """{"challenge":"Y2hhbGxlbmdl","rpId":"example.com"}""",101+ origin = "https://example.com",102+ clientDataHashOverride = clientDataHash,103+ )104+105+ assertNotNull(result)106+ val payload = Json.parseToJsonElement(result!!.responseJson).jsonObject["response"]!!.jsonObject107+ assertFalse(payload.containsKey("clientDataJSON"))108+ val authData = b64Decode(payload["authenticatorData"]!!.jsonPrimitive.content)109+ val verifier = Signature.getInstance("SHA256withECDSA")110+ verifier.initVerify(keyPair.public as ECPublicKey)111+ verifier.update(authData + clientDataHash)112+ assertTrue(verifier.verify(b64Decode(payload["signature"]!!.jsonPrimitive.content)))88113 }89114 90115 @Test fun returns_null_for_missing_private_key() {