BitwardenPasskeyImportTest.kt

4.6 KB · raw

1package com.peachpasswords.vault23import com.peachpasswords.ui.vault.CsvImport4import org.junit.Assert.assertEquals5import org.junit.Assert.assertTrue6import org.junit.Test78class BitwardenPasskeyImportTest {910    private val bitwardenWithPasskey = """11    {12      "encrypted": false,13      "items": [14        {15          "id": "abc-123",16          "type": 1,17          "name": "example.com",18          "login": {19            "username": "[email protected]",20            "password": "hunter2",21            "totp": null,22            "uris": [{"match": null, "uri": "https://example.com/login"}],23            "fido2Credentials": [24              {25                "credentialId": "Y3JlZC1pZA",26                "keyType": "public-key",27                "keyAlgorithm": "ECDSA",28                "keyCurve": "P-256",29                "keyValue": "cHJpdmF0ZS1rZXktc2NhbGFy",30                "rpId": "example.com",31                "rpName": "Example Inc",32                "userHandle": "dXNlci1oYW5kbGU",33                "userName": "[email protected]",34                "userDisplayName": "June",35                "counter": 7,36                "discoverable": true,37                "creationDate": "2025-06-01T00:00:00Z"38              }39            ]40          },41          "notes": null42        }43      ]44    }45    """.trimIndent()4647    private val bitwardenNoPasskey = """48    {49      "encrypted": false,50      "items": [51        {52          "id": "def-456",53          "type": 1,54          "name": "plain.com",55          "login": {56            "username": "user",57            "password": "pass",58            "uris": [{"match": null, "uri": "https://plain.com"}]59          }60        }61      ]62    }63    """.trimIndent()6465    @Test fun detects_bitwarden_json() {66        assertEquals(CsvImport.BITWARDEN_JSON, CsvImport.detectProvider(bitwardenWithPasskey))67    }6869    @Test fun parses_login_with_passkey() {70        val entries = CsvImport.parse(bitwardenWithPasskey, CsvImport.BITWARDEN_JSON)71        assertEquals(1, entries.size)72        val entry = entries[0]73        assertEquals("example.com", entry.title)74        assertEquals("[email protected]", entry.username)75        assertEquals("hunter2", entry.password)76        assertEquals(1, entry.passkeys.size)7778        val pk = entry.passkeys[0]79        assertEquals("Y3JlZC1pZA", pk.credentialId)80        assertEquals("example.com", pk.rpId)81        assertEquals("Example Inc", pk.rpName)82        assertEquals(-7, pk.algorithm)83        assertEquals("dXNlci1oYW5kbGU", pk.userHandle)84        assertEquals("[email protected]", pk.userName)85        assertEquals("June", pk.userDisplayName)86        assertEquals(7, pk.signCount)87        assertTrue(pk.privateKeyJwk.contains("\"kty\":\"EC\""))88        assertTrue(pk.privateKeyJwk.contains("\"d\":\"cHJpdmF0ZS1rZXktc2NhbGFy\""))89    }9091    @Test fun login_without_passkey_has_empty_passkeys() {92        val entries = CsvImport.parse(bitwardenNoPasskey, CsvImport.BITWARDEN_JSON)93        assertEquals(1, entries.size)94        assertTrue(entries[0].passkeys.isEmpty())95    }9697    @Test fun ed25519_passkey_maps_to_algorithm_minus_8() {98        val json = """99        {100          "items": [101            {102              "type": 1,103              "name": "ed.example.com",104              "login": {105                "username": "u",106                "fido2Credentials": [107                  {108                    "credentialId": "Y3JlZA",109                    "keyAlgorithm": "EdDSA",110                    "keyCurve": "Ed25519",111                    "keyValue": "c2VlZA",112                    "rpId": "ed.example.com",113                    "counter": 0114                  }115                ]116              }117            }118          ]119        }120        """.trimIndent()121        val entries = CsvImport.parse(json, CsvImport.BITWARDEN_JSON)122        val pk = entries[0].passkeys[0]123        assertEquals(-8, pk.algorithm)124        assertTrue(pk.privateKeyJwk.contains("\"kty\":\"OKP\""))125    }126127    @Test fun unsupported_algorithm_is_skipped() {128        val json = """129        {130          "items": [131            {132              "type": 1,133              "name": "rsa.example.com",134              "login": {135                "username": "u",136                "fido2Credentials": [137                  {138                    "credentialId": "Y3JlZA",139                    "keyAlgorithm": "RSA",140                    "keyCurve": "",141                    "keyValue": "a2V5",142                    "rpId": "rsa.example.com",143                    "counter": 0144                  }145                ]146              }147            }148          ]149        }150        """.trimIndent()151        val entries = CsvImport.parse(json, CsvImport.BITWARDEN_JSON)152        assertTrue(entries[0].passkeys.isEmpty())153    }154}