1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
use bitflags::bitflags;
// TODO: Remove Eq and PartialEq from types that don't need them
/// User input capabilities ([Vol 3] Part H, Section 2.3.2, Table 2.3).
#[allow(dead_code)] // TODO: Implement Keyboard
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub(super) enum InputCap {
/// Device does not have the ability to indicate 'yes' or 'no'.
None,
/// Device has a mechanism for the user to indicate either 'yes' or 'no'.
YesNo,
/// Device has a numeric keyboard that can input the numbers '0' to '9' and
/// a confirmation. Device also has a mechanism for the user to indicate
/// either 'yes' or 'no'.
Keyboard,
}
/// User output capabilities ([Vol 3] Part H, Section 2.3.2, Table 2.4).
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub(super) enum OutputCap {
/// Device does not have the ability to display or communicate a 6 digit
/// decimal number.
None,
/// Device has the ability to display or communicate a 6 digit decimal
/// number.
Numeric,
}
/// Key generation method ([Vol 3] Part H, Section 2.3.5.1, Table 2.8).
#[derive(Clone, Copy, Debug)]
pub(super) enum KeyGenMethod {
// TODO: Oob
JustWorks,
PasskeyEntry,
NumCompare,
}
impl KeyGenMethod {
/// Map of IO capabilities to key generation method
/// ([Vol 3] Part H, Section 2.3.5.1, Table 2.8).
#[rustfmt::skip]
const MAP: [[Self; 5]; 5] = {
use KeyGenMethod::*;
[// DisplayOnly DisplayYesNo KeyboardOnly NoInputNoOutput KeyboardDisplay
[JustWorks, JustWorks, PasskeyEntry, JustWorks, PasskeyEntry ], // DisplayOnly
[JustWorks, NumCompare, PasskeyEntry, JustWorks, NumCompare ], // DisplayYesNo
[PasskeyEntry, PasskeyEntry, PasskeyEntry, JustWorks, PasskeyEntry ], // KeyboardOnly
[JustWorks, JustWorks, JustWorks, JustWorks, JustWorks ], // NoInputNoOutput
[PasskeyEntry, NumCompare, PasskeyEntry, JustWorks, NumCompare ], // KeyboardDisplay
]
};
/// Returns key generation method for the given combination of IO
/// capabilities. The mapping is symmetric, so it doesn't matter which
/// parameter is the initiator and which one is the responder.
#[inline(always)]
pub const fn resolve(a: IoCap, b: IoCap) -> Self {
Self::MAP[a as usize][b as usize]
}
}
/// Command code ([Vol 3] Part H, Section 3.3).
#[derive(
Clone, Copy, Debug, Eq, PartialEq, num_enum::IntoPrimitive, num_enum::TryFromPrimitive,
)]
#[non_exhaustive]
#[repr(u8)]
pub enum Code {
PairingRequest = 0x01,
PairingResponse = 0x02,
PairingConfirm = 0x03,
PairingRandom = 0x04,
PairingFailed = 0x05,
EncryptionInformation = 0x06,
CentralIdentification = 0x07,
IdentityInformation = 0x08,
IdentityAddressInformation = 0x09,
SigningInformation = 0x0A,
SecurityRequest = 0x0B,
PairingPublicKey = 0x0C,
PairingDhKeyCheck = 0x0D,
PairingKeypressNotification = 0x0E,
}
/// IO capability ([Vol 3] Part H, Section 3.5.1).
#[derive(
Clone, Copy, Debug, Default, Eq, PartialEq, num_enum::IntoPrimitive, num_enum::TryFromPrimitive,
)]
#[non_exhaustive]
#[repr(u8)]
pub(super) enum IoCap {
DisplayOnly = 0x00,
DisplayYesNo = 0x01,
KeyboardOnly = 0x02,
#[default]
NoInputNoOutput = 0x03,
KeyboardDisplay = 0x04,
}
impl IoCap {
/// Creates IO capabilities from specified input/output configuration
/// ([Vol 3] Part H, Section 2.3.2, Table 2.5).
#[must_use]
#[inline]
pub const fn new(inp: InputCap, out: OutputCap) -> Self {
#[allow(clippy::match_same_arms)]
match (inp, out) {
(InputCap::None, OutputCap::None) => Self::NoInputNoOutput,
(InputCap::None, OutputCap::Numeric) => Self::DisplayOnly,
(InputCap::YesNo, OutputCap::None) => Self::NoInputNoOutput,
(InputCap::YesNo, OutputCap::Numeric) => Self::DisplayYesNo,
(InputCap::Keyboard, OutputCap::None) => Self::KeyboardOnly,
(InputCap::Keyboard, OutputCap::Numeric) => Self::KeyboardDisplay,
}
}
}
bitflags! {
/// Requested security properties ([Vol 3] Part H, Section 3.5.1).
#[derive(Default)]
#[repr(transparent)]
pub struct AuthReq: u8 {
/// Bonding requested.
const BONDING = 0b01 << 0;
/// MITM protection (authentication) requested.
const MITM = 1 << 2;
/// LE Secure Connections pairing is supported.
const SC = 1 << 3;
/// Enable keypress notifications in the Passkey Entry protocol.
const KEYPRESS = 1 << 4;
/// h7 function is supported for cross-transport key derivation.
const CT2 = 1 << 5;
}
}
bitflags! {
/// LE Key Distribution parameter ([Vol 3] Part H, Section 3.6.1).
#[derive(Default)]
#[repr(transparent)]
pub struct KeyDist: u8 {
/// Ignored in LE Secure Connections pairing.
const ENC = 1 << 0;
/// Distribute IRK using the Identity Information command.
const ID = 1 << 1;
/// Distribute CSRK using the Signing Information command.
const SIGN = 1 << 2;
/// Derive the BR/EDR Link Key from the LE LTK.
const LINK = 1 << 3;
}
}
/// Pairing Failed reason codes ([Vol 3] Part H, Section 3.5.5, Table 3.7).
#[derive(
Clone,
Copy,
Debug,
Eq,
PartialEq,
num_enum::IntoPrimitive,
num_enum::TryFromPrimitive,
thiserror::Error,
)]
#[non_exhaustive]
#[repr(u8)]
pub enum Reason {
/// User input of passkey failed. For example, the user cancelled the
/// operation.
PasskeyEntryFailed = 0x01,
/// OOB data is not available.
OobNotAvailable = 0x02,
/// Pairing procedure cannot be performed as authentication requirements
/// cannot be met due to IO capabilities of one or both devices.
AuthenticationRequirements = 0x03,
/// Confirm value does not match the calculated compare value.
ConfirmValueFailed = 0x04,
/// Pairing is not supported by the device.
PairingNotSupported = 0x05,
/// Resultant encryption key size is not long enough for the security
/// requirements of this device.
EncryptionKeySize = 0x06,
/// Received command is not supported on this device.
CommandNotSupported = 0x07,
/// Pairing failed due to an unspecified reason.
UnspecifiedReason = 0x08,
/// Pairing or authentication procedure is disallowed because too little
/// time has elapsed since last pairing request or security request.
RepeatedAttempts = 0x09,
/// Command length is invalid or a parameter is outside the specified range.
InvalidParameters = 0x0A,
/// Received DHKey Check value doesn't match the one calculated by the local
/// device.
DhKeyCheckFailed = 0x0B,
/// Confirm values in the numeric comparison protocol do not match.
NumericComparisonFailed = 0x0C,
/// Pairing over the LE transport failed due to an in-progress Pairing
/// Request sent over the BR/EDR transport.
BredrPairingInProgress = 0x0D,
/// Link Key generated on the BR/EDR transport cannot be used to derive and
/// distribute keys for the LE transport, or the LTK generated on the LE
/// transport cannot be used to derive a key for the BR/EDR transport.
CrossTransportKeyDerivationNotAllowed = 0x0E,
/// Device chose not to accept a distributed key.
KeyRejected = 0x0F,
}
/// Keypress notification type ([Vol 3] Part H, Section 3.5.8).
#[derive(
Clone, Copy, Debug, Eq, PartialEq, num_enum::IntoPrimitive, num_enum::TryFromPrimitive,
)]
#[non_exhaustive]
#[repr(u8)]
pub enum PasskeyEntry {
Started = 0,
DigitEntered = 1,
DigitErased = 2,
Cleared = 3,
Completed = 4,
}
crate::impl_display_via_debug! { Code, IoCap, Reason, PasskeyEntry }