chromium/components/sync/protocol/password_specifics.proto

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for password data.

// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.

syntax = "proto2";

option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";

option optimize_for = LITE_RUNTIME;

package sync_pb;

import "components/sync/protocol/encryption.proto";

// These are the properties that get serialized into the |encrypted| field of
// PasswordSpecifics. They correspond to fields in
// password_manager::PasswordForm.
//
// Sync unique tag is calculated as
// EscapePath(origin) + "|" +
// EscapePath(username_element) + "|" +
// EscapePath(username_value) + "|" +
// EscapePath(password_element) + "|" +
// EscapePath(signon_realm)
// where '+' is the string concatenation operation. EscapePath escapes a partial
// or complete file/pathname. This includes non-printable, non-7bit, and
// (including space) the following characters ' "#%:<>?[\]^`{|}'. The space
// character is encoded as '%20'.

// A simple example of PasswordSpecificsData, as created by Chrome for a web
// form that the user submitted. E.g. federated credentials or credentials for
// Android apps may have different fields set.
// PasswordSpecificsData {
//   action: "https://example.com/",
//   avatar_url: "",
//   blacklisted: false,
//   date_created: 13305119271948046,
//   date_last_used: 13305119270102691,
//   date_password_modified_windows_epoch_micros: 13305119271949070,
//   display_name: "",
//   federation_url: "",
//   notes: {},
//   origin: "https://rsolomakhin.github.io/autofill/",
//   password_element: "password",
//   password_issues: {},
//   password_value: "pwd",
//   scheme: 0,
//   signon_realm: "https://rsolomakhin.github.io/",
//   times_used: 0,
//   type: 0,
//   username_element: "username",
//   username_value: "Superman"
// }

// All the strings are encoded with UTF-8. URLs are encoded in Punycode.
message PasswordIssues {
  message PasswordIssue {
    // Timestamp set by a client detecting the issue for the first time.
    // Number of microseconds since Windows epoch (1601).
    // This can be unset even if is_muted is set in a few cases in
    // storage (for a time mutes were written without setting this
    // field - fixed starting 2021-11-10).
    optional uint64 date_first_detection_windows_epoch_micros = 1;

    // Whether the issue was muted by user.
    optional bool is_muted = 2;

    // Whether the backend should notify the user about this issue.
    // Set to true if the user hasn't already seen a client notification for
    // this issue (e.g. a leak detection prompt in Chrome). The backend sending
    // notifications does not reset this field. All other sources can write this
    // in both `PasswordSpecificsData` and `PasswordSpecificsMetadata` and do
    // so.
    optional bool trigger_notification_from_backend_on_detection = 3;
  }
  optional PasswordIssue leaked_password_issue = 1;
  optional PasswordIssue reused_password_issue = 2;
  optional PasswordIssue weak_password_issue = 3;
  optional PasswordIssue phished_password_issue = 4;
}

message PasswordSpecificsData {
    // The different types of the saved credential.
  enum Scheme {
    // SCHEME_HTML, the credential represents either a parsed HTML form, or an
    // android credential or a password saved through Credential Management API
    // (https://w3c.github.io/webappsec/specs/credentialmanagement/).
    SCHEME_HTML = 0;
    // SCHEME_BASIC, basic access http authentication.
    SCHEME_BASIC = 1;
    // SCHEME_DIGEST, digest access authentication.
    SCHEME_DIGEST = 2;
    // SCHEME_OTHER, another proxy access authentication.
    SCHEME_OTHER = 3;
    // USERNAME_ONLY, partial credentials saved on Android that contain only
    // username and miss the password.
    USERNAME_ONLY = 4;
  }
  // See the enum above.
  optional int32 scheme = 1;

  // Signon realm stores information on where the saved password was stored, and
  // where it's supposed to be filled again.
  //
  // It can take various formats depending on the exact circumstances where it
  // was recorded. Note that the format is *not* guaranteed to be a valid URL or
  // URI:
  //
  //  * For parsed web forms and normal passwords saved through Credential
  //  Manager
  //    API: <http-scheme>://<url-host>[:<url-port>]/
  //
  //    where
  //      <http-scheme> is one of "http" or "https"
  //      <url-host> is the host for which the password was stored
  //      <url-port> is the option port on the host
  //    The signon realm is a valid URL in this case with an empty path.
  //    Examples:
  //      http://www.example.com/
  //      https://127.0.0.1/
  //      http://www.google.com:8080/
  //      http://192.168.1.254/
  //      https://accounts.google.com/
  //
  //  * For Android apps saved through Autofill with Google:
  //      android://<hash-of-cert>@<package-name>/
  //    where
  //      <hash-of-cert> is the base64 encoded SHA512 of the app's public
  //      certificate <package-name> is the app's package name
  //    Examples:
  //      android://kCyQDzpaoAX2gs-1zdGPKNAeICb8LzRFOxa4NCq0jO8c8d_NFS_q-Y35bU3Nq3GmFV2lLurmNvIZa6YPYZwmWg==@com.pinterest/
  //      android://mNUCvTnoWBkzIhSSkVj-uzAdK42YagmCmyUtPoC6JPmYAN3wKpmTdIRsdJtz6pzNBye8XL7nBbEcx-y9CJeo9A==@com.twitter.android.lite/
  //
  //  * For federated credentials:
  //      federation://<origin_host>/<federation_host>
  //    where
  //      <origin_host> is the host for which the login information was stored
  //      <federation_host> is the host of the federation provider that was
  //        used to sign in
  //    Examples:
  //      federation://www.example.com/accounts.google.com
  //      federation://uk.trustpilot.com/www.facebook.com
  //
  //  * For proxy auth:
  //      <proxy-host>[:<proxy_port>]/<auth-realm>
  //    where
  //      <proxy-host> is the host of the proxy for which the password was
  //      stored
  //      <proxy-port> is the port of the proxy
  //      <auth-realm> is a string provided by the proxy during authentication.
  //      It can contain spaces.
  //    Examples:
  //      proxy2.eq.edu.au:80/MISldap
  //      proxy.det.nsw.edu.au:8080/NSW Department of Education
  //      10.47.2.250:3128/Squid Proxy Server CPUT
  //      default.go2https.com:443/(******Get password from vpnso.com/account/
  //      *****)
  //
  //  * For HTTP basic auth:
  //      <http-scheme>://<url-host>[:<url-port>]/<auth-realm>
  //    where
  //      <http-scheme> is one of "http" or "https"
  //      <url-host> is the host for which the password was stored
  //      <url-port> is the option port on the host
  //      <auth-realm> is a string provided by the host during authentication.
  //      It can contain spaces.
  //    Examples:
  //      http://192.168.1.1/Broadband Router
  //      http://192.168.0.1/TP-LINK Wireless N Router WR841N
  //      http://192.168.1.1/index.htm
  //      https://www.edge.asic.gov.au/ASIC eBusiness
  optional string signon_realm = 2;

  // For parsed web forms and Credential Management API:
  //     url-scheme://url-host[:url-port]/path
  // For Android: "android://<hash of cert>@<package name>/"
  // For proxy/HTTP auth: url-scheme://url-host[:url-port]/path
  optional string origin = 3;

  // Only for web-parsed forms - the action target of the form:
  //     url-scheme://url-host[:url-port]/path
  optional string action = 4;

  // Only for web-parsed forms - the name of the element containing username.
  optional string username_element = 5;

  // For all: the username.
  // For blacklisted forms: <empty>.
  optional string username_value = 6;

  // Only for web-parsed forms - the name of the element containing password.
  optional string password_element = 7;

  // For all: the password.
  // For federated logins and blacklisted forms: <empty>
  optional string password_value = 8;

  // Deprecated: http://crbug.com/413020
  // True if the credential was saved for a HTTPS session with a valid SSL cert.
  // Ignored for Android apps.
  optional bool ssl_valid = 9 [deprecated = true];

  // True for the last credential used for logging in on a given site.
  // Deprecated in M81.
  optional bool preferred = 10 [deprecated = true];

  // Time when the credential was created. Amount of microseconds since 1601.
  optional int64 date_created = 11;

  // True, if user chose permanently not to save the credentials for the form.
  optional bool blacklisted = 12;

  // kFormSubmission(0), user manually filled the username and the password
  // in the form.
  // kGenerated(1), the credential was auto generated.
  // kApi(2), the credential was generated from Credential Management API.
  // kManuallyAdded(3), user manually created the password credential
  // via Settings.
  // kImported(4), the credential was imported using the import flow.
  // kReceivedViaSharing(5), the credential has been received via the password
  // sharing feature.
  optional int32 type = 13;

  // Number of times this login was used for logging in using an HTML form.
  // Chrome uses this field to distinguish log-in and sign-up forms.
  optional int32 times_used = 14;

  // A human readable name of the account holder. Set by CredentialManager API
  // and Android.
  optional string display_name = 15;

  // A URL of the avatar for the credential. Set by CredentialManager API and
  // Android.
  optional string avatar_url = 16;

  // A URL of the IdP used to verify the credential. Set by Credential Manager
  // API and Android.
  optional string federation_url = 17;

  // Time when the credential was last used. This covers *successful* logins to
  // the website, and explicit updates to the password. It does *not* cover if
  // the password just gets filled but not actually submitted, or if the login
  // failed.
  // Note that password consumers other than Chrome (e.g. Google Play Services)
  // might not update this at all.
  // Amount of microseconds since 1601, aka Windows epoch.
  optional int64 date_last_used = 18;

  // Set if an issue was detected that puts this password at risk. All the
  // clients are expected to clear the field when the password value is updated.
  // 'reused' part can be additionally reset when the analysis on the entire
  // password store is completed.
  optional PasswordIssues password_issues = 19;

  // Time when the |password_value| was last modified. For new credentials it
  // should be set to |date_created|. For subsequent updates the timestamp is
  // changed if and only if the new password value was saved.
  // Number of microseconds since Windows epoch (1601).
  optional int64 date_password_modified_windows_epoch_micros = 20;

  message Notes {
    message Note {
      // The display name must be unique within the scope of a password.
      optional string unique_display_name = 1;
      // The user-defined value of the note.
      optional string value = 2;
      // The creation time of the note. Number of microseconds since 1601.
      optional int64 date_created_windows_epoch_micros = 3;
      // Whether the value of the note is not displayed in plain text by
      // default.
      optional bool hide_by_default = 4;
    }
    repeated Note note = 1;
  }
  reserved 21;
  // Set of extra notes that the user attached to the password. The presence of
  // this field, even with an empty Notes message, becomes the authoritative
  // value for notes and would disregard whatever `encrypted_notes_backup`
  // contains.
  optional Notes notes = 22;

  // For credentials that have been shared by another user, this field captures
  // the sender email. It's empty for credentials that weren't received via
  // password sharing feature.
  optional string sender_email = 23;

  // Similar to `sender_email` but for the sender name.
  optional string sender_name = 24;

  // The timestamp when the password was received via sharing feature from
  // another user.
  optional int64 date_received_windows_epoch_micros = 25;

  // Whether the user has been already notified that they received this password
  // from another user via the password sharing feature. This is synced to avoid
  // showing the notification on multiple devices.
  optional bool sharing_notification_displayed = 26;

  // Similar to `sender_email` but for the url of the sender profile image.
  optional string sender_profile_image_url = 27;
}

// Contains the password specifics metadata which simplifies its lookup.
message PasswordSpecificsMetadata {
  // The signon realm for the credential. For more details, see the
  // `signon_realm` field in PasswordSpecificsData.
  optional string url = 1;

  // True, if user chose permanently not to save the credentials for the form.
  // Introduced in M82. Copy from PasswordSpecificsData.blacklisted.
  optional bool blacklisted = 2;

  // Copy from PasswordSpecificsData.date_last_used.
  // Introduced in M112.
  optional int64 date_last_used_windows_epoch_micros = 3;

  // Copy from PasswordSpecificsData.password_issues. Introduced in M114.
  optional PasswordIssues password_issues = 4;

  // Copy from PasswordSpecificsData.type. Introduced in M119.
  optional int32 type = 13;
}

// Properties of password sync objects.
message PasswordSpecifics {
  // The actual password data. Contains an encrypted PasswordSpecificsData
  // message.
  optional EncryptedData encrypted = 1;
  // An unsynced field for use internally on the client. This field should
  // never be set in any network-based communications because it contains
  // unencrypted material.
  optional PasswordSpecificsData client_only_encrypted_data = 2;
  // Password related metadata, which is sent to the server side. The field
  // should never be set for full encryption users. If encryption is enabled,
  // this field must be cleared.
  optional PasswordSpecificsMetadata unencrypted_metadata = 3;
  reserved 4;
  // An encrypted backup of the notes field inside the PasswordSpecificsData.
  // The Sync server preserves the contents of this field across commits from
  // legacy clients that don't set this field. It is the responsibility of Sync
  // clients to populate the contents of PasswordSpecificsData notes fields
  // using the contents of this field. This should be deprecated together with
  // the logic for preserving it on the server when clients without support for
  // the |notes| field are no longer allowed by the server (below support
  // version horizon).
  //
  // Encryption key considerations:
  // a) For commits, the client must use the same key for both encrypted blobs.
  // b) For handling getupdates, the two keys may NOT necessarily match the
  //    encryption key used, as in theory the new blob could be "behind" if key
  //    rotation took place. As of today, it is safe to assume that if
  //    |encrypted| is decryptable by a client, then |encrypted_notes_backup|
  //    must be decryptable too (i.e. the Nigori keybag should include older
  //    versions of the key). But not the other way round.
  //
  // If both `encrypted_notes_backup` and the `notes` in `encrypted` are
  // populated, the one in notes is considered the authoritative value.
  optional EncryptedData encrypted_notes_backup = 5;
}