blob: d2585221fb5e6aa06d25f84f4fd9fb3b17b4e5b1 [file] [log] [blame]
// 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.
#ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_SM_TYPES_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_SM_TYPES_H_
#include <lib/fit/function.h>
#include <optional>
#include <string>
#include <unordered_map>
#include "src/connectivity/bluetooth/core/bt-host/common/uint128.h"
#include "src/connectivity/bluetooth/core/bt-host/hci-spec/constants.h"
#include "src/connectivity/bluetooth/core/bt-host/hci-spec/link_key.h"
#include "src/connectivity/bluetooth/core/bt-host/sm/smp.h"
namespace bt::sm {
const std::unordered_map<Code, size_t> kCodeToPayloadSize{
{kSecurityRequest, sizeof(AuthReqField)},
{kPairingRequest, sizeof(PairingRequestParams)},
{kPairingResponse, sizeof(PairingResponseParams)},
{kPairingConfirm, sizeof(PairingConfirmValue)},
{kPairingRandom, sizeof(PairingRandomValue)},
{kPairingFailed, sizeof(PairingFailedParams)},
{kEncryptionInformation, sizeof(EncryptionInformationParams)},
{kMasterIdentification, sizeof(MasterIdentificationParams)},
{kIdentityInformation, sizeof(IRK)},
{kIdentityAddressInformation, sizeof(IdentityAddressInformationParams)},
{kPairingPublicKey, sizeof(PairingPublicKeyParams)},
{kPairingDHKeyCheck, sizeof(PairingDHKeyCheckValueE)},
};
// The available algorithms used to generate the cross-transport key during pairing.
enum CrossTransportKeyAlgo {
// Use only the H6 function during cross-transport derivation (v5.2 Vol. 3 Part H 2.2.10).
kUseH6,
// Use the H7 function during cross-transport derivation (v5.2 Vol. 3 Part H 2.2.11).
kUseH7
};
// Represents the features exchanged during Pairing Phase 1.
struct PairingFeatures final {
PairingFeatures();
PairingFeatures(bool initiator, bool sc, bool will_bond,
std::optional<CrossTransportKeyAlgo> algo, PairingMethod method,
uint8_t enc_key_size, KeyDistGenField local_kd, KeyDistGenField remote_kd);
// True if the local device is in the "initiator" role.
bool initiator;
// True if LE Secure Connections pairing should be used. Otherwise, LE Legacy
// Pairing should be used.
bool secure_connections;
// True if pairing is to be performed with bonding, false if not
bool will_bond;
// If present, prescribes the algorithm to use during cross-transport key derivation. If not
// present, cross-transport key derivation should not take place.
std::optional<CrossTransportKeyAlgo> generate_ct_key;
// Indicates the key generation model used for Phase 2.
PairingMethod method;
// The negotiated encryption key size.
uint8_t encryption_key_size;
// The keys that we must distribute to the peer.
KeyDistGenField local_key_distribution;
// The keys that will be distributed to us by the peer.
KeyDistGenField remote_key_distribution;
};
constexpr KeyDistGenField DistributableKeys(KeyDistGenField keys) {
// The link key field never affects the distributed keys. It only has meaning when the devices use
// LE Secure Connections, where it means the devices should generate the BR/EDR Link Key locally.
return keys & ~KeyDistGen::kLinkKey;
}
// Returns a bool indicating whether `features` calls for the devices to exchange key information
// during the Key Distribution/Generation Phase 3.
bool HasKeysToDistribute(PairingFeatures features);
// Each enum variant corresponds to an LE security mode 1 level in v5.2 Vol. Part C 10.2.1. Fuchsia
// only supports encryption based security (Security Mode 1 and Secure Connections Only mode).
enum class SecurityLevel {
// No encryption
kNoSecurity = 1,
// Encrypted without MITM protection (unauthenticated)
kEncrypted = 2,
// Encrypted with MITM protection (authenticated)
kAuthenticated = 3,
// Encrypted with MITM protection, Secure Connections, and a 128-bit encryption key.
kSecureAuthenticated = 4,
};
// Returns a string representation of |level| for debug messages.
const char* LevelToString(SecurityLevel level);
// Represents the security properties of a key. The security properties of a
// connection's LTK defines the security properties of the connection.
class SecurityProperties final {
public:
SecurityProperties();
SecurityProperties(SecurityLevel level, size_t enc_key_size, bool secure_connections);
SecurityProperties(bool encrypted, bool authenticated, bool secure_connections,
size_t enc_key_size);
// Build from a BR/EDR Link Key that resulted from pairing. |lk_type| should not be
// kChangedCombination, because that means that the link key is the same type as before it was
// changed, which this has no knowledge of.
//
// Legacy pairing keys will be considered to have security level kNoSecurity because legacy
// pairing is superceded by Secure Simple Pairing in Core Spec v2.1 + EDR in 2007. Backwards
// compatiblity is optional per v5.0, Vol 3, Part C, Section 5. Furthermore, the last Core Spec
// with only legacy pairing (v2.0 + EDR) was withdrawn by Bluetooth SIG on 2019-01-28.
//
// TODO(fxbug.dev/36360): SecurityProperties will treat kDebugCombination keys as "encrypted,
// unauthenticated, and no Secure Connections" to potentially allow their use as valid link keys,
// but does not store the fact that they originate from a controller in pairing debug mode, a
// potential hazard. Care should be taken at the controller interface to enforce particular
// policies regarding debug keys.
explicit SecurityProperties(hci::LinkKeyType lk_type);
SecurityLevel level() const;
size_t enc_key_size() const { return enc_key_size_; }
bool encrypted() const { return properties_ & Property::kEncrypted; }
bool secure_connections() const { return properties_ & Property::kSecureConnections; }
bool authenticated() const { return properties_ & Property::kAuthenticated; }
// Returns the BR/EDR link key type that produces the current security properties. Returns
// std::nullopt if the current security level is kNoSecurity.
//
// SecurityProperties does not encode the use of LinkKeyType::kDebugCombination keys (see Core
// Spec v5.0 Vol 2, Part E Section 7.6.4), produced when a controller is in debug mode, so
// SecurityProperties constructed from LinkKeyType::kDebugCombination returns
// LinkKeyType::kUnauthenticatedCombination192 from this method.
std::optional<hci::LinkKeyType> GetLinkKeyType() const;
// Returns a string representation of these properties.
std::string ToString() const;
// Returns whether `this` SecurityProperties is at least as secure as |other|. This checks the
// encryption/authentication level of `this` vs. other, that `this` used secure connections if
// |other| did, and that `this` encryption key size is at least as large as |others|.
bool IsAsSecureAs(const SecurityProperties& other) const;
// Compare two properties for equality.
bool operator==(const SecurityProperties& other) const {
return properties_ == other.properties_ && enc_key_size_ == other.enc_key_size_;
}
bool operator!=(const SecurityProperties& other) const { return !(*this == other); }
private:
// Possible security properties for a link.
enum Property : uint8_t {
kEncrypted = (1 << 0),
kAuthenticated = (1 << 1),
kSecureConnections = (1 << 2)
};
using PropertiesField = uint8_t;
PropertiesField properties_;
size_t enc_key_size_;
};
// Represents a reusable long term key for a specific transport.
class LTK final {
public:
LTK() = default;
LTK(const SecurityProperties& security, const hci::LinkKey& key);
const SecurityProperties& security() const { return security_; }
const hci::LinkKey& key() const { return key_; }
bool operator==(const LTK& other) const {
return security() == other.security() && key() == other.key();
}
bool operator!=(const LTK& other) const { return !(*this == other); }
private:
SecurityProperties security_;
hci::LinkKey key_;
};
// Represents a 128-bit key.
class Key final {
public:
Key() = default;
Key(const SecurityProperties& security, const UInt128& value);
const SecurityProperties& security() const { return security_; }
const UInt128& value() const { return value_; }
bool operator==(const Key& other) const {
return security() == other.security() && value() == other.value();
}
private:
SecurityProperties security_;
UInt128 value_;
};
// Container for LE pairing data.
struct PairingData final {
// The identity address.
std::optional<DeviceAddress> identity_address;
// The long term link encryption key generated by the local device. For LTKs generated by Secure
// Connections, this will be the same as peer_ltk.
std::optional<sm::LTK> local_ltk;
// The long term link encryption key generated by the peer device. For LTKs generated by Secure
// Connections, this will be the same as local_ltk.
std::optional<sm::LTK> peer_ltk;
// The cross-transport key for pairing-free encryption on the other transport.
std::optional<sm::LTK> cross_transport_key;
// The identity resolving key used to resolve RPAs to |identity|.
std::optional<sm::Key> irk;
// The connection signature resolving key used in LE security mode 2.
std::optional<sm::Key> csrk;
bool operator==(const PairingData& other) const {
return identity_address == other.identity_address && local_ltk == other.local_ltk &&
peer_ltk == other.peer_ltk && irk == other.irk && csrk == other.csrk;
}
};
// Container for identity information for distribution.
struct IdentityInfo {
UInt128 irk;
DeviceAddress address;
};
// Enum for the possible values of the SM Bondable Mode as defined in spec V5.1 Vol 3 Part C
// Section 9.4
enum class BondableMode {
// Allows pairing which results in bonding, as well as pairing which does not
Bondable,
// Does not allow pairing which results in bonding
NonBondable,
};
// Represents the local device's settings for easy mapping to Pairing(Request|Response)Parameters.
struct LocalPairingParams {
// The local I/O capability.
IOCapability io_capability;
// Whether or not OOB authentication data is available locally. Defaults to no OOB data.
OOBDataFlag oob_data_flag = OOBDataFlag::kNotPresent;
// The local requested security properties (Vol 3, Part H, 2.3.1). Defaults to no Authentication
// Requirements.
AuthReqField auth_req = 0u;
// Maximum encryption key size supported by the local device. Valid values are 7-16. Defaults
// to maximum allowed encryption key size.
uint8_t max_encryption_key_size = kMaxEncryptionKeySize;
// The keys that the local system is able to distribute. Defaults to distributing no keys.
KeyDistGenField local_keys = 0u;
// The keys that are desired from the peer. Defaults to distributing no keys.
KeyDistGenField remote_keys = 0u;
};
// These roles correspond to the device which starts pairing.
enum class Role {
// The LMP Master device is always kInitiator (V5.0 Vol. 3 Part H Appendix C.1).
kInitiator,
// The LMP Slave device is always kResponder (V5.0 Vol. 3 Part H Appendix C.1).
kResponder
};
using PairingProcedureId = uint64_t;
// Used by Phase 2 classes to notify their owner that a new encryption key is ready. For Legacy
// Pairing, this is the STK which may only be used for the current session. For Secure Connections,
// this is the LTK which may be persisted.
using OnPhase2KeyGeneratedCallback = fit::function<void(const UInt128&)>;
// Used to notify classes of peer Pairing Requests.
using PairingRequestCallback = fit::function<void(PairingRequestParams)>;
} // namespace bt::sm
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_SM_TYPES_H_