| // Copyright 2018 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. |
| |
| library fuchsia.identity.internal; |
| |
| using fuchsia.auth; |
| using fuchsia.identity.account; |
| using fuchsia.identity.authentication; |
| using fuchsia.identity.external; |
| using fuchsia.kms; |
| |
| const uint32 HASH_SIZE = 32; |
| const uint32 HASH_SALT_SIZE = 32; |
| using GlobalIdHash = array<byte>:HASH_SIZE; |
| using HashSalt = array<byte>:HASH_SALT_SIZE; |
| |
| /// The control channel for an AccountHandler component. |
| /// |
| /// This interface is intended only for use by the AccountManager component that |
| /// starts instances of AccountHandler. We define which account the handler |
| /// should be handling one time via this channel rather than via startup flags to |
| /// provide additional flexibility given the range of scenarios: |
| /// * The account is completely new |
| /// * The account is being added to the current device for the first time |
| /// * Account information is already present on the local disk and is readable |
| /// * Account information is already present on the local disk but is not yet |
| /// readable because the disk is not yet decrypted. |
| /// |
| /// A given Account Handler progresses through the following state machine: |
| /// ``` |
| /// | |
| /// V |
| /// +---------------+ |
| /// +--------------| Uninitialized |------------+ |
| /// | +---------------+ | |
| /// | | | |
| /// | PrepareForTransfer | | Preload |
| /// V | V |
| /// +------------------+ | +------------------+ |
| /// | PendingTransfer | | | Locked | |
| /// +------------------+ | +------------------+ |
| /// | | ^ |
| /// | PerformTransfer | | |
| /// V | | |
| /// +------------------+ | | LockAccount / |
| /// | Transferred | | CreateAccount | UnlockAccount |
| /// +------------------+ | | |
| /// | | | |
| /// | FinalizeTransfer | | |
| /// | V | |
| /// | +---------------+ | |
| /// +------------->| Initialized |<-----------+ |
| /// +---------------+ |
| /// | |
| /// | Terminate |
| /// V |
| /// +---------------+ |
| /// | Finished | |
| /// +---------------+ |
| /// ``` |
| /// |
| /// * `Uninitialized` - the handler has just been instantiated and contains no |
| /// account information. |
| /// * `PendingTranfer` - the handler is awaiting an account transfer. It has |
| /// created a public key pair that will be associated with the account on this |
| /// device, but contains no other account information. |
| /// * `Transferred` - the handler has received transferred account information |
| /// and is holding it in memory. In this state, the account transfer is not |
| /// yet known to be valid, and is pending further validation from account |
| /// manager. The account is not available for use, and regardless of the |
| /// intended lifetime of the account on this device, the account is not |
| /// saved to disk. |
| /// * `Locked` - the handler has loaded pre-authentication data, which lets it |
| /// unlock the account subsequently. |
| /// * `Initialized` - the handler has loaded account information and is ready |
| /// to serve requests for the `Account` interface. If the account is |
| /// persistent on this device, then it is saved to disk. |
| /// * `Finished` - the handler is in the process of shutting down and will soon |
| /// terminate. |
| // TODO(42836): Supply UI for system authentication. |
| [Discoverable] |
| protocol AccountHandlerControl { |
| /// Creates a completely new Fuchsia account, optionally protecting the |
| /// account with a single enrollment of an authentication mechanism. |
| /// |
| /// `auth_mechanism_id` An `AuthMechanismId` for a storage |
| /// unlock-capable authentication mechanism. If |
| /// provided, a single enrollment of that |
| /// mechanism will be created for storage |
| /// unlock. |
| /// |
| /// Fails with FAILED_PRECONDITION if the AccountHandler is not in the `Uninitialized` |
| /// state. |
| CreateAccount( |
| fuchsia.identity.account.AuthMechanismId? auth_mechanism_id) |
| -> () error fuchsia.identity.account.Error; |
| |
| /// Loads pre-auth data, which allows the account to be unlocked later. |
| /// |
| /// This moves the AccountHandler from the `Uninitialized` state to the `Locked` state. |
| /// |
| /// Fails with FAILED_PRECONDITION if the AccountHandler is not in the `Uninitialized` |
| /// state. |
| Preload() -> () error fuchsia.identity.account.Error; |
| |
| /// Reach the `Initialized` state, attempting authentication and unlocking the account where |
| /// necessary. |
| /// |
| /// Fails with |
| /// - FAILED_PRECONDITION if the AccountHandler is not in the `Locked` or `Initialized` state. |
| /// - FAILED_AUTH_ATTEMPT if the authentication attempt failed. |
| UnlockAccount() |
| -> () error fuchsia.identity.account.Error; |
| |
| /// Reach the `Locked` state, closing any open `Account` and `Persona` channels for the account. |
| /// |
| /// Fails with FAILED_PRECONDITION if the AccountHandler is not in the `Initialized` or |
| /// `Locked` state. |
| LockAccount() -> () error fuchsia.identity.account.Error; |
| |
| /// Prepares the AccountHandler to receive an account from another device through |
| /// direct transfer. This moves the AccountHandler from the `Uninitialized` |
| /// state to the `PendingTransfer` state. |
| /// |
| /// `PrepareForAccountTransfer` should be used in conjunction with |
| /// `PerformAccountTransfer`, `FinalizeAccountTransfer`, and |
| /// `EncryptAccountData` to provision an existing account on another device |
| /// (source) to this device (target). |
| /// |
| /// The expected sequence of calls to setup an AccountHandler with a transferred |
| /// account is as follows: |
| /// 1. `PrepareForAccountTransfer` is called on the target device. |
| /// 2. The public key for the target device is retrieved using `GetPublicKey` |
| /// and handed to the source device. |
| /// 3. Account data is encrypted on the source device using the public key |
| /// with `EncryptAccountData`. |
| /// 4. The encrypted account data is passed in to AccountHandler on the target |
| /// device with `PerformAccountTransfer`. |
| /// 5. After any validation, the transfer is finalized on the target device |
| /// with a call to `FinalizeAccountTransfer`. |
| /// |
| /// Fails with FAILED_PRECONDITION if the AccountHandler is not in the `Uninitialized` |
| /// state. |
| PrepareForAccountTransfer() -> () error fuchsia.identity.account.Error; |
| |
| // TODO(satsukiu): investigate using VMOs or pagination over Overnet for |
| // when account size exceeds what FIDL can handle. |
| |
| /// Loads the encrypted account into memory, but does not make it available |
| /// for use yet. The account data passed in is expected to be serialized as |
| /// an `AccountTransferContainer` and encrypted using the public key |
| /// retrieved from `GetPublicKey`. This format of account data is provided |
| /// by `EncryptAccountData` on the source device. |
| /// |
| /// Moves the AccountHandler from the `PendingTransfer` state to the |
| /// `Transferred` state. |
| /// |
| /// `encrypted_account_data` Account data retrieved on the source device |
| /// using `EncryptAccountData`. |
| /// |
| /// Fails with FAILED_PRECONDITION if the AccountHandler is not in the |
| /// `PendingTransfer` state. |
| PerformAccountTransfer(EncryptedAccountData encrypted_account_data) |
| -> () error fuchsia.identity.account.Error; |
| |
| /// Completes the account transfer started through |
| /// `PrepareForAccountTransfer` and `PerformAccountTransfer`. This saves |
| /// the account to disk if the account is persistent and makes it available |
| /// for use. |
| /// |
| /// Moves the AccountHandler from the `Transferred` state to the |
| /// `Initialized` state. |
| /// |
| /// Fails with FAILED_PRECONDITION if the AccountHandler is not in the `Transferred` |
| /// state. |
| FinalizeAccountTransfer() |
| -> () error fuchsia.identity.account.Error; |
| |
| // TODO(fxr/37227) - update encrypt/decrypt APIs to handle data given by KMS. |
| |
| /// Serializes and encrypts the account contained in the AccountHandler for |
| /// transfer. The account will be serialized as `AccountData` and |
| /// encrypted using `target_public_key`. The resulting |
| /// `EncryptedAccountData` should be passed to AccountHandler on the target |
| /// device using `PerformAccountTransfer`. |
| /// |
| /// `target_public_key` The public key used to encrypt the account data. |
| /// |
| /// Returns: `encrypted_account_data` Bytes containing the encrypted account |
| /// |
| /// Fails with FAILED_PRECONDITION if the AccountHandler is not in the `Initialized` |
| /// state. |
| EncryptAccountData(fuchsia.kms.PublicKey target_public_key) |
| -> (EncryptedAccountData encrypted_account_data) error fuchsia.identity.account.Error; |
| |
| /// Deletes all persistent information about the Fuchsia account handled by |
| /// this handler, including all credentials and global identifiers. |
| /// Credential revocation is attempted before deletion. After a |
| /// successful call to RemoveAccount, all other open interfaces for this |
| /// account handler will be closed and any subsequent calls on the current |
| /// interface will fail. |
| /// |
| /// `force` If true, continues removing the account even if revocation of |
| /// credentials fails. If false, any revocation failure will result |
| /// in an error and the account will remain. In this case, a subset |
| /// of the credentials may have been deleted. |
| RemoveAccount(bool force) -> () error fuchsia.identity.account.Error; |
| |
| // TODO(jsankey): Add methods to cover adding an existing account and |
| // handling an account where the disk is not yet decrypted. |
| |
| /// Connects an interface to read properties of and perform operations on |
| /// the account handled by this handler. The AccountHandler must be in the |
| /// `Initialized` state. |
| /// |
| /// `context_provider` An `AuthenticationContextProvider` capable of |
| /// supplying UI contexts used for interactive |
| /// authentication on this account |
| /// `account` The server end of an `Account` channel |
| /// |
| /// Fails with FAILED_PRECONDITION if the AccountHandler is not in the `Initialized` |
| /// state. |
| GetAccount( |
| fuchsia.auth.AuthenticationContextProvider auth_context_provider, |
| request<fuchsia.identity.account.Account> account) |
| -> () error fuchsia.identity.account.Error; |
| |
| /// Retrieves the public key associated with this account on this device. |
| /// The public key is exposed so that the target AccountHandler's key can |
| /// be distributed to the source AccountHandler during an account transfer. |
| /// This allows the source AccountHandler to encrypt account data such that |
| /// only the target AccountHandler can decrypt the data. |
| /// |
| /// Returns: `public_key` Public key in an asymmetric key pair associated |
| /// with this account on this device. |
| /// |
| /// Fails with FAILED_PRECONDITION is the AccountHandler is not in one of |
| /// `PendingTransfer`, `Transferred`, or `Initialized` states. |
| GetPublicKey() -> (fuchsia.kms.PublicKey public_key) error fuchsia.identity.account.Error; |
| |
| /// Generates a hash of the global account ID using the provided salt. |
| /// Returning a hash of the global ID lets the account system determine |
| /// whether two locally provisioned accounts represent the same account |
| /// without storing every account's global ID. A salt is added so that |
| /// accounts cannot be easily correlated across devices. |
| /// |
| /// The AccountHandler must be in either the `Transferred` or `Initialized` |
| /// states. |
| /// |
| /// `salt` Bytes used as a salt while hashing global ID. |
| /// |
| /// Returns: `id_hash` A hash of the global ID of the contained account |
| /// |
| /// Fails with FAILED_PRECONDITION if the AccountHandler is not in one of |
| /// `Transferred` or `Initialized` states. |
| GetGlobalIdHash(HashSalt salt) -> (GlobalIdHash id_hash) error fuchsia.identity.account.Error; |
| |
| /// Signals that the AccountHandler should tear itself down. After the |
| /// receiver responds by closing its handle, the caller may terminate the |
| /// component if it hasn't already exited. |
| Terminate(); |
| }; |
| |
| /// An interface that supplies the account and authentication services that |
| /// an AccountHandler needs to perform its role in the system. |
| /// |
| /// This service is supplied into the namespace of AccountHandler by the |
| /// component that launches it (i.e. the AccountManager). |
| [Discoverable] |
| protocol AccountHandlerContext { |
| /// Connects to the `Oauth` implementation for a particular service provider, |
| /// launching it if necessary. |
| /// |
| /// `auth_provider_type` An OAuth identity provider matching a configuration |
| /// set in an AuthProviderConfig.auth_provider_type |
| /// `oauth` The server end of an `Oauth` channel |
| GetOauth(string auth_provider_type, |
| request<fuchsia.identity.external.Oauth> oauth) |
| -> () error fuchsia.identity.account.Error; |
| |
| /// Connects to the `OpenIdConnect` implementation for a particular service |
| /// provider, launching it if necessary. |
| /// |
| /// `auth_provider_type` An OpenID Connect identity provider matching a |
| /// configuration set in an |
| /// AuthProviderConfig.auth_provider_type |
| /// `open_id_connect` The server end of an `OpenIDConnect` channel |
| GetOpenIdConnect(string auth_provider_type, |
| request<fuchsia.identity.external.OpenIdConnect> open_id_connect) |
| -> () error fuchsia.identity.account.Error; |
| |
| /// Connects to the `OauthOpenIdConnect` implementation for a particular |
| /// service provider, launching it if necessary. |
| /// |
| /// `auth_provider_type` An OpenID Connect identity provider matching a |
| /// configuration set in an |
| /// AuthProviderConfig.auth_provider_type |
| /// `oauth_open_id_connect` The server end of an `OauthOpenIDConnect` channel |
| GetOauthOpenIdConnect(string auth_provider_type, |
| request<fuchsia.identity.external.OauthOpenIdConnect> |
| oauth_open_id_connect) |
| -> () error fuchsia.identity.account.Error; |
| |
| /// Connects to a `StorageUnlockMechanism` implementation identified by its |
| /// auth mechanism ID, launching the associated Authenticator if necessary. |
| /// |
| /// `auth_mechanism_id` An identifier matching an authentication mechanism |
| /// configured by the account system. |
| /// `storage_unlock_mechanism` The server end of an `StorageUnlockMechanism` channel |
| GetStorageUnlockAuthMechanism( |
| fuchsia.identity.account.AuthMechanismId auth_mechanism_id, |
| request<fuchsia.identity.authentication.StorageUnlockMechanism> storage_unlock_mechanism) |
| -> () error fuchsia.identity.account.Error; |
| }; |
| |
| /// Contents of an account, used for serialization during account transfer. |
| table AccountData { |
| /// A globally unique identifier for the account. |
| 1: fuchsia.identity.account.GlobalAccountId global_id; |
| }; |
| |
| /// Encrypted form of AccountData. |
| using EncryptedAccountData = bytes:16000; |