// 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 go_package = "./sync_pb"; 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; } }