| // 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 MAX_NAME_LEN uint32 = 128; |
| /// The maximum length of metadata in a `KeySingleton` or `KeySet`, in bytes. |
| const MAX_METADATA_LEN uint32 = 128; |
| /// The maximum length of an unstructured random `Key`, in bytes. |
| const MAX_KEY_LEN uint32 = 64; |
| |
| /// The maximum number of `Key` objects in a `KeySet`. |
| const MAX_KEYSET_SIZE uint32 = 64; |
| /// Two times the maximum number of `Key` objects in a `KeySet`. |
| const TWICE_MAX_KEYSET_SIZE uint32 = 128; |
| |
| /// Specifies the reason that a `fuchsia.identity.keys` method failed. |
| type Error = strict enum { |
| /// 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 | |
| /// +---------+ |
| /// ``` |
| type SynchronizationState = strict enum { |
| /// 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(struct { |
| properties KeySingletonProperties; |
| }) -> (struct { |
| properties KeySingletonProperties; |
| state SynchronizationState; |
| 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(struct { |
| name string:MAX_NAME_LEN; |
| }) -> (struct { |
| properties KeySingletonProperties; |
| state SynchronizationState; |
| 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(struct { |
| name string:MAX_NAME_LEN; |
| }) -> (struct {}) 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(resource struct { |
| properties KeySetProperties; |
| key_set_to_freeze string:<MAX_NAME_LEN, optional>; |
| key_set server_end:KeySet; |
| }) -> (struct {}) 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(resource struct { |
| name string:MAX_NAME_LEN; |
| key_set server_end:KeySet; |
| }) -> (struct {}) 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(struct { |
| name string:MAX_NAME_LEN; |
| }) -> (struct {}) 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(struct { |
| name string:MAX_NAME_LEN; |
| }) -> (struct {}) 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. |
| type Key = flexible union { |
| /// An unstructured random key of the length specified in |
| /// `KeySingletonProperties.key_length` or `KeySetProperties.key_length`. |
| 1: random_key bytes:MAX_KEY_LEN; |
| }; |
| |
| /// Specifies the properties of a `KeySingleton`. These properties are provided |
| /// when the `KeySingleton` is created and do not change during its lifetime. |
| type KeySingletonProperties = table { |
| /// A name of the `KeySingleton`. Unique within the `KeyManager` that |
| /// supplied it at a single point in time. |
| 1: name string:MAX_NAME_LEN; |
| |
| /// 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: uid uint64; |
| |
| /// Optional metadata associated with the `KeySingleton`. This is supplied |
| /// by the client and is opaque to the account system. |
| 3: metadata bytes:MAX_METADATA_LEN; |
| |
| /// The size of the key material in this `KeySingleton` in bytes. The value |
| /// must be between one and MAX_KEY_LEN inclusive. |
| 4: key_length uint32; |
| }; |
| |
| /// Specifies the properties of a `KeySet`. These properties are provided when |
| /// the `KeySet` is created and do not change during its lifetime. |
| type KeySetProperties = table { |
| /// A name of the `KeySet`. Unique within the `KeyManager` that supplied it |
| /// at a single point in time. |
| 1: name string:MAX_NAME_LEN; |
| |
| /// 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: uid uint64; |
| |
| /// Optional metadata associated with the `KeySet`. This is supplied by the |
| /// client and is opaque to the account system. |
| 3: metadata bytes:MAX_METADATA_LEN; |
| |
| /// The size of the keys in this `KeySet` in bytes. The value must be |
| /// between one and MAX_KEY_LEN inclusive. |
| 4: key_length uint32; |
| |
| /// 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: max_keys uint32; |
| |
| /// 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: automatic_rotation bool; |
| |
| /// 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: manual_rotation bool; |
| }; |
| |
| /// `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() -> (struct { |
| state SynchronizationState; |
| }); |
| |
| /// Returns the `KeySetProperties` for this key set. |
| /// |
| /// These properties are fixed and will not change through the life of the |
| /// key set. |
| GetProperties() -> (struct { |
| properties KeySetProperties; |
| }); |
| |
| /// 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() -> (struct { |
| ids vector<KeyId>:MAX_KEYSET_SIZE; |
| }) 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() -> (struct { |
| ids vector<KeyId>:TWICE_MAX_KEYSET_SIZE; |
| }) 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(struct { |
| id KeyId; |
| }) -> (struct { |
| key Key; |
| state SynchronizationState; |
| }) 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(struct { |
| id KeyId; |
| }) -> (struct {}) 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() -> (struct {}) 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(struct { |
| id KeyId; |
| }) -> (struct {}) 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() -> (struct { |
| id KeyId; |
| }) 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() -> (struct { |
| id KeyId; |
| }) error Error; |
| }; |