@bota-dev/react-native-sdk 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +30 -2
  2. package/lib/commonjs/ble/constants.js +28 -2
  3. package/lib/commonjs/ble/constants.js.map +1 -1
  4. package/lib/commonjs/ble/parsers.js +128 -0
  5. package/lib/commonjs/ble/parsers.js.map +1 -1
  6. package/lib/commonjs/managers/DeviceManager.js +179 -0
  7. package/lib/commonjs/managers/DeviceManager.js.map +1 -1
  8. package/lib/commonjs/utils/crypto.js +169 -0
  9. package/lib/commonjs/utils/crypto.js.map +1 -0
  10. package/lib/commonjs/utils/index.js +12 -0
  11. package/lib/commonjs/utils/index.js.map +1 -1
  12. package/lib/module/ble/constants.js +26 -0
  13. package/lib/module/ble/constants.js.map +1 -1
  14. package/lib/module/ble/parsers.js +124 -1
  15. package/lib/module/ble/parsers.js.map +1 -1
  16. package/lib/module/managers/DeviceManager.js +181 -2
  17. package/lib/module/managers/DeviceManager.js.map +1 -1
  18. package/lib/module/utils/crypto.js +160 -0
  19. package/lib/module/utils/crypto.js.map +1 -0
  20. package/lib/module/utils/index.js +1 -0
  21. package/lib/module/utils/index.js.map +1 -1
  22. package/lib/typescript/src/ble/constants.d.ts +18 -0
  23. package/lib/typescript/src/ble/constants.d.ts.map +1 -1
  24. package/lib/typescript/src/ble/parsers.d.ts +34 -1
  25. package/lib/typescript/src/ble/parsers.d.ts.map +1 -1
  26. package/lib/typescript/src/managers/DeviceManager.d.ts +73 -1
  27. package/lib/typescript/src/managers/DeviceManager.d.ts.map +1 -1
  28. package/lib/typescript/src/models/Device.d.ts +65 -0
  29. package/lib/typescript/src/models/Device.d.ts.map +1 -1
  30. package/lib/typescript/src/utils/crypto.d.ts +83 -0
  31. package/lib/typescript/src/utils/crypto.d.ts.map +1 -0
  32. package/lib/typescript/src/utils/index.d.ts +1 -0
  33. package/lib/typescript/src/utils/index.d.ts.map +1 -1
  34. package/package.json +6 -2
  35. package/src/ble/constants.ts +26 -0
  36. package/src/ble/parsers.ts +131 -0
  37. package/src/managers/DeviceManager.ts +244 -0
  38. package/src/models/Device.ts +76 -0
  39. package/src/utils/crypto.ts +221 -0
  40. package/src/utils/index.ts +1 -0
@@ -11,6 +11,7 @@ import {
11
11
  SERVICE_DEVICE_INFO,
12
12
  SERVICE_BOTA_PROVISIONING,
13
13
  SERVICE_BOTA_CONTROL,
14
+ SERVICE_BOTA_WIFI_CONFIG,
14
15
  CHAR_SERIAL_NUMBER,
15
16
  CHAR_FIRMWARE_REVISION,
16
17
  CHAR_HARDWARE_REVISION,
@@ -22,6 +23,9 @@ import {
22
23
  CHAR_RECORDING_CONTROL,
23
24
  CHAR_RECORDING_STATUS,
24
25
  CHAR_TIME_SYNC,
26
+ CHAR_WIFI_GRANT,
27
+ CHAR_WIFI_CREDENTIAL,
28
+ CHAR_WIFI_STATUS,
25
29
  API_ENDPOINT_PRODUCTION,
26
30
  API_ENDPOINT_SANDBOX,
27
31
  PROVISIONING_SUCCESS,
@@ -41,6 +45,9 @@ import {
41
45
  parsePairingState,
42
46
  parseDeviceStatus,
43
47
  createTimeSyncData,
48
+ parseWiFiStatusInfo,
49
+ parseWiFiConfigResult,
50
+ createWiFiGrantPacket,
44
51
  } from '../ble/parsers';
45
52
  import type {
46
53
  DiscoveredDevice,
@@ -53,6 +60,10 @@ import type {
53
60
  // RecordingCommand, // TODO: Re-enable when used
54
61
  StartRecordingOptions,
55
62
  StopRecordingOptions,
63
+ WiFiConfigGrant,
64
+ WiFiCredentials,
65
+ WiFiConfigResult,
66
+ WiFiStatusInfo,
56
67
  } from '../models/Device';
57
68
  import type { DeviceManagerEvents } from '../models/Status';
58
69
  import {
@@ -60,6 +71,11 @@ import {
60
71
  ProvisioningError,
61
72
  } from '../utils/errors';
62
73
  import { logger } from '../utils/logger';
74
+ import {
75
+ deriveSessionKey,
76
+ encryptWiFiCredentials,
77
+ formatWiFiCredentialPacket,
78
+ } from '../utils/crypto';
63
79
 
64
80
  const log = logger.tag('DeviceManager');
65
81
 
@@ -776,6 +792,234 @@ export class DeviceManager extends EventEmitter<DeviceManagerEvents> {
776
792
  };
777
793
  }
778
794
 
795
+ // ============================================================================
796
+ // WiFi Upload Configuration
797
+ // ============================================================================
798
+
799
+ /**
800
+ * Configure WiFi credentials on device via BLE.
801
+ * Requires a WiFi configuration grant from backend.
802
+ *
803
+ * @param deviceId - Connected device ID
804
+ * @param credentials - WiFi network credentials
805
+ * @param grant - WiFi config grant from backend
806
+ * @returns Configuration result
807
+ *
808
+ * @example
809
+ * ```typescript
810
+ * // 1. Get grant from backend
811
+ * const grant = await api.createWiFiConfigGrant(deviceId);
812
+ *
813
+ * // 2. Configure device via BLE
814
+ * const result = await deviceManager.configureWiFi(
815
+ * deviceId,
816
+ * { ssid: 'MyNetwork', password: 'password123', securityType: 'WPA2' },
817
+ * grant
818
+ * );
819
+ *
820
+ * if (result.success) {
821
+ * console.log('WiFi configured successfully');
822
+ * }
823
+ * ```
824
+ */
825
+ async configureWiFi(
826
+ deviceId: string,
827
+ credentials: WiFiCredentials,
828
+ grant: WiFiConfigGrant
829
+ ): Promise<WiFiConfigResult> {
830
+ log.info('Configuring WiFi on device', { deviceId, ssid: credentials.ssid });
831
+
832
+ try {
833
+ // Step 1: Submit WiFi config grant to device
834
+ const grantPacket = createWiFiGrantPacket(grant.grantBlob);
835
+ await this.bleManager.writeCharacteristic(
836
+ deviceId,
837
+ SERVICE_BOTA_WIFI_CONFIG,
838
+ CHAR_WIFI_GRANT,
839
+ grantPacket
840
+ );
841
+
842
+ log.debug('WiFi grant submitted');
843
+
844
+ // Step 2: Derive K_session from grant
845
+ const sessionKey = deriveSessionKey(grant.grantBlob);
846
+
847
+ // Step 3: Encrypt WiFi credentials with K_session
848
+ const encrypted = encryptWiFiCredentials(
849
+ credentials.ssid,
850
+ credentials.password,
851
+ sessionKey
852
+ );
853
+
854
+ // Step 4: Format credential packet for BLE transmission
855
+ const credentialPacket = formatWiFiCredentialPacket(encrypted);
856
+
857
+ log.debug('Sending encrypted WiFi credentials', {
858
+ packetSize: credentialPacket.length,
859
+ });
860
+
861
+ // Step 5: Write encrypted credentials to device
862
+ await this.bleManager.writeCharacteristic(
863
+ deviceId,
864
+ SERVICE_BOTA_WIFI_CONFIG,
865
+ CHAR_WIFI_CREDENTIAL,
866
+ credentialPacket
867
+ );
868
+
869
+ // Step 6: Wait for configuration result
870
+ const result = await this.waitForWiFiConfigResult(deviceId);
871
+
872
+ if (result.success) {
873
+ log.info('WiFi configuration successful', { deviceId });
874
+ } else {
875
+ log.warn('WiFi configuration failed', { deviceId, error: result.error });
876
+ }
877
+
878
+ return result;
879
+ } catch (error) {
880
+ log.error('WiFi configuration error', error instanceof Error ? error : undefined);
881
+ throw new DeviceError(
882
+ `Failed to configure WiFi: ${error instanceof Error ? error.message : 'Unknown error'}`,
883
+ 'WIFI_CONFIG_ERROR',
884
+ deviceId,
885
+ error instanceof Error ? error : undefined
886
+ );
887
+ }
888
+ }
889
+
890
+ /**
891
+ * Get WiFi connection status from device.
892
+ *
893
+ * @param deviceId - Connected device ID
894
+ * @returns WiFi status information
895
+ *
896
+ * @example
897
+ * ```typescript
898
+ * const status = await deviceManager.getWiFiStatus(deviceId);
899
+ * console.log('WiFi status:', status.status);
900
+ * if (status.status === 'connected') {
901
+ * console.log('Connected to:', status.ssid);
902
+ * console.log('Signal strength:', status.signalStrength);
903
+ * }
904
+ * ```
905
+ */
906
+ async getWiFiStatus(deviceId: string): Promise<WiFiStatusInfo> {
907
+ log.debug('Reading WiFi status', { deviceId });
908
+
909
+ try {
910
+ const data = await this.bleManager.readCharacteristic(
911
+ deviceId,
912
+ SERVICE_BOTA_WIFI_CONFIG,
913
+ CHAR_WIFI_STATUS
914
+ );
915
+
916
+ const status = parseWiFiStatusInfo(data);
917
+
918
+ log.debug('WiFi status', { deviceId, status: status.status, ssid: status.ssid });
919
+
920
+ return status;
921
+ } catch (error) {
922
+ log.error('Failed to read WiFi status', error instanceof Error ? error : undefined);
923
+ throw new DeviceError(
924
+ `Failed to read WiFi status: ${error instanceof Error ? error.message : 'Unknown error'}`,
925
+ 'WIFI_STATUS_ERROR',
926
+ deviceId,
927
+ error instanceof Error ? error : undefined
928
+ );
929
+ }
930
+ }
931
+
932
+ /**
933
+ * Subscribe to WiFi status updates from device.
934
+ *
935
+ * @param deviceId - Connected device ID
936
+ * @param callback - Callback function for status updates
937
+ * @returns Subscription object (call .remove() to unsubscribe)
938
+ *
939
+ * @example
940
+ * ```typescript
941
+ * const subscription = deviceManager.subscribeToWiFiStatus(deviceId, (status) => {
942
+ * console.log('WiFi status update:', status.status);
943
+ * if (status.status === 'connected') {
944
+ * console.log('Connected to:', status.ssid);
945
+ * } else if (status.status === 'failed') {
946
+ * console.error('Connection failed:', status.lastError);
947
+ * }
948
+ * });
949
+ *
950
+ * // Later: unsubscribe
951
+ * subscription.remove();
952
+ * ```
953
+ */
954
+ subscribeToWiFiStatus(
955
+ deviceId: string,
956
+ callback: (status: WiFiStatusInfo) => void
957
+ ): Subscription {
958
+ log.debug('Subscribing to WiFi status', { deviceId });
959
+
960
+ return this.bleManager.subscribeToCharacteristic(
961
+ deviceId,
962
+ SERVICE_BOTA_WIFI_CONFIG,
963
+ CHAR_WIFI_STATUS,
964
+ (data) => {
965
+ try {
966
+ const status = parseWiFiStatusInfo(data);
967
+ callback(status);
968
+ } catch (error) {
969
+ log.error('Failed to parse WiFi status', error instanceof Error ? error : undefined);
970
+ }
971
+ },
972
+ (error) => {
973
+ log.error('WiFi status subscription error', error);
974
+ }
975
+ );
976
+ }
977
+
978
+ /**
979
+ * Wait for WiFi configuration result from device.
980
+ */
981
+ private waitForWiFiConfigResult(deviceId: string): Promise<WiFiConfigResult> {
982
+ return new Promise((resolve, reject) => {
983
+ const timeout = setTimeout(() => {
984
+ subscription.remove();
985
+ reject(new DeviceError(
986
+ 'WiFi configuration timeout',
987
+ 'WIFI_CONFIG_TIMEOUT',
988
+ deviceId
989
+ ));
990
+ }, OPERATION_TIMEOUT);
991
+
992
+ const subscription = this.bleManager.subscribeToCharacteristic(
993
+ deviceId,
994
+ SERVICE_BOTA_WIFI_CONFIG,
995
+ CHAR_WIFI_STATUS,
996
+ (data) => {
997
+ try {
998
+ // Configuration result comes via status updates
999
+ const result = parseWiFiConfigResult(data);
1000
+
1001
+ clearTimeout(timeout);
1002
+ subscription.remove();
1003
+ resolve(result);
1004
+ } catch (error) {
1005
+ // Ignore parse errors, wait for valid result
1006
+ log.debug('Ignoring invalid WiFi config result', { error: error instanceof Error ? error.message : String(error) });
1007
+ }
1008
+ },
1009
+ (error) => {
1010
+ clearTimeout(timeout);
1011
+ subscription.remove();
1012
+ reject(new DeviceError(
1013
+ `WiFi config result error: ${error.message}`,
1014
+ 'WIFI_CONFIG_RESULT_ERROR',
1015
+ deviceId,
1016
+ error
1017
+ ));
1018
+ }
1019
+ );
1020
+ });
1021
+ }
1022
+
779
1023
  /**
780
1024
  * Clean up resources
781
1025
  */
@@ -76,6 +76,20 @@ export interface DiscoveredDevice {
76
76
  discoveredAt: Date;
77
77
  }
78
78
 
79
+ /**
80
+ * Device capabilities (bitmask)
81
+ */
82
+ export interface DeviceCapabilities {
83
+ /** Supports Bluetooth Sync (BLE transfer) */
84
+ bleSync: boolean;
85
+ /** Supports WiFi Upload */
86
+ wifiUpload: boolean;
87
+ /** Supports Cellular Upload (4G/LTE) */
88
+ lteUpload: boolean;
89
+ /** Supports Remote Recording Control */
90
+ remoteRecord: boolean;
91
+ }
92
+
79
93
  /**
80
94
  * Device after successful BLE connection
81
95
  */
@@ -96,6 +110,8 @@ export interface ConnectedDevice {
96
110
  connectionState: ConnectionState;
97
111
  /** Negotiated MTU size */
98
112
  mtu: number;
113
+ /** Device capabilities */
114
+ capabilities?: DeviceCapabilities;
99
115
  }
100
116
 
101
117
  /**
@@ -255,3 +271,63 @@ export interface RecordingState {
255
271
  /** Who initiated the recording */
256
272
  initiatedBy?: 'local' | 'remote';
257
273
  }
274
+
275
+ // ============================================================================
276
+ // WiFi Upload Configuration Types
277
+ // ============================================================================
278
+
279
+ /**
280
+ * WiFi security type
281
+ */
282
+ export type WiFiSecurityType = 'WPA2' | 'WPA3' | 'WEP' | 'OPEN';
283
+
284
+ /**
285
+ * WiFi connection status
286
+ */
287
+ export type WiFiStatus = 'idle' | 'connecting' | 'connected' | 'failed' | 'disconnected';
288
+
289
+ /**
290
+ * WiFi configuration grant from backend
291
+ */
292
+ export interface WiFiConfigGrant {
293
+ /** Grant blob (JWT token) */
294
+ grantBlob: string;
295
+ /** Expiration timestamp */
296
+ expiresAt: Date;
297
+ }
298
+
299
+ /**
300
+ * WiFi credentials to configure
301
+ */
302
+ export interface WiFiCredentials {
303
+ /** WiFi network SSID */
304
+ ssid: string;
305
+ /** WiFi password */
306
+ password: string;
307
+ /** Security type (default: WPA2) */
308
+ securityType?: WiFiSecurityType;
309
+ }
310
+
311
+ /**
312
+ * WiFi configuration result from device
313
+ */
314
+ export interface WiFiConfigResult {
315
+ /** Whether configuration was successful */
316
+ success: boolean;
317
+ /** Error code if failed */
318
+ error?: 'invalid_grant' | 'grant_expired' | 'decryption_error' | 'storage_error' | 'unknown';
319
+ }
320
+
321
+ /**
322
+ * WiFi status information from device
323
+ */
324
+ export interface WiFiStatusInfo {
325
+ /** Current WiFi connection status */
326
+ status: WiFiStatus;
327
+ /** Connected SSID (if connected) */
328
+ ssid?: string;
329
+ /** Signal strength (0-100) */
330
+ signalStrength?: number;
331
+ /** Last connection error (if failed) */
332
+ lastError?: string;
333
+ }
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Cryptographic utilities for WiFi Upload configuration
3
+ *
4
+ * NOTE: This module requires react-native-quick-crypto to be installed.
5
+ * Add to your app: npm install react-native-quick-crypto
6
+ */
7
+
8
+ import { Buffer } from 'buffer';
9
+
10
+ // Type-only import to avoid runtime errors if crypto is not installed
11
+ type CryptoModule = typeof import('crypto');
12
+
13
+ /**
14
+ * Get crypto module (lazy loaded to avoid errors if not installed)
15
+ */
16
+ function getCrypto(): CryptoModule {
17
+ try {
18
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
19
+ return require('react-native-quick-crypto') as CryptoModule;
20
+ } catch {
21
+ throw new Error(
22
+ 'WiFi Upload requires react-native-quick-crypto. ' +
23
+ 'Install it with: npm install react-native-quick-crypto'
24
+ );
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Derive K_session from WiFi config grant.
30
+ * K_session is used by device and app to encrypt/decrypt WiFi credentials in transit.
31
+ *
32
+ * Derivation: K_session = HMAC-SHA256(grant_blob, "BOTA_WIFI_SESSION_V1")
33
+ *
34
+ * @param grantBlob - Base64-encoded JWT grant from backend
35
+ * @returns 32-byte K_session as hex string
36
+ */
37
+ export function deriveSessionKey(grantBlob: string): string {
38
+ const crypto = getCrypto();
39
+
40
+ return crypto
41
+ .createHmac('sha256', grantBlob)
42
+ .update('BOTA_WIFI_SESSION_V1')
43
+ .digest('hex');
44
+ }
45
+
46
+ /**
47
+ * Encrypt WiFi credentials for BLE transmission using ChaCha20-Poly1305.
48
+ *
49
+ * Format sent to device:
50
+ * - 12 bytes: nonce (random)
51
+ * - N bytes: encrypted SSID
52
+ * - 16 bytes: auth tag for SSID
53
+ * - M bytes: encrypted password
54
+ * - 16 bytes: auth tag for password
55
+ *
56
+ * @param ssid - WiFi network SSID
57
+ * @param password - WiFi password
58
+ * @param sessionKey - K_session derived from grant (hex string)
59
+ * @returns Encrypted payload ready for BLE transmission
60
+ */
61
+ export function encryptWiFiCredentials(
62
+ ssid: string,
63
+ password: string,
64
+ sessionKey: string
65
+ ): {
66
+ nonce: Buffer;
67
+ ssidEncrypted: Buffer;
68
+ ssidAuthTag: Buffer;
69
+ passwordEncrypted: Buffer;
70
+ passwordAuthTag: Buffer;
71
+ } {
72
+ const crypto = getCrypto();
73
+
74
+ // Convert session key from hex to Buffer
75
+ const keyBuffer = Buffer.from(sessionKey, 'hex');
76
+
77
+ // Generate random nonce (12 bytes for ChaCha20-Poly1305)
78
+ const nonce = crypto.randomBytes(12);
79
+
80
+ // Encrypt SSID
81
+ const ssidCipher = crypto.createCipheriv('chacha20-poly1305', keyBuffer, nonce, {
82
+ authTagLength: 16,
83
+ });
84
+ const ssidEncrypted = Buffer.concat([
85
+ ssidCipher.update(ssid, 'utf-8'),
86
+ ssidCipher.final(),
87
+ ]);
88
+ const ssidAuthTag = ssidCipher.getAuthTag();
89
+
90
+ // Encrypt password (reuse same nonce with different additional data)
91
+ const passwordCipher = crypto.createCipheriv('chacha20-poly1305', keyBuffer, nonce, {
92
+ authTagLength: 16,
93
+ });
94
+ // Use different AAD to prevent nonce reuse issues
95
+ passwordCipher.setAAD(Buffer.from('password'), { plaintextLength: Buffer.byteLength(password, 'utf-8') });
96
+ const passwordEncrypted = Buffer.concat([
97
+ passwordCipher.update(password, 'utf-8'),
98
+ passwordCipher.final(),
99
+ ]);
100
+ const passwordAuthTag = passwordCipher.getAuthTag();
101
+
102
+ return {
103
+ nonce,
104
+ ssidEncrypted,
105
+ ssidAuthTag,
106
+ passwordEncrypted,
107
+ passwordAuthTag,
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Decrypt WiFi credentials received from device (if needed for verification).
113
+ *
114
+ * @param encrypted - Encrypted SSID or password
115
+ * @param nonce - 12-byte nonce
116
+ * @param authTag - 16-byte authentication tag
117
+ * @param sessionKey - K_session derived from grant (hex string)
118
+ * @param aad - Additional authenticated data (optional, use 'password' for password field)
119
+ * @returns Decrypted plaintext
120
+ */
121
+ export function decryptWiFiCredential(
122
+ encrypted: Buffer,
123
+ nonce: Buffer,
124
+ authTag: Buffer,
125
+ sessionKey: string,
126
+ aad?: string
127
+ ): string {
128
+ const crypto = getCrypto();
129
+
130
+ // Convert session key from hex to Buffer
131
+ const keyBuffer = Buffer.from(sessionKey, 'hex');
132
+
133
+ // Decrypt with ChaCha20-Poly1305
134
+ const decipher = crypto.createDecipheriv('chacha20-poly1305', keyBuffer, nonce, {
135
+ authTagLength: 16,
136
+ });
137
+
138
+ decipher.setAuthTag(authTag);
139
+
140
+ if (aad) {
141
+ decipher.setAAD(Buffer.from(aad), { plaintextLength: encrypted.length });
142
+ }
143
+
144
+ const decrypted = Buffer.concat([
145
+ decipher.update(encrypted),
146
+ decipher.final(),
147
+ ]);
148
+
149
+ return decrypted.toString('utf-8');
150
+ }
151
+
152
+ /**
153
+ * Format encrypted WiFi credentials for BLE transmission.
154
+ *
155
+ * Packet format:
156
+ * [nonce (12 bytes)][ssid_encrypted (N bytes)][ssid_tag (16 bytes)]
157
+ * [password_encrypted (M bytes)][password_tag (16 bytes)]
158
+ *
159
+ * @param encrypted - Result from encryptWiFiCredentials
160
+ * @returns Single buffer ready for BLE write
161
+ */
162
+ export function formatWiFiCredentialPacket(encrypted: {
163
+ nonce: Buffer;
164
+ ssidEncrypted: Buffer;
165
+ ssidAuthTag: Buffer;
166
+ passwordEncrypted: Buffer;
167
+ passwordAuthTag: Buffer;
168
+ }): Buffer {
169
+ return Buffer.concat([
170
+ encrypted.nonce,
171
+ encrypted.ssidEncrypted,
172
+ encrypted.ssidAuthTag,
173
+ encrypted.passwordEncrypted,
174
+ encrypted.passwordAuthTag,
175
+ ]);
176
+ }
177
+
178
+ /**
179
+ * Parse encrypted WiFi credential packet received from device.
180
+ *
181
+ * @param packet - Buffer received from BLE
182
+ * @param ssidLength - Expected SSID encrypted length
183
+ * @param passwordLength - Expected password encrypted length
184
+ * @returns Parsed components
185
+ */
186
+ export function parseWiFiCredentialPacket(
187
+ packet: Buffer,
188
+ ssidLength: number,
189
+ passwordLength: number
190
+ ): {
191
+ nonce: Buffer;
192
+ ssidEncrypted: Buffer;
193
+ ssidAuthTag: Buffer;
194
+ passwordEncrypted: Buffer;
195
+ passwordAuthTag: Buffer;
196
+ } {
197
+ let offset = 0;
198
+
199
+ const nonce = packet.subarray(offset, offset + 12);
200
+ offset += 12;
201
+
202
+ const ssidEncrypted = packet.subarray(offset, offset + ssidLength);
203
+ offset += ssidLength;
204
+
205
+ const ssidAuthTag = packet.subarray(offset, offset + 16);
206
+ offset += 16;
207
+
208
+ const passwordEncrypted = packet.subarray(offset, offset + passwordLength);
209
+ offset += passwordLength;
210
+
211
+ const passwordAuthTag = packet.subarray(offset, offset + 16);
212
+ offset += 16;
213
+
214
+ return {
215
+ nonce,
216
+ ssidEncrypted,
217
+ ssidAuthTag,
218
+ passwordEncrypted,
219
+ passwordAuthTag,
220
+ };
221
+ }
@@ -5,3 +5,4 @@
5
5
  export * from './errors';
6
6
  export { logger } from './logger';
7
7
  export * from './retry';
8
+ export * from './crypto';