blob: 3a0e73c043b4551b62c60e040c90b67183932e5f [file] [log] [blame]
// 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;
};