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 }