use serde::{Deserialize, Serialize}; use uuid::Uuid; use zeroize::Zeroize; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum ItemKind { Login, SecureNote, Card, Identity, } /// A stored WebAuthn credential (docs/05 "Passkeys"). #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Zeroize)] pub struct Passkey { pub credential_id: Vec, pub rp_id: String, pub user_handle: Vec, pub user_name: String, /// PKCS#8 DER of the P-256 private key. pub private_key: Vec, pub sign_count: u32, pub created_at: i64, } /// One item. Field-level structure is what makes three-way merges possible (docs/03). #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Item { pub id: Uuid, pub kind: ItemKind, pub title: String, #[serde(default)] pub username: Option, #[serde(default)] pub password: Option, #[serde(default)] pub urls: Vec, /// `otpauth://` URI or a bare base32 secret. #[serde(default)] pub totp: Option, #[serde(default)] pub notes: Option, #[serde(default)] pub passkeys: Vec, #[serde(default)] pub tags: Vec, #[serde(default)] pub favorite: bool, /// Android package names this login may fill (verified via Digital Asset Links when possible). #[serde(default)] pub android_apps: Vec, pub created_at: i64, pub modified_at: i64, } impl Item { pub fn new_login(title: impl Into, now: i64) -> Self { Self { id: Uuid::new_v4(), kind: ItemKind::Login, title: title.into(), username: None, password: None, urls: vec![], totp: None, notes: None, passkeys: vec![], tags: vec![], favorite: false, android_apps: vec![], created_at: now, modified_at: now, } } } impl Drop for Item { fn drop(&mut self) { self.password.zeroize(); self.totp.zeroize(); self.notes.zeroize(); self.passkeys.zeroize(); } }