chromium/chromeos/ash/services/secure_channel/public/mojom/secure_channel.mojom

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

module ash.secure_channel.mojom;

import "chromeos/ash/components/multidevice/mojom/multidevice_types.mojom";
import "chromeos/ash/services/secure_channel/public/mojom/nearby_connector.mojom";
import "chromeos/ash/services/secure_channel/public/mojom/secure_channel_types.mojom";
import "mojo/public/mojom/base/time.mojom";

// Keep in sync with SecureChannelConnectionAttemptFailureReason in
// tools/metrics/histograms/enums.xml
enum ConnectionAttemptFailureReason {
  // The local device could not authenticate with the remote device. This likely
  // means that one (or both) devices have not synced their keys recently.
  AUTHENTICATION_ERROR = 0,

  // An advertisement could not be generated for this connection. This likely
  // means that one (or both) devices have not synced their BeaconSeeds
  // recently.
  COULD_NOT_GENERATE_ADVERTISEMENT = 1,

  // GATT connections were attempted, but they failed. This could be caused by
  // errors creating GATT connections, dropped GATT connections, or an issue
  // relating to GATT services being unavailable.
  GATT_CONNECTION_ERROR = 2,

  // A Nearby Connection was attempted, but it failed. Potential causes include
  // internal API errors and flaky Bluetooth/WebRTC connections.
  NEARBY_CONNECTION_ERROR = 3,

  // The provided local device does not have a public key set.
  LOCAL_DEVICE_INVALID_PUBLIC_KEY = 4,

  // The provided local device does not have a persistent symmetric key set.
  LOCAL_DEVICE_INVALID_PSK = 5,

  // The provided local device does not have a valid Bluetooth address.
  LOCAL_DEVICE_INVALID_BLUETOOTH_ADDRESS = 6,

  // The provided remote device does not have a public key set.
  REMOTE_DEVICE_INVALID_PUBLIC_KEY = 7,

  // The provided remote device does not have a persistent symmetric key set.
  REMOTE_DEVICE_INVALID_PSK = 8,

  // The provided remote device does not have a valid Bluetooth address.
  REMOTE_DEVICE_INVALID_BLUETOOTH_ADDRESS = 9,

  // Timeouts occurred trying to contact the remote device.
  TIMEOUT_FINDING_DEVICE = 10,

  // The local Bluetooth adapter is disabled (turned off).
  ADAPTER_DISABLED = 11,

  // The local Bluetooth adapter is not present.
  ADAPTER_NOT_PRESENT = 12,

  // Tried to connect using a role which is unsupported for the connection
  // medium (e.g., Nearby Connections only supports the listener role, not the
  // initiator role).
  UNSUPPORTED_ROLE_FOR_MEDIUM = 13,

  // Tried requesting a connection via Nearby Connections, but no
  // NearbyConnector was provided.
  MISSING_NEARBY_CONNECTOR = 14,

  // The attempt was intentionally interrupted.
  CONNECTION_CANCELLED = 15,
};

enum ConnectionCreationDetail {
  REMOTE_DEVICE_USED_BACKGROUND_BLE_ADVERTISING,
  REMOTE_DEVICE_USED_FOREGROUND_BLE_ADVERTISING
};

// Determines the order in which connections are attempted when system resources
// must be shared. For example, a device can only register a limited number of
// BLE advertisements at a given time due to hardware constraints; in this
// situation, a connection attempt with a higher priority will be allowed to
// register an advertisement before an attempt with a lower priority.
//
// For connection mediums which do not require use of limited system resources,
// ConnectionPriority is ignored.
enum ConnectionPriority {
  // Should be used for connection attempts which do not have latency
  // requirements (e.g., background scans for nearby devices).
  LOW,

  // Should be used when the connection attempt should complete in a reasonable
  // amount of time but is not urgent (e.g., heartbeat/keep-alive messages).
  MEDIUM,

  // Should be used when the user is directly waiting on the result of the
  // connection (e.g., the user clicks a button and sees a spinner in the UI
  // until the connection succeeds).
  HIGH
};

enum ConnectionMedium {
  // Connection occurs using GATT over Bluetooth Low Energy (BLE).
  kBluetoothLowEnergy,

  // Connection occurs using the Nearby Connections library, which bootstraps
  // a connection via Bluetooth Classic, then "upgrades" bandwidth to a WebRTC
  // connection.
  kNearbyConnections
};

enum DiscoveryResult {
  // Failed to discovered device. A DiscoveryErrorCode will be appended\
  // when logging.
  kFailure,

  // Successfully discovered device.
  kSuccess
};

// Possible error code happened during BLE discovery process. Should be
// associated with DiscoveryResult.
enum DiscoveryErrorCode {
  kBluetoothTurnedOff,
  kFilterCreationFailed,
  kErrorStartingDiscovery,
  kBleSessionInvalidated,
  kDeviceNotInScanRequest,
  kTimeout
};

// Different state during Secure Channel authentication process.
enum SecureChannelState {
  kGeneratingSessionKeys,
  kSendingHello,
  kSentHello,
  kReceivedResponderAuth,
  kValidatedResponderAuth,
  kSentInitiatorAuth,
  kAuthenticationSuccess,
  kFailureNotConnectedToRemoteDevice,
  kFailedToGenerateSessionKeys,
  kFailedToGenerateHelloMessage,
  kFailedToSendHelloMessage,
  kFailedToWaitForResponderAuth,
  kReceivedUnexpectedMessage,
  kFailedToValidateReponderAuth,
  kFailedToGenerateInitiatorAuth,
  kFailedToSendInitiatorAuth,
  kFailureDisconnectDuringAuthentication
};

struct BluetoothConnectionMetadata {
  // RSSI at the exact time that this metadata was requested. Note that RSSI
  // may change drastically in a short period; this is only a snapshot.
  int32 current_rssi;
};

struct ConnectionMetadata {
  // Details which describe how the connection was created. If no
  // ConnectionCreationDetails apply to this connection, the array is empty.
  array<ConnectionCreationDetail> creation_details;

  // Only available for connections over Bluetooth.
  BluetoothConnectionMetadata? bluetooth_connection_metadata;

  // The responder auth message returned by the other device during the
  // cryptographic handshake that established the channel. These are serialized
  // bytes of a proto which contains an encrypted message, returned by the
  // remote device in the last step of a 3-step cryptographic handshake. These
  // bytes were publicly visible over BLE when the connection with the remote
  // device was being established. It is used by some clients for further
  // cryptographic communication.
  // Defined by //chromeos/ash/services/device_sync/proto/securemessage.proto.
  string channel_binding_data;
};

// Allows clients to send messages to a remote device, or registers local files
// to receive incoming files transferred from a remote device. Can also be used
// to get metadata of the underlying connection. Called from Secure Channel
// client side API implementation.
interface Channel {
  // If the connection is dropped (e.g., by the remote device or due to
  // connection instability), this reason is supplied to the
  // connection_error_with_reason_handler.
  const int32 kConnectionDroppedReason = 1;

  // Sends an encrypted message over the connection. The message is sent
  // along with metadata indicating the feature used to create this
  // connection. The callback is invoked when the message has been sent
  // successfully. If the message fails to be sent, the entire Channel is
  // disconnected with reason |kConnectionDroppedReason|, and the SendMessage()
  // callback is never invoked.
  SendMessage(string message) => ();

  // Registers |payload_files| to receive an incoming file transfer with
  // the given |payload_id|. Callers can listen to progress information about
  // the transfer through the |listener|. Returns whether the file was
  // successfully registered. The |listener| will be disconnected after the file
  // transfer is complete.
  RegisterPayloadFile(int64 payload_id,
                      PayloadFiles payload_files,
                      pending_remote<FilePayloadListener> listener)
      => (bool success);

  GetConnectionMetadata() => (ConnectionMetadata metadata);
};

interface MessageReceiver {
  // Invoked when a message is received over the connection. Only messages
  // corresponding to the feature used when creating the connection are
  // passed to this callback.
  OnMessageReceived(string message);
};

// Delegate interface used to handle connection attempt successes/failures. Note
// that this interface is intended to be implemented by clients of the
// DeviceSync service.
interface ConnectionDelegate {
  // Invoked when a connection attempt failed (i.e., the failure occurred before
  // a connection could be established).
  OnConnectionAttemptFailure(ConnectionAttemptFailureReason reason);

  // Invoked when a connection is established. The channel is authenticated
  // with account credentials, and all messages sent over the channel are
  // encrypted. Client should set a connection_error_with_reason_handler on
  // |channel| to be notified when it has been invalidated due to a dropped
  // connection. Note that clients are expected to hold the reference to
  // |channel| until they are done using the connection.
  // |nearby_connection_state_listener_receiver| will only be invoked if the
  // channel is over Nearby Connection.
  OnConnection(pending_remote<Channel> channel,
               pending_receiver<MessageReceiver> message_receiver_receiver,
               pending_receiver<NearbyConnectionStateListener>
               nearby_connection_state_listener_receiver);
};

// The interface used to log state of each step during Secure Channel connection
// establishment.
interface SecureChannelStructuredMetricsLogger {
  // Invoked when BLE discovery step of Secure Channel connection attempt has an
  // result. And if the result is a failure result, an error code will also be
  // passed in.
  LogDiscoveryAttempt(DiscoveryResult result, DiscoveryErrorCode? error_code);

  // Invoked when there is a state change during Nearby Connection process.
  LogNearbyConnectionState(NearbyConnectionStep step,
                           NearbyConnectionStepResult status);

  // Invoked when there is a state change during Secure Channel authentication
  // process.
  LogSecureChannelState(SecureChannelState state);
};

// Brokers connections between the current Chromebook and remote devices,
// allowing the sending/receiving of messages. All channels created via this API
// are authenticated and all messages sent are encrypted via account-specific
// keys.
//
// If multiple connections are requested to the same device, this service
// creates only one connection and shares it among all clients; likewise, if
// multiple clients are sharing a single connection and one client drops its
// reference to the connection, the connection will stay active until each
// client has invalidated its reference to the channel.
//
// All connections require metadata about the local device (i.e., the device on
// which this code is running) in order to perform a cryptographic handshake
// which ensures that both devices are owned by the same underlying account.
// Note that each logged-in user on Chrome OS has its own set of keys; thus, the
// provided local device is not the same each time the call is made. For
// instance, at the sign-in (i.e., account-choosing) screen, EasyUnlock tries to
// establish connections to phones belonging to each account on the device, so
// each of these connection attempts uses a different local device.
//
// If a Nearby-based connection is requested, clients must first invoke
// SetNearbyConnector().
//
// Note: Each API function results in either one connection (success case)
// or zero connections (failure case). Clients wishing to initiate another
// connection after the first one has completed should invoke the API again.
interface SecureChannel {
  // Starts listening for an incoming connection attempt from |device|.
  // This function listens indefinitely until a connection is formed, at
  // which time |delegate|'s OnConnection() is invoked. |delegate|'s
  // OnConnectionFailure() is only invoked if the connection could not be
  // attempted at all (e.g., if |device| did not contain valid metadata). To
  // cancel a connection attempt (i.e., to stop listening for incoming
  // connections), disconnect |delegate|.
  ListenForConnectionFromDevice(
      ash.multidevice.mojom.RemoteDevice device_to_connect,
      ash.multidevice.mojom.RemoteDevice local_device,
      string feature,
      ConnectionMedium connection_medium,
      ConnectionPriority connection_priority,
      pending_remote<ConnectionDelegate> delegate);

  // Initiates a connection to |device|, which is presumed to be listening
  // for incoming connections. This function attempts to create a connection
  // until it has succeeded or until it has failed several attempts.
  // |delegate|'s OnConnection() is called in the success case, and
  // OnConnectionFailure() is called if a connection could not be attempted or
  // if a connection was attempted and failed several retry attempts. To cancel
  // the connection attempt, disconnect |delegate|.
  InitiateConnectionToDevice(
      ash.multidevice.mojom.RemoteDevice device_to_connect,
      ash.multidevice.mojom.RemoteDevice local_device,
      string feature,
      ConnectionMedium connection_medium,
      ConnectionPriority connection_priority,
      pending_remote<ConnectionDelegate> delegate,
      pending_remote<SecureChannelStructuredMetricsLogger>?
      secure_channel_structured_metrics_logger);

  // Sets a NearbyConnector to be used by future connection requests. Must be
  // called before requesting a connection with
  // ConnectionMedium::kNearbyConnections. Should only be called once.
  SetNearbyConnector(pending_remote<NearbyConnector> nearby_connector);

  // Retrieves the timestamp of the last successful discovery for the given
  // |remote_device_id|, or null if we haven't seen this remote device during
  // the current Chrome session.
  GetLastSeenTimestamp(string remote_device_id) => (mojo_base.mojom.Time? time);
};