| // Copyright 2019 The Fuchsia Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | /// Defines protocols to manage and access key material that is consistent | 
 | /// across all devices provisioned with a particular identity. | 
 | /// | 
 | /// Acheiving consistency implies synchonization between devices and this | 
 | /// synchronization is not instantaneous. For some implementations | 
 | /// synchronization may take many minutes, or even longer when devices are | 
 | /// offline. The library provides information about the progress of the | 
 | /// sychronization. | 
 | library fuchsia.identity.keys; | 
 |  | 
 | alias KeyId = uint32; | 
 |  | 
 | /// The maximum length of a `KeySingleton` or `KeySet` name, in bytes. | 
 | const uint32 MAX_NAME_LEN = 128; | 
 | /// The maximum length of metadata in a `KeySingleton` or `KeySet`, in bytes. | 
 | const uint32 MAX_METADATA_LEN = 128; | 
 | /// The maximum length of an unstructured random `Key`, in bytes. | 
 | const uint32 MAX_KEY_LEN = 64; | 
 |  | 
 | /// The maximum number of `Key` objects in a `KeySet`. | 
 | const uint32 MAX_KEYSET_SIZE = 64; | 
 | /// Two times the maximum number of `Key` objects in a `KeySet`. | 
 | const uint32 TWICE_MAX_KEYSET_SIZE = 128; | 
 |  | 
 | /// Specifies the reason that a `fuchsia.identity.keys` method failed. | 
 | enum Error { | 
 |     /// Some other problem occurred that cannot be classified using one of the | 
 |     /// more specific statuses. Retry is optional. | 
 |     UNKNOWN = 1; | 
 |  | 
 |     /// An internal error occurred. This usually indicates a bug within the | 
 |     /// account system itself. Retry is optional. | 
 |     INTERNAL = 2; | 
 |  | 
 |     /// The requested operation is not supported. This generally indicates that | 
 |     /// implementation of a new feature is not yet complete. The request should | 
 |     /// not be retried. | 
 |     UNSUPPORTED_OPERATION = 3; | 
 |  | 
 |     /// The request was malformed in some way, such as using an invalid key | 
 |     /// size. The request should not be retried. | 
 |     INVALID_REQUEST = 4; | 
 |  | 
 |     /// A local resource error occurred such as I/O, FIDL, or memory allocation | 
 |     /// failure. Retry, after a delay, is recommended. | 
 |     RESOURCE = 5; | 
 |  | 
 |     /// The requested key or key set is not present.  The request should not be | 
 |     /// retried. | 
 |     NOT_FOUND = 7; | 
 |  | 
 |     /// The request would require an illegal change to an entry that is in a | 
 |     /// frozen state. | 
 |     FROZEN = 8; | 
 | }; | 
 |  | 
 | /// Summarizes the sychronization between the device's local state and the | 
 | /// master definition for some item (such as a `KeySingleton` or `KeySet`) in an | 
 | /// identity. Depending on the identity and implementation this "master | 
 | /// definition" might take one of many forms such as the device itself, a | 
 | /// server, a blockchain, a transparency directory, or a quorum of devices. | 
 | /// | 
 | /// Synchronization follows the state machine illustrated below. Only `KeySet` | 
 | /// supports the `PENDING_FROZEN_ADDITION`, `PENDING_FREEZE`, and `FROZEN` | 
 | /// states: | 
 | /// ``` | 
 | ///             +------------------------+                 --\ | 
 | ///             |                        |                   | | 
 | ///             |   +----------------+   | [commit           | | 
 | ///             |   | PENDING_FROZEN |   |  success]         | | 
 | ///             |   |   _ADDITION    |---|----------\        \ Present on | 
 | ///             |   +----------------+   |          |        / local device | 
 | ///             |     ^                  | Delete   |        | | 
 | ///             |     | Freeze locally   | locally  |        | | 
 | ///   Add       |     |                  |----------|--\     | | 
 | ///   locally   |  +------------------+  | [commit  |  |     | | 
 | ///   ----------|->| PENDING_ADDITION |  |  fail]   |  |     | | 
 | ///             |  +------------------+  |          |  |     | | 
 | ///             |          |             |          |  |     | | 
 | ///   Add       +----------|-------------+          |  |     | | 
 | ///   remotely             |                        |  |     | | 
 | ///   ----------------+    | [commit success]       |  |     | | 
 | ///                   |    |                        |  |     |  --\ | 
 | ///             +-----|----|------------+           |  |     |    | | 
 | ///             |     v    v            |           |  |     |    | | 
 | ///             |  +-----------------+  |           |  |     |    | | 
 | ///   +----------->|     LIVE        |  |           |  |     |    | | 
 | ///   |         |  +-----------------+  |           |  |     |    | | 
 | ///   |         |         | ^           |           |  |     |    | | 
 | ///   |         |  Freeze | | [commit   |           |  |     |    | | 
 | ///   |         | locally | |  fail]    |           |  |     |    | | 
 | ///   |         |         v |           | Delete    |  |     |    | | 
 | ///   |         |  +-----------------+  | Remotely  |  |     |    | | 
 | ///   |         |  | PENDING_FREEZE  |  |-----------|--+     |    | | 
 | ///   |         |  +-----------------+  |           |  |     |    | | 
 | ///   |         |          |            |           |  |     |    | | 
 | ///   |         |          | [commit    |           |  |     |    | | 
 | ///   |         |          |  success]  |           |  |     |    | | 
 | ///   |         |          v            |           |  |     |    | | 
 | ///   |         |  +-----------------+  |           |  |     |    | | 
 | ///   | +--------->|    FROZEN       |<-------------/  |     |    | | 
 | ///   | |       |  +-----------------+  |              |     |    | | 
 | ///   | |       |    ^                  |              |     |    | | 
 | ///   | |       |    | Freeze remotely  |              |     |    | | 
 | ///   | |       |    |                  |              |     |    | | 
 | ///   | |       +----+------------------+              |     |    | | 
 | ///   | |                    |                         |     |    | | 
 | ///   | | [previous state    | Delete                  |   --/    | | 
 | ///   | |  == FROZEN]        | locally                 |          | | 
 | ///   | |                    v            Delete       |          \ Present | 
 | ///   | +----+      +------------------+  remotely     |          / in master | 
 | ///   |      |      | PENDING_DELETION |---------------+          | definition | 
 | ///   |      ^      +------------------+  [commit      |          | | 
 | ///   |     / \              |             success]    |        --/ | 
 | ///   +----<   >-------------+                         V | 
 | /// [else]  \ /   [commit fail]                   +---------+ | 
 | ///          v                                    | INVALID | | 
 | ///                                               +---------+ | 
 | /// ``` | 
 | enum SynchronizationState { | 
 |     /// The item is present and not frozen on the current device but has not yet | 
 |     /// been committed into the master definition. Items in this state might not | 
 |     /// reach other devices and might become invalid if committing the addition | 
 |     /// fails. | 
 |     PENDING_ADDITION = 1; | 
 |  | 
 |     /// The item is present and frozen on the current device but has not yet | 
 |     /// been committed into the master definition. Items in this state might not | 
 |     /// reach other devices and might become invalid if committing the addition | 
 |     /// fails. If committing the item succeeds it will enter the `FROZEN` state | 
 |     /// directly without passing through `LIVE`. | 
 |     PENDING_FROZEN_ADDITION = 2; | 
 |  | 
 |     /// The item is present and not frozen both on the current device and in the | 
 |     /// master definition of the identity. It should eventually be consistent | 
 |     /// across all devices with access to the identity. | 
 |     LIVE = 3; | 
 |  | 
 |     /// The item has been frozen on the current device but this operation has | 
 |     /// not yet been committed. Items in this state might return to the `LIVE` | 
 |     /// state if committing the freeze fails. | 
 |     PENDING_FREEZE = 4; | 
 |  | 
 |     /// The item is present and frozen both on the current device and in the | 
 |     /// master definition of the identity. Mutations are no longer possible | 
 |     /// but deletions are still allowed. | 
 |     FROZEN = 5; | 
 |  | 
 |     /// The item is present in the master definition of the identity, but has | 
 |     /// been deleted from the current device and this deletion has not yet been | 
 |     /// committed. Items in this state might return to the `LIVE` or `FROZEN` | 
 |     /// state if committing the deletion fails. | 
 |     PENDING_DELETION = 6; | 
 | }; | 
 |  | 
 | /// `KeyManager` provides access to key material that is consistent across all | 
 | /// devices provisioned with the identity that the `KeyManager` was acquired | 
 | /// from, such as a Fuchsia Persona. | 
 | /// | 
 | /// Acheiving consistency implies synchonization between devices and this | 
 | /// synchronization is not instantaneous. For some implementations | 
 | /// synchronization may take many minutes or even longer when devices are | 
 | /// offline. The `KeyManager` is able to report information about the progress | 
 | /// of this synchronization via `SynchonizationState`. | 
 | protocol KeyManager { | 
 |     /// Returns the `KeySingleton` with the name supplied in `properties` or | 
 |     /// creates a new `KeySingleton` if this name does not exist. | 
 |     /// | 
 |     /// This method follows the hanging get pattern and does not return until | 
 |     /// the response would be different than the previous request for the same | 
 |     /// name. Errors are always returned as soon as they are detected. | 
 |     /// | 
 |     /// `properties` The properties of the new `KeySingleton`. | 
 |     /// | 
 |     /// Returns: `properties` The `KeySingletonProperties` supplied at creation, | 
 |     ///                       with uid populated. | 
 |     ///          `state` The `SychronizationState` for the `KeySingleton`. | 
 |     ///          `key` The `Key` for the `KeySingleton`. | 
 |     WatchOrCreateKeySingleton(KeySingletonProperties properties) | 
 |         -> (KeySingletonProperties properties, | 
 |             SynchronizationState state, | 
 |             Key key) error Error; | 
 |  | 
 |     /// Returns the `KeySingleton` with the specified name. | 
 |     /// | 
 |     /// Fails with `NOT_FOUND` if the requested name does not exist. This method | 
 |     /// follows the hanging get pattern and does not return until the response | 
 |     /// would be different than the previous request for the same name. Errors | 
 |     /// are always returned as soon as they are detected. | 
 |     /// | 
 |     /// `name` The name of the requested `KeySingleton`. | 
 |     /// | 
 |     /// Returns: `properties` The `KeySingletonProperties` supplied at creation, | 
 |     ///                       with uid populated. | 
 |     ///          `state` The `SychronizationState` for the `KeySingleton`. | 
 |     ///          `key` The `Key` for the `KeySingleton`. | 
 |     WatchKeySingleton(string:MAX_NAME_LEN name) | 
 |         -> (KeySingletonProperties properties, | 
 |             SynchronizationState state, | 
 |             Key key) error Error; | 
 |  | 
 |     /// Deletes a previously created `KeySingleton`. | 
 |     /// | 
 |     /// Fails with `NOT_FOUND` if the requested name does not exist. This method | 
 |     /// returns once the deletion has been recorded locally and this does not | 
 |     /// guarantee that the deletion will be successfully committed. | 
 |     /// | 
 |     /// `name` The name of the key singleton. | 
 |     DeleteKeySingleton(string:MAX_NAME_LEN name) -> () error Error; | 
 |  | 
 |     /// Connects a channel to the `KeySet` with the name supplied in | 
 |     /// `properties` or creates a new `KeySet` containing a single `Key` if this | 
 |     /// name does not exist. | 
 |     /// | 
 |     /// `properties` The properties of the new `KeySet`. | 
 |     /// `key_set_to_freeze` If this string is the name of another `KeySet` that | 
 |     ///                     is in the `LIVE` state, and if this method causes a | 
 |     ///                     new `KeySet` to be created, the other `KeySet` will | 
 |     ///                     be moved to the `FROZEN` state atomically with the | 
 |     ///                     new `KeySet` reaching the `LIVE` state. | 
 |     /// `key_set` The server end of a `KeySet` channel. | 
 |     GetOrCreateKeySet( | 
 |         KeySetProperties properties, | 
 |         string:MAX_NAME_LEN? key_set_to_freeze, | 
 |         request<KeySet> key_set) | 
 |         -> () error Error; | 
 |  | 
 |     /// Connects a channel to a previously created `KeySet`. | 
 |     /// | 
 |     /// Fails with `NOT_FOUND` if the requested name does not exist. | 
 |     /// | 
 |     /// `name` The name of the key set. | 
 |     /// `key_set` The server end of a `KeySet` channel. | 
 |     GetKeySet(string:MAX_NAME_LEN name, request<KeySet> key_set) | 
 |         -> () error Error; | 
 |  | 
 |     /// Freezes a previously created `KeySet`. | 
 |     /// | 
 |     /// Fails with `NOT_FOUND` if the requested name does not exist.  The method | 
 |     /// returns once the freeze has been recorded locally and this does not | 
 |     /// guarantee that the freeze will be successfully committed. If the | 
 |     /// `KeySet` is already in the `PENDING_FROZEN_ADDITION`, `PENDING_FREEZE`, | 
 |     /// or `FROZEN` state this operation has no effect. | 
 |     /// | 
 |     /// `name` The name of the key set. | 
 |     FreezeKeySet(string:MAX_NAME_LEN name) -> () error Error; | 
 |  | 
 |     /// Deletes a previously created `KeySet`. | 
 |     /// | 
 |     /// Fails with `NOT_FOUND` if the requested name does not exist. Once the | 
 |     /// deletion has been successfully committed any `KeySet` channels referring | 
 |     /// to it will be closed. The method returns once the deletion has been | 
 |     /// recorded locally and this does not guarantee that the deletion will | 
 |     /// be successfully committed. | 
 |     /// | 
 |     /// `name` The name of the key set. | 
 |     DeleteKeySet(string:MAX_NAME_LEN name) -> () error Error; | 
 | }; | 
 |  | 
 | /// Defines the key material in a `KeySingleton` or each member of a `KeySet`. | 
 | /// | 
 | /// Note: Currently all keys are defined as fixed length arrays of random data | 
 | ///       but we use an xunion to potentially allow more structured key types | 
 | ///       in a future version of the API. | 
 | flexible union Key { | 
 |     /// An unstructured random key of the length specified in | 
 |     /// `KeySingletonProperties.key_length` or `KeySetProperties.key_length`. | 
 |     1: bytes:MAX_KEY_LEN random_key; | 
 | }; | 
 |  | 
 | /// Specifies the properties of a `KeySingleton`. These properties are provided | 
 | /// when the `KeySingleton` is created and do not change during its lifetime. | 
 | table KeySingletonProperties { | 
 |     /// A name of the `KeySingleton`. Unique within the `KeyManager` that | 
 |     /// supplied it at a single point in time. | 
 |     1: string:MAX_NAME_LEN name; | 
 |  | 
 |     /// A numeric identifier for the `KeySingleton` that is unique within the | 
 |     /// `KeyManager` that created it. The same name might be reused for | 
 |     /// different key singletons during the lifetime of a `KeyManager` but these | 
 |     /// will have different `uid` values. | 
 |     /// | 
 |     /// This field is set by the account system and must not be supplied in | 
 |     /// `KeyManager.CreateKeySingleton`. | 
 |     2: uint64 uid; | 
 |  | 
 |     /// Optional metadata associated with the `KeySingleton`. This is supplied | 
 |     /// by the client and is opaque to the account system. | 
 |     3: bytes:MAX_METADATA_LEN metadata; | 
 |  | 
 |     /// The size of the key material in this `KeySingleton` in bytes. The value | 
 |     /// must be between one and MAX_KEY_LEN inclusive. | 
 |     4: uint32 key_length; | 
 | }; | 
 |  | 
 | /// Specifies the properties of a `KeySet`. These properties are provided when | 
 | /// the `KeySet` is created and do not change during its lifetime. | 
 | table KeySetProperties { | 
 |     /// A name of the `KeySet`. Unique within the `KeyManager` that supplied it | 
 |     /// at a single point in time. | 
 |     1: string:MAX_NAME_LEN name; | 
 |  | 
 |     /// A numeric identifier for the `KeySet` that is unique within the | 
 |     /// `KeyManager` that created it. The same name might be reused for | 
 |     /// different key sets during the lifetime of a `KeyManager` but these will | 
 |     /// have different `uid` values. | 
 |     /// | 
 |     /// This field is set by the account system and must not be supplied in | 
 |     /// `KeyManager.CreateKeySet`. | 
 |     2: uint64 uid; | 
 |  | 
 |     /// Optional metadata associated with the `KeySet`. This is supplied by the | 
 |     /// client and is opaque to the account system. | 
 |     3: bytes:MAX_METADATA_LEN metadata; | 
 |  | 
 |     /// The size of the keys in this `KeySet` in bytes. The value must be | 
 |     /// between one and MAX_KEY_LEN inclusive. | 
 |     4: uint32 key_length; | 
 |  | 
 |     /// The maximum number of keys within this `KeySet`. Old keys will be | 
 |     /// automatically deleted to prevent this limit being exceeded. The value | 
 |     /// must be between one and MAX_KEYSET_SIZE inclusive. | 
 |     5: uint32 max_keys; | 
 |  | 
 |     /// If true, a new key will be added to this `KeySet` each time a device | 
 |     /// with access to the identity is remotely revoked. | 
 |     /// `automatic_rotation` and `manual_rotation` cannot both be false. | 
 |     6: bool automatic_rotation; | 
 |  | 
 |     /// If true, users of this KeySet may cause new keys to be added by calling | 
 |     /// `KeySet.AddKey`. | 
 |     /// `automatic_rotation` and `manual_rotation` cannot both be false. | 
 |     7: bool manual_rotation; | 
 | }; | 
 |  | 
 | /// `KeySet` provides access to a set of keys that is consistent across devices | 
 | /// provisioned with the identity that it applies to. | 
 | /// | 
 | /// The keys within a key set are typically all used for the same purpose, such | 
 | /// as encrypting a sensitive dataset. A recent key can be used to encrypt new | 
 | /// data but older keys remain available to decrypt older data. | 
 | /// | 
 | /// All keys in the set are of the same size and each is identified by a | 
 | /// monotonically increasing `KeyId` starting at one. A new key is added into | 
 | /// the set on each rotation event. These rotation events may either occur | 
 | /// "automatically" or "manually": | 
 | /// * An automatic rotation occurs when a device's access to the identity is | 
 | ///   remotely revoked (we trust a device that performs its own recovation to | 
 | ///   also securely delete the key material it is holding). The newly added key | 
 | ///   is not supplied to the device whose access was revoked. The account system | 
 | ///   may combine the revocation of multiple devices into a single rotation | 
 | ///   event. | 
 | /// * A manual rotation occurs when a client calls the `AddKey` method. | 
 | /// | 
 | /// Old keys are deleted from the set automatically to avoid exceeding its | 
 | /// maximum size, clients may also delete keys using the `DeleteKey` method. | 
 | /// | 
 | /// Optionally, a client may "mark" specific keys in the `KeySet` to track | 
 | /// its processing of individual keys. The account system will report the | 
 | /// latest key that has been marked on any device. | 
 | /// | 
 | /// A key set may be frozen, after which no further keys will be added and no | 
 | /// further mark operations may be performed. Keys may still be deleted from a | 
 | /// frozen key set. | 
 | protocol KeySet { | 
 |     /// Returns the `SynchronizationState` for the entire `KeySet`. Refer to the | 
 |     /// documentation on `SynchronizationState` for the available states and | 
 |     /// state transitions. | 
 |     /// | 
 |     /// This method follows the hanging get pattern and does not return until | 
 |     /// the response would be different than the previous request. | 
 |     WatchSynchronizationState() -> (SynchronizationState state); | 
 |  | 
 |     /// Returns the `KeySetProperties` for this key set. | 
 |     /// | 
 |     /// These properties are fixed and will not change through the life of the | 
 |     /// key set. | 
 |     GetProperties() -> (KeySetProperties properties); | 
 |  | 
 |     /// Return a vector of the `KeyId` for all keys in the key set with | 
 |     /// `SynchronizationState == LIVE`. | 
 |     /// | 
 |     /// The returned vector is ordered by id. This method follows the hanging | 
 |     /// get pattern and does not return until the response would be different | 
 |     /// than the previous request. | 
 |     WatchSynchronizedIds() -> (vector<KeyId>:MAX_KEYSET_SIZE ids) error Error; | 
 |  | 
 |     /// Return a vector of the `KeyId` for all keys in the key set, whatever | 
 |     /// their `SynchronizationState`. | 
 |     /// | 
 |     /// The returned vector is ordered by id. This method follows the hanging | 
 |     /// get pattern and does not return until the response would be different | 
 |     /// than the previous request. | 
 |     /// | 
 |     /// Note: Worst case this vector will be twice the maximum size of the key | 
 |     ///       set. Consider a full key set that contains N `LIVE` keys. | 
 |     ///       N manual rotations are now performed before the first succeeds. | 
 |     ///       Each rotation moves a key from the `LIVE` state to the | 
 |     ///       `PENDING_DELETION` state and adds a new key in the | 
 |     ///       `PENDING_ADDITION` state. | 
 |     WatchAllIds() -> (vector<KeyId>:TWICE_MAX_KEYSET_SIZE ids) error Error; | 
 |  | 
 |     /// Returns a `Key` from the key set. | 
 |     /// | 
 |     /// Fails with `NOT_FOUND` if the requested key does not exist. This method | 
 |     /// follows the hanging get pattern and does not return until the response | 
 |     /// would be different than the previous request for this `id`. | 
 |     /// | 
 |     /// `id` The `KeyId` of the requested key. | 
 |     /// | 
 |     /// Returns: `key` The requested `Key`. | 
 |     ///          `state` The current `SynchronizationState` of the key. Note | 
 |     ///                  that keys in `PENDING_ADDITION` are not final: they | 
 |     ///                  might never reach other devices and the same `KeyId` | 
 |     ///                  might be reused for a different `Key` in the future. | 
 |     WatchKey(KeyId id) -> (Key key, SynchronizationState state) error Error; | 
 |  | 
 |     /// Deletes a `Key` from the key set. | 
 |     /// | 
 |     /// Fails with `NOT_FOUND` if the requested key does not exist. The method | 
 |     /// returns once the deletion has been recorded locally and this does not | 
 |     /// guarantee that the deletion will be successfully committed. | 
 |     /// | 
 |     /// `id` The `KeyId` of the requested key. | 
 |     DeleteKey(KeyId id) -> () error Error; | 
 |  | 
 |     /// Adds a new `Key` to the key set, i.e. performs a manual key rotation. | 
 |     /// | 
 |     /// The method returns once the addition has been recorded locally and this | 
 |     /// does not guarantee that the addition will be successfully committed. | 
 |     /// | 
 |     /// Fails with `UNSUPPORTED_OPERATION` if `KeySetProperties.manual_rotation` | 
 |     /// is false or with `FROZEN` if the key set has been frozen. | 
 |     AddKey() -> () error Error; | 
 |  | 
 |     /// Performs a "Mark" operation on a key, such that it is eligible to be | 
 |     /// returned by `GetMaxMarkedId`. | 
 |     /// | 
 |     /// The largest `KeyId` on which a mark operation has been performed is | 
 |     /// synchronized across devices. | 
 |     /// | 
 |     /// Fails with `FROZEN` if the key set has been frozen. | 
 |     /// | 
 |     /// `id` The `KeyId` of the requested key. The method will fail with | 
 |     ///      NOT_FOUND if this key does not exist. | 
 |     MarkId(KeyId id) -> () error Error; | 
 |  | 
 |     /// Returns the maximum `KeyId` on which `MarkId` has been called and | 
 |     /// successfully commited. | 
 |     /// | 
 |     /// This method follows the hanging get pattern and does not return until | 
 |     /// the response would be different than the previous request. | 
 |     /// | 
 |     /// Returns: `id` The largest `KeyId` for which a `MarkId` call has been | 
 |     ///               made and successfully committed, or zero if no `MarkId` | 
 |     ///               calls have been committed. | 
 |     WatchMaxCommittedMarkedId() -> (KeyId id) error Error; | 
 |  | 
 |     /// Returns the maximum `KeyId` on which `MarkId` has been called, even if | 
 |     /// this has not yet been successfully committed. | 
 |     /// | 
 |     /// This method follows the hanging get pattern and does not return until | 
 |     /// the response would be different than the previous request. | 
 |     /// | 
 |     /// Returns: `id` The largest `KeyId` for which a `MarkId` call has been | 
 |     ///               made, or zero if no `MarkId` calls have been made. | 
 |     WatchMaxPendingMarkedId() -> (KeyId id) error Error; | 
 | }; |