/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ namespace logins { /// We expose the crypto primitives on the namespace /// Create a new, random, encryption key. [Throws=LoginsApiError] string create_key(); /// Create a "canary" string, which can be used to test if the encryption //key is still valid for the logins data [Throws=LoginsApiError] string create_canary([ByRef]string text, [ByRef]string encryption_key); /// Check that key is still valid using the output of `create_canary`. //`text` much match the text you initially passed to `create_canary()` [Throws=LoginsApiError] boolean check_canary([ByRef]string canary, [ByRef]string text, [ByRef]string encryption_key); /// Utility function to create a StaticKeyManager to be used for the time /// being until support lands for [trait implementation of an UniFFI /// interface](https://mozilla.github.io/uniffi-rs/next/proc_macro/index.html#structs-implementing-traits) /// in UniFFI. KeyManager create_static_key_manager(string key); /// Similar to create_static_key_manager above, create a /// ManagedEncryptorDecryptor by passing in a KeyManager EncryptorDecryptor create_managed_encdec(KeyManager key_manager); /// Create a LoginStore with StaticKeyManager by passing in a db path and a /// static key LoginStore create_login_store_with_static_key_manager(string path, string key); }; /// A login entry from the user, not linked to any database record. /// The add/update APIs input these. dictionary LoginEntry { // login fields string origin; string? http_realm; string? form_action_origin; string username_field; string password_field; // secure login fields string password; string username; }; /// Metadata fields managed internally by the library. /// These are automatically set on `add()` and updated on operations like `touch()` and `update()`. /// Not included in LoginEntry; use `add_with_meta()` when importing records with existing metadata. dictionary LoginMeta { string id; i64 times_used; i64 time_created; i64 time_last_used; i64 time_password_changed; i64? time_last_breach_alert_dismissed; }; /// A login together with record fields, handed over to the store API; ie a login persisted /// elsewhere, useful for migrations dictionary LoginEntryWithMeta { LoginEntry entry; LoginMeta meta; }; /// A bulk insert result entry, returned by `add_many` and `add_many_with_meta` [Enum] interface BulkResultEntry { Success(Login login); Error(string message); }; /// A login stored in the database dictionary Login { // meta fields string id; i64 times_used; i64 time_created; i64 time_last_used; i64 time_password_changed; // breach fields i64? time_last_breach_alert_dismissed; // login fields string origin; string? http_realm; string? form_action_origin; string username_field; string password_field; // secure login fields string password; string username; }; /// Metrics tracking deletion of logins that cannot be decrypted, see `delete_undecryptable_records_for_remote_replacement` /// for more details dictionary LoginsDeletionMetrics { u64 local_deleted; u64 mirror_deleted; }; /// These are the errors returned by our public API. [Error] interface LoginsApiError { /// NSS not initialized. NSSUninitialized(); /// NSS error during authentication NSSAuthenticationError(string reason); /// error during authentication (in PrimaryPasswordAuthenticator) AuthenticationError(string reason); /// authentication has been cancelled. AuthenticationCanceled(); /// The login data supplied is invalid. The reason will indicate what's wrong with it. InvalidRecord(string reason); /// Asking to do something with a guid which doesn't exist. NoSuchRecord(string reason); /// Encryption key is missing. MissingKey(); /// Encryption key is not valid. InvalidKey(); /// encryption failed EncryptionFailed(string reason); /// decryption failed DecryptionFailed(string reason); /// An operation was interrupted at the request of the consuming app. Interrupted(string reason); /// something internal went wrong which doesn't have a public error value /// because the consuming app can not reasonably take any action to resolve it. /// The underlying error will have been logged and reported. /// (ideally would just be `Unexpected`, but that would be a breaking change) UnexpectedLoginsApiError(string reason); }; [Trait, WithForeign] interface EncryptorDecryptor { [Throws=LoginsApiError] bytes encrypt(bytes cleartext); [Throws=LoginsApiError] bytes decrypt(bytes ciphertext); }; [Trait, WithForeign] interface KeyManager { [Throws=LoginsApiError] bytes get_key(); }; interface StaticKeyManager { constructor(string key); }; interface ManagedEncryptorDecryptor { constructor(KeyManager key_manager); }; interface LoginStore { [Throws=LoginsApiError] constructor(string path, EncryptorDecryptor encdec); [Throws=LoginsApiError] Login add(LoginEntry login); [Throws=LoginsApiError] sequence add_many(sequence logins); [Throws=LoginsApiError] Login add_with_meta(LoginEntryWithMeta entry_with_meta); [Throws=LoginsApiError] sequence add_many_with_meta(sequence entries_with_meta); [Throws=LoginsApiError] Login update([ByRef] string id, LoginEntry login); [Throws=LoginsApiError] Login add_or_update(LoginEntry login); [Throws=LoginsApiError] boolean delete([ByRef] string id); [Throws=LoginsApiError, Self=ByArc] sequence delete_many(sequence ids); /// Clear out locally stored logins data /// /// If sync is enabled, then we will try to recover the data on the next sync. /// /// The main reason to call this is when regenerating a new encryption key. /// In that case, there's no reason to keep around the local data since it can't be decrypted. /// Calling `wipe_local` is better than keeping around these un-decryptable logins, since we /// might be able to recover the data via sync. /// /// This is a no-op for freshly created databases, so it's safe to call this whenever a key is /// generated. [Throws=LoginsApiError] void wipe_local(); [Throws=LoginsApiError, Self=ByArc] void reset(); /// The `delete_undecryptable_records_for_remote_replacement` function locally deletes stored logins /// that cannot be decrypted and sets the last sync time to 0 so any existing server records can be downloaded /// and overwrite the locally deleted records. /// /// NB: This function was created to unblock iOS logins users who are unable to sync logins and should not be used /// outside of this use case. [Throws=LoginsApiError, Self=ByArc] LoginsDeletionMetrics delete_undecryptable_records_for_remote_replacement(); [Throws=LoginsApiError] void touch([ByRef] string id); /// Checks multiple logins for password reuse in a single batch operation. /// /// Returns the GUIDs of logins whose passwords match any password in the breach database. /// This is more efficient than calling `is_potentially_vulnerable_password()` repeatedly, /// as it decrypts the breach database only once. [Throws=LoginsApiError] sequence are_potentially_vulnerable_passwords(sequence ids); /// Checks if a login's password matches any password in the local breach database. /// /// Returns true if this login's password appears in the breachesL table, indicating /// that the same password has been breached on a different domain (password reuse). /// This is independent of whether this specific login has been marked as breached. [Throws=LoginsApiError] boolean is_potentially_vulnerable_password([ByRef] string id); /// Records a list of potentially vulnerable passwords in the breach database. /// /// This is used to bulk-populate the breachesL table with known breached passwords, /// typically during import operations or when syncing breach data. /// Passwords are encrypted before storage and duplicates are automatically filtered out. [Throws=LoginsApiError] void record_potentially_vulnerable_passwords(sequence passwords); /// Removes all recorded breaches. [Throws=LoginsApiError] void reset_all_breaches(); /// Stores that the user dismissed the breach alert for a login. [Throws=LoginsApiError] void record_breach_alert_dismissal([ByRef] string id); /// Stores the time at which the user dismissed the breach alert for a login. [Throws=LoginsApiError] void record_breach_alert_dismissal_time([ByRef] string id, i64 timestamp); [Throws=LoginsApiError] boolean is_empty(); [Throws=LoginsApiError] i64 count(); [Throws=LoginsApiError] i64 count_by_origin([ByRef] string origin); [Throws=LoginsApiError] i64 count_by_form_action_origin([ByRef] string form_action_origin); [Throws=LoginsApiError] sequence list(); [Throws=LoginsApiError] sequence get_by_base_domain([ByRef] string base_domain); [Throws=LoginsApiError] boolean has_logins_by_base_domain([ByRef] string base_domain); [Throws=LoginsApiError] Login? find_login_to_update(LoginEntry look); [Throws=LoginsApiError] Login? get([ByRef] string id); /// Run maintenance on the DB /// /// This is intended to be run during idle time and will take steps / to clean up / shrink the /// database. [Throws=LoginsApiError] void run_maintenance(optional RunMaintenanceOptions? options=null); [Self=ByArc] void register_with_sync_manager(); /// Returns a bridged sync engine for Desktop's Sync framework. /// Without this UDL entry the engine is invisible to JS: UniFFI generates /// the XPCOM glue that lets JS call `rustStore.bridgedEngine()`. [Throws=LoginsApiError, Self=ByArc] LoginsBridgedEngine bridged_engine(); [Self=ByArc] void shutdown(); }; /// The Desktop-facing bridged sync engine. The canonical docs are in /// https://searchfox.org/mozilla-central/source/services/interfaces/mozIBridgedSyncEngine.idl /// It's only actually used on Desktop, but it's fine to expose this everywhere. /// NOTE: all timestamps here are milliseconds. interface LoginsBridgedEngine { [Throws=LoginsApiError] i64 last_sync(); [Throws=LoginsApiError] void set_last_sync(i64 last_sync); [Throws=LoginsApiError] string? sync_id(); [Throws=LoginsApiError] string reset_sync_id(); [Throws=LoginsApiError] string ensure_current_sync_id([ByRef]string new_sync_id); [Throws=LoginsApiError] void sync_started(); [Throws=LoginsApiError] void store_incoming(sequence incoming_envelopes_as_json); [Throws=LoginsApiError] sequence apply(); [Throws=LoginsApiError] void set_uploaded(i64 new_timestamp, sequence uploaded_ids); [Throws=LoginsApiError] void sync_finished(); [Throws=LoginsApiError] void reset(); [Throws=LoginsApiError] void wipe(); }; dictionary RunMaintenanceOptions { // Wipe un-decryptable logins. These will hopefully come back on the next sync. boolean delete_undecryptable_records_for_remote_replacement=true; };