chromium/components/sync/protocol/webauthn_credential_specifics.proto

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// 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;

// WebauthnCredentialSpecifics is an entity that backs a WebAuthn
// PublicKeyCredential. Since it contains the authenticator’s view of this
// object, it has a private key rather than a public key.
// (https://www.w3.org/TR/webauthn-2/#iface-pkcredential).
//
// Names of fields are taken from WebAuthn where possible. E.g.
// user.displayName in WebAuthn becomes user_display_name here.
//
// All fields are immutable after creation except for user_name and
// user_display_name, which may be updated by a user.
message WebauthnCredentialSpecifics {
  // Sync's ID for this entity (sometimes called the client unique tag), 16
  // random bytes. This value is used within Sync to identify this entity. The
  // credential ID is not used because the (hashed) sync_id is exposed to the
  // Sync server, and we don’t want Google to be able to map a credential ID to
  // an account. Password entities construct this value from the concatenation
  // of many fields and depend on the fact that the server only sees a hash of
  // it. But the only high-entropy secret here is the private key, which will
  // have different encryption in the future, and private keys are not the sort
  // of data to copy into other fields. Therefore this independent value is
  // provided to form the client's ID.
  optional bytes sync_id = 1;

  // The credential ID, 16 random bytes. This is a value surfaced in
  // the WebAuthn API (https://www.w3.org/TR/webauthn-2/#credential-id).
  optional bytes credential_id = 2;

  // An RP ID is a WebAuthn concept:
  // https://www.w3.org/TR/webauthn-2/#rp-id. It’s usually a domain name,
  // although in non-Web contexts it can be a URL with a non-Web scheme.
  optional string rp_id = 3;

  // The user ID, which is also called a “user handle” in WebAuthn
  // (https://www.w3.org/TR/webauthn-2/#user-handle), is an RP-specific
  // identifier that is up to 64-bytes long. An authenticator conceptually only
  // stores a single credential for a given (rp_id, user_id) pair, but there
  // may be several credentials in Sync. They are prioritised using
  // newly_shadowed_credential_ids and creation_time. See below.
  //
  // (We wish to be able to retain several entities for a single (rp_id,
  // user_id) pair because there’s an edge case where we may wish to revert to
  // an older entry and thus need to keep the older entry around in Sync. The
  // revert could happen on a different device too.)
  optional bytes user_id = 4;

  // The id of credentials with the same (rp_id, user_id) that were
  // shadowed by the creation of this entity.
  //
  // A credential is shadowed if one or more other credentials (from the same
  // account, and with the same (rp_id, user_id)) include its credential_id in
  // their list of shadowed IDs. Shadowed credentials are ignored when finding
  // a credential to sign with. If there is more than one candidate remaining
  // after filtering shadowed credentials then the most recently created (based
  // on creation_time) is used.
  //
  // The reason for all this is that sites can replace a credential by creating
  // another one with the same (rp_id, user_id) pair. However, we don't
  // immediately know whether the WebAuthn response reached the website's
  // server. Consider a user with a poor internet connection. Javascript in the
  // site's origin triggers a credential creation that “overwrites” an existing
  // credential, but the Javascript is unable to send the new public key to the
  // website's server. The user is now locked out: the old credential has been
  // over-written but the website's server doesn't know about the new one.
  //
  // Thus we wish to keep “overwritten” credentials around for a while to allow
  // for some sort of recovery. In the simple case, a new credential shadows
  // the single, previous old credential. We could depend on creation_time, but
  // client clocks aren't always accurate, thus this field.
  //
  // In complicated cases two devices might race to replace a credential, in
  // which case (after mutual syncing) two candidate credentials exist for the
  // same (rp_id, user_id) pair because neither shadows the other. In this case
  // we pick the newest based on |creation_time| but it's quite possible that
  // some recovery will be needed because the website's server thinks the other
  // one is correct.
  //
  // A generation counter isn't used because a single device might replace a
  // series of credentials as it tries to update the website's server. But that
  // doesn't mean that it should dominate a different device that replaced it
  // only once, but later.
  repeated bytes newly_shadowed_credential_ids = 5;

  // The local time on the device when this credential was created. Given in
  // milliseconds since the UNIX epoch. This is used to break ties between
  // credentials. See newly_shadowed_credential_ids.
  optional int64 creation_time = 6;

  // The human-readable account identifier. Usually an email address. This is
  // mutable.
  // https://www.w3.org/TR/webauthn-2/#dom-publickeycredentialentity-name
  optional string user_name = 7;

  // The human-readable name. Usually a legal name. This is mutable.
  // https://www.w3.org/TR/webauthn-2/#dom-publickeycredentialuserentity-displayname.
  optional string user_display_name = 8;

  // Reserving obsolete field.
  reserved 10;

  // Credentials may optionally be enabled for Secure Payment Confirmation[1] on
  // third-party sites. This is opt-in at creation time.
  //
  // [1] https://www.w3.org/TR/secure-payment-confirmation/
  optional bool third_party_payments_support = 11;

  // Time when this passkey was last successfully asserted. Number of
  // microseconds since 1601, aka Windows epoch. This mirrors the
  // `date_last_used` field in PasswordSpecificsData.
  optional int64 last_used_time_windows_epoch_micros = 13;

  message Encrypted {
    // The bytes of the private key, in PKCS#8 format.
    optional bytes private_key = 1;

    // The secret for implementing the hmac-secret extension
    // https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#sctn-hmac-secret-extension
    optional bytes hmac_secret = 2;

    // The contents of the credential's credBlob.
    // https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#sctn-credBlob-extension
    optional bytes cred_blob = 3;

    // The contents of the credential's largeBlob value(*). Unlike with security
    // keys, largeBlob data is not stored in a single lump for all credentials,
    // but as per-credential data. This data is presented to the authenticator
    // over CTAP and thus has already had the required DEFLATE compression
    // applied by the remote platform. The uncompressed size of this data is in
    // the next field.
    //
    // (*) "large" with respect to embedded devices. Maximum length is 2KiB for
    // Google Password Manager.
    //
    // https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#authenticatorLargeBlobs
    optional bytes large_blob = 4;

    // The claimed uncompressed size of the DEFLATE-compressed data in
    // `large_blob`. This corresponds to the `origSize` field from the spec:
    // https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#large-blob
    optional uint64 large_blob_uncompressed_size = 5;
  }

  // The "version" (sometimes called the epoch) of the key that `encrypted_data`
  // is encrypted with. This allows trial decryption to be avoided when present.
  optional int32 key_version = 14;

  oneof encrypted_data {
    // The bytes of the private key, encrypted with a feature-specific security
    // domain.
    bytes private_key = 9;

    // A serialized, `Encrypted` message, encrypted with a feature-specific
    // security domain.
    bytes encrypted = 12;
  }

  // `edited_by_user` is set to `true` the first time a user manually changes
  // the credential's `user_name` or `user_display_name`.
  optional bool edited_by_user = 15;

  // `hidden` is set to `true` if the credential should not be shown on
  // authentication surfaces, e.g. because a site marked it as obsolete.
  optional bool hidden = 16;
}