1package com.peachpasswords.vault23import org.junit.Assert.assertEquals4import org.junit.Assert.assertTrue5import org.junit.Test678910111213141516class BrowserPasskeyJwkTest {1718 19 private val browserEntryJson = """20 {21 "id": "e1",22 "type": "login",23 "title": "GitHub",24 "username": "june",25 "createdAt": 1,26 "modifiedAt": 2,27 "passkey": {28 "rpId": "github.com",29 "rpName": "GitHub",30 "credentialId": "Y3JlZA",31 "publicKeyCose": "cG9zZQ",32 "privateKeyJwk": {33 "kty": "EC",34 "crv": "P-256",35 "x": "abc",36 "y": "def",37 "d": "secret"38 },39 "userHandle": "dXNlcg",40 "userName": "june",41 "userDisplayName": "June",42 "algorithm": -7,43 "signCount": 0,44 "aaguid": "",45 "backupEligible": false,46 "backupState": false,47 "createdAt": 148 }49 }50 """.trimIndent()5152 @Test fun `a browser passkey entry decodes instead of throwing on the jwk object`() {53 val entry = VaultCodec.decodeEntryForTest(browserEntryJson)54 assertEquals("GitHub", entry.title)55 val jwk = entry.passkey?.privateKeyJwk56 assertTrue("the jwk should survive as compact json, got $jwk", jwk != null && jwk.contains("\"kty\""))57 assertTrue("the private component must not be lost", jwk!!.contains("secret"))58 }5960 @Test fun `re-encoding writes the jwk back as an object the browser can read`() {61 val entry = VaultCodec.decodeEntryForTest(browserEntryJson)62 val json = VaultCodec.encodeEntryForTest(entry)63 64 assertTrue("expected an object, got: $json", json.contains("\"privateKeyJwk\":{"))65 assertTrue(json.contains("\"kty\":\"EC\""))66 }6768 @Test fun `a jwk already stored as a string still round-trips`() {69 val asString = browserEntryJson.replace(70 Regex("\"privateKeyJwk\": \\{[^}]*\\}"),71 "\"privateKeyJwk\": \"not-json\"",72 )73 val entry = VaultCodec.decodeEntryForTest(asString)74 assertEquals("not-json", entry.passkey?.privateKeyJwk)75 assertTrue(VaultCodec.encodeEntryForTest(entry).contains("\"privateKeyJwk\":\"not-json\""))76 }77}