1# -*- coding: utf-8 -*-
2# Copyright 2019 Oticon A/S
3# SPDX-License-Identifier: Apache-2.0
4
5import string;
6from enum import IntEnum;
7from components.utils import *;
8
9SMP_CID = 6;
10
11class SMPCapability(IntEnum):
12    SMP_CAP_DISPLAY_ONLY                 =   0 # Display Only
13    SMP_CAP_DISPLAY_YES_NO               =   1 # Display Yes or No
14    SMP_CAP_KEYBOARD_ONLY                =   2 # Keyboard Only
15    SMP_CAP_NO_INPUT_NO_OUTPUT           =   3 # No Input and No Output
16    SMP_CAP_KEYBOARD_DISPLAY             =   4 # Keyboard and Display
17
18class SMPOOBFlag(IntEnum):
19    SMP_OOB_NO_AUTH_DATA                 =   0 # OOB Authentication data not present
20    SMP_OOB_AUTH_DATA_PRESENT            =   1 # OOB Authentication data from remote device present
21
22class SMPBondingFlag(IntEnum):
23    SMP_BOND_NONE                        =   0 # No Bondig requested
24    SMP_BOND_REQUESTED                   =   1 # Bonding requested
25
26class SMPMITMFlag(IntEnum):                    # MITM - 'Man In The Middle' attacks protection
27    SMP_MITM_NONE                        =   0 # No MITM protection requested
28    SMP_MITM_REQUESTED                   =   4 # MITM protection requested
29
30class SMPSecureConnections(IntEnum):
31    SMP_SC_NOT_SUPPORTED                 =   0 # Secure Connections Pairing not supported
32    SMP_SC_SUPPORTED                     =   8 # Secure Connections Pairing supported
33
34class SMPKeyPressNotifications(IntEnum):
35    SMP_KPN_NONE                         =   0 # Do not exchange Keypress Notifications
36    SMP_KPN_REQUESTED                    =  16 # Do exchange Keypress Notifications
37
38class SMPSupportH7(IntEnum):
39    SMP_CT2_NO_SUPPORT                   =   0 # No support for the h7 security function
40    SMP_CT2_SUPPORTED                    =  32 # Support for the h7 security function present
41
42class SMPPasskey(IntEnum):
43    SMP_PASSKEY_ENTRY_STARTED            =   0 # Passkey entry started
44    SMP_PASSKEY_DIGIT_ENTERED            =   1 # Passkey digit entered
45    SMP_PASSKEY_DIGIT_ERASED             =   2 # Passkey digit erased
46    SMP_PASSKEY_CLEARED                  =   3 # Passkey entry cleared
47    SMP_PASSKEY_ENTRY_COMPLETE           =   4 # Passkey entry completed
48
49class SMPKeyGeneration(IntEnum):
50    SMP_KEY_GEN_JUST_WORKS               =   0 # Just Works
51    SMP_KEY_GEN_NUM_COMPARE              =   1 # Numeric Comparison (Only for LE Secure Connections)
52    SMP_KEY_GEN_PASSKEY                  =   2 # PassKey Entry
53    SMP_KEY_GEN_OOB                      =   3 # Out of Band
54
55class SMPDistribution(IntEnum):
56    SMP_DST_ENCKEY                       =   1 # Distribute LTK, EDIV and Rand (LE legacy pairing); ignored (Secure Connections)
57    SMP_DST_IDKEY                        =   2 # Distribute IRK and BD_ADDR
58    SMP_DST_SIGNKEY                      =   4 # Distribute CSRK
59    SMP_DST_LINKKEY                      =   8 # Derive Link Key from LTK (Secure Connections); ignored (no Secure Connections)
60
61class SMPOpcode(IntEnum):
62    SMP_PAIRING_REQUEST                  =   1 # Pairing Request  - Pairing Feature Exchange - IO Capability, OOB data flag, AuthReq, Max. Encryption Key Size, Initiator Key Distribution, Responder Key Distribution
63    SMP_PAIRING_RESPONSE                 =   2 # Pairing Response - Pairing Feature Exchange - IO Capability, OOB data flag, AuthReq, Max. Encryption Key Size, Initiator Key Distribution, Responder Key Distribution
64    SMP_PAIRING_CONFIRM                  =   3 # Pairing Confirm  - Pairing Confirmation value
65    SMP_PAIRING_RANDOM                   =   4 # Pairing Random   - Pairing Random value
66    SMP_PAIRING_FAILED                   =   5 # Pairing Failed   - Pairing Failed reason
67    SMP_ENCRYPTION_INFORMATION           =   6 # Encryption Information - Long Term Key
68    SMP_CENTRAL_IDENTIFICATION            =   7 # Central Identification  - Dsitribute EDIV and Rand for encrypting future connections
69    SMP_IDENTITY_INFORMATION             =   8 # Identity Information   - Distribute the IRK
70    SMP_IDENTITY_ADDRESS_INFORMATION     =   9 # Identity Address Information - Distribute Public or Static Random device address
71    SMP_SIGNING_INFORMATION              =  10 # Signing Information    - Distribute the CSRK
72    SMP_SECURITY_REQUEST                 =  11 # Security Request       - Initiate security with the requested properties
73    SMP_PAIRING_PUBLIC_KEY               =  12 # Pairing Public Key     - Distribute Public Keys X and Y
74    SMP_PAIRING_DHKEY_CHECK              =  13 # Pairing DHKey Check    - Distribute DHKey Check
75    SMP_PAIRING_KEYPRESS_NOTIFICATION    =  14 # Keypress Notification  - Inform about keys entered or erased
76
77class SMPError(IntEnum):
78    SMP_ERROR_PASSKEY                    =   1 # The user input of passkey failed, for example, the user cancelled the operation
79    SMP_ERROR_OOB                        =   2 # The OOB data is not available
80    SMP_ERROR_AUTHENTICATION             =   3 # The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices
81    SMP_ERROR_CONFIRM                    =   4 # The confirm value does not match the calculated compare value
82    SMP_ERROR_PAIRING                    =   5 # Pairing is not supported by the device
83    SMP_ERROR_ENCRYPTION                 =   6 # The resultant encryption key size is insufficient for the security requirements of this device
84    SMP_ERROR_COMMAND                    =   7 # The SMP command received is not supported on this device
85    SMP_ERROR_REASON                     =   8 # Pairing failed due to an unspecified reason
86    SMP_ERROR_REPEATED_ATTEMPTS          =   9 # Pairing or authentication procedure is disallowed because too little time has elapsed since last pairing request or security request
87    SMP_ERROR_INVALID_PARAMETERS         =  10 # The invalid parameters error code indicates that the command length is invalid or that a parameter is outside the specified range
88    SMP_ERROR_DHKEY_CHECK                =  11 # Indicates to the remote device that the DHKey Check value received doesn't match the one calculated by the local device
89    SMP_ERROR_NUMERIC_COMPARISON         =  12 # Indicates that the confirm values in the numeric comparison protocol do not match
90    SMP_ERROR_PAIRING_IN_PROGRESS        =  13 # Indicates that the pairing over the LE transport failed due to a Pairing Request sent over the BR/EDR transport
91    SMP_ERROR_CROSS_KEY_GENERATION       =  14 # Indicates that the BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport
92
93class SMPData:
94
95    def __init__(self):
96        self.data = [];
97
98    def encode(self, opcode, *args):
99      #
100      # encode ( SMPOpcode.SMP_PAIRING_REQUEST, <io_capability>, <oob_flag>, <auth_req>, <max_enq_key_size>, <init_key_dist>, <resp_key_dist> )
101      #          where <io_capability> 1 octet, <oob_flag> 1 octet, <auth_req> 1 octet, <max_enq_key_size> 1 octet, <init_key_dist> 1 octet, <resp_key_dist> 1 octet
102      #
103        if   ( opcode == SMPOpcode.SMP_PAIRING_REQUEST ):
104            self.data = [ opcode ] + list( args[:6] );
105      #
106      # encode ( SMPOpcode.SMP_PAIRING_RESPONSE, <io_capability>, <oob_flag>, <auth_req>, <max_enq_key_size>, <init_key_dist>, <resp_key_dist> )
107      #          where <io_capability> 1 octet, <oob_flag> 1 octet, <auth_req> 1 octet, <max_enq_key_size> 1 octet, <init_key_dist> 1 octet, <resp_key_dist> 1 octet
108      #
109        elif ( opcode == SMPOpcode.SMP_PAIRING_RESPONSE ):
110            self.data = [ opcode ] + list( args[:6] );
111      #
112      # encode ( SMPOpcode.SMP_PAIRING_CONFIRM, <confirm_value> )
113      #       where <confirm_value> 16 octets
114      #
115        elif ( opcode == SMPOpcode.SMP_PAIRING_CONFIRM ):
116            self.data = [ opcode ] + toArray( args[0], 16 );
117      #
118      # encode ( SMPOpcode.SMP_PAIRING_RANDOM, <random_value> )
119      #       where <random_value> 16 octets
120      #
121        elif ( opcode == SMPOpcode.SMP_PAIRING_RANDOM ):
122            self.data = [ opcode ] + toArray( args[0], 16 );
123      #
124      # encode ( SMPOpcode.SMP_PAIRING_FAILED, <reason> )
125      #       where <reason> 1 octet
126      #
127        elif ( opcode == SMPOpcode.SMP_PAIRING_FAILED ):
128            self.data = [ opcode ] + [ args[0] ];
129      #
130      # encode ( SMPOpcode.SMP_ENCRYPTION_INFORMATION, <ltk> )
131      #       where <ltk> 16 octets
132      #
133        elif ( opcode == SMPOpcode.SMP_ENCRYPTION_INFORMATION ):
134            self.data = [ opcode ] + toArray( args[0], 16 );
135      #
136      # encode ( SMPOpcode.SMP_CENTRAL_IDENTIFICATION, <ediv>, <rand> )
137      #       where <ediv> 2 octets; <rand> 8 octets
138      #
139        elif ( opcode == SMPOpcode.SMP_CENTRAL_IDENTIFICATION ):
140            self.data = [ opcode ] + toArray( args[0], 2 ) + toArray( args[1], 8 );
141      #
142      # encode ( SMPOpcode.SMP_IDENTITY_INFORMATION, <irk> )
143      #       where <irk> 16 octets
144      #
145        elif ( opcode == SMPOpcode.SMP_IDENTITY_INFORMATION ):
146            self.data = [ opcode ] + toArray( args[0], 16 );
147      #
148      # encode ( SMPOpcode.SMP_IDENTITY_ADDRESS_INFORMATION, <address_type>, <address> )
149      #       where <address_type> 1 octet; <address> 6 octets
150      #
151        elif ( opcode == SMPOpcode.SMP_IDENTITY_ADDRESS_INFORMATION ):
152            self.data = [ opcode ] + [ args[0] ] + toArray( args[1], 6 );
153      #
154      # encode ( SMPOpcode.SMP_SIGNING_INFORMATION, <csrk> )
155      #       where <csrk> 16 octets
156      #
157        elif ( opcode == SMPOpcode.SMP_SIGNING_INFORMATION ):
158            self.data = [ opcode ] + toArray( args[0], 16 );
159      #
160      # encode ( SMPOpcode.SMP_SECURITY_REQUEST, <properties> )
161      #       where <properties> 1 octet
162      #
163        elif ( opcode == SMPOpcode.SMP_SECURITY_REQUEST ):
164            self.data = [ opcode ] + [ args[0] ];
165      #
166      # encode ( SMPOpcode.SMP_PAIRING_PUBLIC_KEY, <key_X>, <key_Y> )
167      #       where <key_X> 32 octets; <key_Y> 32 octets
168      #
169        elif ( opcode == SMPOpcode.SMP_PAIRING_PUBLIC_KEY ):
170            self.data = [ opcode ] + toArray( args[0], 32 ) + toArray( args[1], 32 );
171      #
172      # encode ( SMPOpcode.SMP_PAIRING_DHKEY_CHECK, <check_value> )
173      #       where <check_value> 16 octets
174      #
175        elif ( opcode == SMPOpcode.SMP_PAIRING_DHKEY_CHECK ):
176            self.data = [ opcode ] + toArray( args[0], 16 );
177      #
178      # encode ( SMPOpcode.SMP_PAIRING_KEYPRESS_NOTIFICATION, <type> )
179      #       where <type> 1 octet
180      #
181        elif ( opcode == SMPOpcode.SMP_PAIRING_KEYPRESS_NOTIFICATION ):
182            self.data = [ opcode ] + [ args[0] ];
183      #
184      # The first two octets in the L2CAP PDU contains the length of the entire L2CAP PDU in octets, excluding the Length and CID fields.
185      #
186        if len(self.data) > 0:
187            self.data = toArray( len(self.data), 2 ) + toArray( SMP_CID, 2 ) + self.data;
188        return self.data;
189
190    def decode(self, data):
191        self.data = data[:];
192        size = toNumber( data[:2] );
193        cid = toNumber( data[2:4] );
194        opcode = SMPOpcode(data[4]);
195
196        result = { "opcode": opcode };
197      #
198      # decode ( SMPOpcode.SMP_PAIRING_REQUEST, <io_capability>, <oob_flag>, <auth_req>, <max_enq_key_size>, <init_key_dist>, <resp_key_dist> )
199      #          where <io_capability> 1 octet, <oob_flag> 1 octet, <auth_req> 1 octet, <max_enq_key_size> 1 octet, <init_key_dist> 1 octet, <resp_key_dist> 1 octet
200      #
201        if   ( opcode == SMPOpcode.SMP_PAIRING_REQUEST ):
202            result["capability"] = data[5];
203            result["oob"] = data[6];
204            result["auth"] = data[7];
205            result["max_key_size"] = data[8];
206            result["init_key_dist"] = data[9];
207            result["resp_key_dist"] = data[10];
208      #
209      # decode ( SMPOpcode.SMP_PAIRING_RESPONSE, <io_capability>, <oob_flag>, <auth_req>, <max_enq_key_size>, <init_key_dist>, <resp_key_dist> )
210      #          where <io_capability> 1 octet, <oob_flag> 1 octet, <auth_req> 1 octet, <max_enq_key_size> 1 octet, <init_key_dist> 1 octet, <resp_key_dist> 1 octet
211      #
212        elif ( opcode == SMPOpcode.SMP_PAIRING_RESPONSE ):
213            result["capability"] = data[5];
214            result["oob"] = data[6];
215            result["auth"] = data[7];
216            result["max_key_size"] = data[8];
217            result["init_key_dist"] = data[9];
218            result["resp_key_dist"] = data[10];
219      #
220      # decode ( SMPOpcode.SMP_PAIRING_CONFIRM, <confirm_value> )
221      #       where <confirm_value> 16 octets
222      #
223        elif ( opcode == SMPOpcode.SMP_PAIRING_CONFIRM ):
224            result["value"] = toNumber( data[5:21] );
225      #
226      # decode ( SMPOpcode.SMP_PAIRING_RANDOM, <random_value> )
227      #       where <random_value> 16 octets
228      #
229        elif ( opcode == SMPOpcode.SMP_PAIRING_RANDOM ):
230            result["value"] = toNumber( data[5:21] );
231      #
232      # decode ( SMPOpcode.SMP_PAIRING_FAILED, <reason> )
233      #       where <reason> 1 octet
234      #
235        elif ( opcode == SMPOpcode.SMP_PAIRING_FAILED ):
236            result["reason"] = (SMPError)(data[5]);
237      #
238      # decode ( SMPOpcode.SMP_ENCRYPTION_INFORMATION, <ltk> )
239      #       where <ltk> 16 octets
240      #
241        elif ( opcode == SMPOpcode.SMP_ENCRYPTION_INFORMATION ):
242            result["ltk"] = toNumber( data[5:21] );
243      #
244      # decode ( SMPOpcode.SMP_CENTRAL_IDENTIFICATION, <ediv>, <rand> )
245      #       where <ediv> 2 octets; <rand> 8 octets
246      #
247        elif ( opcode == SMPOpcode.SMP_CENTRAL_IDENTIFICATION ):
248            result["ediv"] = toNumber( data[5:7] );
249            result["rand"] = toNumber( data[7:15] );
250      #
251      # decode ( SMPOpcode.SMP_IDENTITY_INFORMATION, <irk> )
252      #       where <irk> 16 octets
253      #
254        elif ( opcode == SMPOpcode.SMP_IDENTITY_INFORMATION ):
255            result["irk"] = toNumber( data[5:21] );
256      #
257      # decode ( SMPOpcode.SMP_IDENTITY_ADDRESS_INFORMATION, <address_type>, <address> )
258      #       where <address_type> 1 octet; <address> 6 octets
259      #
260        elif ( opcode == SMPOpcode.SMP_IDENTITY_ADDRESS_INFORMATION ):
261            result["type"] = data[5];
262            result["address"] = toNumber( data[6:12] );
263      #
264      # decode ( SMPOpcode.SMP_SIGNING_INFORMATION, <csrk> )
265      #       where <csrk> 16 octets
266      #
267        elif ( opcode == SMPOpcode.SMP_SIGNING_INFORMATION ):
268            result["csrk"] = toNumber( data[5:21] );
269      #
270      # decode ( SMPOpcode.SMP_PAIRING_PUBLIC_KEY, <key_X>, <key_Y> )
271      #       where <key_X> 32 octets; <key_Y> 32 octets
272      #
273        elif ( opcode == SMPOpcode.SMP_PAIRING_PUBLIC_KEY ):
274            result["key_X"] = toNumber( data[5:37] );
275            result["key_Y"] = toNumber( data[37:69] );
276      #
277      # decode ( SMPOpcode.SMP_PAIRING_DHKEY_CHECK, <check_value> )
278      #       where <check_value> 16 octets
279      #
280        elif ( opcode == SMPOpcode.SMP_PAIRING_DHKEY_CHECK ):
281            result["value"] = toNumber( data[5:21] );
282      #
283      # decode ( SMPOpcode.SMP_PAIRING_KEYPRESS_NOTIFICATION, <type> )
284      #       where <type> 1 octet
285      #
286        elif ( opcode == SMPOpcode.SMP_PAIRING_KEYPRESS_NOTIFICATION ):
287            result["change"] = (SMPPasskey)(data[5]);
288
289        return result;
290
291    def error(self, code):
292        errorTexts = {
293            SMPError.SMP_ERROR_PASSKEY:
294                "The user input of passkey failed.",
295            SMPError.SMP_ERROR_OOB:
296                "The OOB data is not available.",
297            SMPError.SMP_ERROR_AUTHENTICATION:
298                "The pairing procedure cannot be performed as authentication requirements cannot be met.",
299            SMPError.SMP_ERROR_CONFIRM:
300                "The confirm value does not match the calculated compare value.",
301            SMPError.SMP_ERROR_PAIRING:
302                "Pairing is not supported by the device.",
303            SMPError.SMP_ERROR_ENCRYPTION:
304                "The resultant encryption key size is insufficient for the security requirements.",
305            SMPError.SMP_ERROR_COMMAND:
306                "The SMP command received is not supported.",
307            SMPError.SMP_ERROR_REASON:
308                "Pairing failed due to an unspecified reason.",
309            SMPError.SMP_ERROR_REPEATED_ATTEMPTS:
310                "Pairing or authentication procedure is disallowed because too little time has elapsed since last pairing- or security-request.",
311            SMPError.SMP_ERROR_INVALID_PARAMETERS:
312                "The command length is invalid or a parameter is outside the specified range.",
313            SMPError.SMP_ERROR_DHKEY_CHECK:
314                "The DHKey Check value received doesn't match the one calculated.",
315            SMPError.SMP_ERROR_NUMERIC_COMPARISON:
316                "The confirm values in the numeric comparison protocol do not match.",
317            SMPError.SMP_ERROR_PAIRING_IN_PROGRESS:
318                "The pairing over LE transport failed due to a Pairing Request sent over the BR/EDR transport.",
319            SMPError.SMP_ERROR_CROSS_KEY_GENERATION:
320                "The BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport."
321        };
322        if code in errorTexts:
323            return errorTexts[code];
324        else:
325            return "Unknown error code (%d)" % code;
326