package com.peachpasswords.vault import android.os.Parcelable import kotlinx.parcelize.Parcelize import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Parcelize @Serializable data class VaultEntry( val id: String, val type: String? = null, val title: String, val username: String? = null, val email: String? = null, val password: String? = null, val url: String? = null, val additionalUrls: List? = null, /** SHA-256 signing-certificate fingerprints bound to native app associations. */ val nativeSignerDigests: Set? = null, val totp: String? = null, val notes: String? = null, val breach: Int? = null, val passwordChangedAt: Long? = null, /** Past passwords, newest first. Captured automatically on password change. */ val passwordHistory: List? = null, val canary: Canary? = null, val createdAt: Long, val modifiedAt: Long, val deletedAt: Long? = null, val tags: List? = null, // MN-5 (verified vs peach-browser `shared/backup.ts`): the browser stores passkey // entries FLAT on the entry — rpId/rpName/credentialId/publicKeyCose/userHandle/ // userName/userDisplayName/algorithm/signCount directly, NOT under a nested object. // The flat fields below use the SAME camelCase names, so they serialize compatibly // with the browser and need no @SerialName. `privateKeyJwk` is preserved as a // compact JSON string internally and emitted as a JSON object in PeachScript; // Android Credential Provider signing still needs a future ceremony implementation. val passkey: PasskeyData? = null, val signCount: Int? = null, val lastUsedAt: Long? = null, val cardholderName: String? = null, val cardNumber: String? = null, val expMonth: String? = null, val expYear: String? = null, val cvc: String? = null, val cardNickname: String? = null, /** * "visa" | "mastercard" | "amex" | "discover" | "unknown" — derivable from cardNumber. * Serialized as "network" to match the browser's card shape so it survives sync (MF-3). */ @SerialName("network") val cardNetwork: String? = null, /** * Last 4 PAN digits. Derived from cardNumber on save (see VaultRepository) and * serialized as "last4" for browser parity — the browser stores this separately * rather than deriving it at render time (MF-3). */ @SerialName("last4") val last4: String? = null, val rpId: String? = null, val rpName: String? = null, val credentialId: String? = null, val publicKeyCose: String? = null, @Serializable(with = JwkStringSerializer::class) val privateKeyJwk: String? = null, val userHandle: String? = null, val userName: String? = null, val userDisplayName: String? = null, val algorithm: Int? = null, val aaguid: String? = null, val backupEligible: Boolean? = null, val backupState: Boolean? = null, val browser: String? = null, val transports: List? = null, ) : Parcelable fun VaultEntry.isLogin() = type == "login" || type == null fun VaultEntry.isNote() = type == "note" fun VaultEntry.isCard() = type == "card" fun VaultEntry.isDeleted() = deletedAt != null fun VaultEntry.hasTOTP() = !totp.isNullOrBlank() fun VaultEntry.hasPasskey() = passkey != null fun VaultEntry.isPasskeyOnly() = hasPasskey() && password.isNullOrBlank() && totp.isNullOrBlank() && email.isNullOrBlank() fun VaultEntry.isBreach() = (breach ?: 0) > 0 fun VaultEntry.domainKey(): String? { val urlStr = url ?: return null return try { val host = java.net.URL(urlStr).host host.removePrefix("www.") } catch (_: Exception) { null } }