@matter/react-native 0.16.0-alpha.0-20250809-ee8375bcb → 0.16.0-alpha.0-20250812-285b75d83

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 (49) hide show
  1. package/dist/cjs/ble/BleReactNative.d.ts +5 -6
  2. package/dist/cjs/ble/BleReactNative.d.ts.map +1 -1
  3. package/dist/cjs/ble/BleReactNative.js +3 -6
  4. package/dist/cjs/ble/BleReactNative.js.map +1 -1
  5. package/dist/cjs/ble/ReactNativeBleChannel.d.ts.map +1 -1
  6. package/dist/cjs/ble/ReactNativeBleChannel.js +6 -3
  7. package/dist/cjs/ble/ReactNativeBleChannel.js.map +1 -1
  8. package/dist/cjs/ble/ReactNativeBleClient.d.ts.map +1 -1
  9. package/dist/cjs/ble/ReactNativeBleClient.js +27 -17
  10. package/dist/cjs/ble/ReactNativeBleClient.js.map +1 -1
  11. package/dist/cjs/crypto/ReactNativeCrypto.d.ts +21 -8
  12. package/dist/cjs/crypto/ReactNativeCrypto.d.ts.map +1 -1
  13. package/dist/cjs/crypto/ReactNativeCrypto.js +58 -56
  14. package/dist/cjs/crypto/ReactNativeCrypto.js.map +2 -2
  15. package/dist/cjs/net/NetworkReactNative.d.ts.map +1 -1
  16. package/dist/cjs/net/NetworkReactNative.js +0 -2
  17. package/dist/cjs/net/NetworkReactNative.js.map +1 -1
  18. package/dist/cjs/net/UdpChannelReactNative.d.ts +1 -1
  19. package/dist/cjs/net/UdpChannelReactNative.d.ts.map +1 -1
  20. package/dist/cjs/net/UdpChannelReactNative.js +6 -3
  21. package/dist/cjs/net/UdpChannelReactNative.js.map +1 -1
  22. package/dist/esm/ble/BleReactNative.d.ts +5 -6
  23. package/dist/esm/ble/BleReactNative.d.ts.map +1 -1
  24. package/dist/esm/ble/BleReactNative.js +3 -6
  25. package/dist/esm/ble/BleReactNative.js.map +1 -1
  26. package/dist/esm/ble/ReactNativeBleChannel.d.ts.map +1 -1
  27. package/dist/esm/ble/ReactNativeBleChannel.js +6 -3
  28. package/dist/esm/ble/ReactNativeBleChannel.js.map +1 -1
  29. package/dist/esm/ble/ReactNativeBleClient.d.ts.map +1 -1
  30. package/dist/esm/ble/ReactNativeBleClient.js +27 -17
  31. package/dist/esm/ble/ReactNativeBleClient.js.map +1 -1
  32. package/dist/esm/crypto/ReactNativeCrypto.d.ts +21 -8
  33. package/dist/esm/crypto/ReactNativeCrypto.d.ts.map +1 -1
  34. package/dist/esm/crypto/ReactNativeCrypto.js +62 -58
  35. package/dist/esm/crypto/ReactNativeCrypto.js.map +1 -1
  36. package/dist/esm/net/NetworkReactNative.d.ts.map +1 -1
  37. package/dist/esm/net/NetworkReactNative.js +0 -2
  38. package/dist/esm/net/NetworkReactNative.js.map +1 -1
  39. package/dist/esm/net/UdpChannelReactNative.d.ts +1 -1
  40. package/dist/esm/net/UdpChannelReactNative.d.ts.map +1 -1
  41. package/dist/esm/net/UdpChannelReactNative.js +6 -3
  42. package/dist/esm/net/UdpChannelReactNative.js.map +1 -1
  43. package/package.json +6 -7
  44. package/src/ble/BleReactNative.ts +5 -9
  45. package/src/ble/ReactNativeBleChannel.ts +6 -5
  46. package/src/ble/ReactNativeBleClient.ts +27 -19
  47. package/src/crypto/ReactNativeCrypto.ts +108 -106
  48. package/src/net/NetworkReactNative.ts +3 -10
  49. package/src/net/UdpChannelReactNative.ts +16 -5
@@ -22,7 +22,7 @@ export class ReactNativeBleClient {
22
22
  private deviceDiscoveredCallback: ((peripheral: Device, manufacturerData: Uint8Array) => void) | undefined;
23
23
 
24
24
  constructor() {
25
- // this.bleMnager.setLogLevel(LogLevel.Verbose)
25
+ // this.bleManager.setLogLevel(LogLevel.Verbose)
26
26
  const subscription = this.bleManager.onStateChange(state => {
27
27
  this.bleState = state;
28
28
  logger.debug(`BLE state changed to ${state}`);
@@ -53,7 +53,7 @@ export class ReactNativeBleClient {
53
53
  subscription.remove();
54
54
  void this.stopScanning();
55
55
  }
56
- });
56
+ }, true);
57
57
  }
58
58
 
59
59
  public setDiscoveryCallback(callback: (peripheral: Device, manufacturerData: Uint8Array) => void) {
@@ -70,23 +70,31 @@ export class ReactNativeBleClient {
70
70
  if (this.bleState === BluetoothState.PoweredOn) {
71
71
  logger.debug("Start BLE scanning for Matter Services ...");
72
72
  this.isScanning = true;
73
- await this.bleManager.startDeviceScan([BLE_MATTER_SERVICE_UUID], {}, (error, peripheral) => {
74
- if (error !== null || peripheral === null) {
75
- this.isScanning = false;
76
- logger.error("Error while scanning for BLE devices", error);
77
- if (this.shouldScan) {
78
- this.startScanning().catch(error =>
79
- logger.error("Error while restarting scanning after error", error),
80
- );
81
- } else {
82
- this.stopScanning().catch(error =>
83
- logger.error("Error while stopping scanning after error", error),
84
- );
73
+ await this.bleManager.startDeviceScan(
74
+ null,
75
+ {
76
+ allowDuplicates: false,
77
+ scanMode: 2, // Low Latency
78
+ callbackType: 1, // All matches
79
+ },
80
+ (error, peripheral) => {
81
+ if (error !== null || peripheral === null) {
82
+ this.isScanning = false;
83
+ logger.error("Error while scanning for BLE devices", error);
84
+ if (this.shouldScan) {
85
+ this.startScanning().catch(error =>
86
+ logger.error("Error while restarting scanning after error", error),
87
+ );
88
+ } else {
89
+ this.stopScanning().catch(error =>
90
+ logger.error("Error while stopping scanning after error", error),
91
+ );
92
+ }
93
+ return;
85
94
  }
86
- return;
87
- }
88
- this.handleDiscoveredDevice(peripheral);
89
- });
95
+ this.handleDiscoveredDevice(peripheral);
96
+ },
97
+ );
90
98
  } else {
91
99
  logger.debug("ble state is not poweredOn ... delay scanning till poweredOn");
92
100
  }
@@ -111,7 +119,7 @@ export class ReactNativeBleClient {
111
119
  logger.info(`Peripheral ${peripheral.id} is not connectable ... ignoring`);
112
120
  return;
113
121
  }
114
- const matterServiceDataBase64 = peripheral.serviceData?.[BLE_MATTER_SERVICE_UUID];
122
+ const matterServiceDataBase64 = peripheral.serviceData?.[BLE_MATTER_SERVICE_UUID.toLowerCase()];
115
123
  if (matterServiceDataBase64 === undefined) {
116
124
  logger.info(`Peripheral ${peripheral.id} does not advertise Matter Service ... ignoring`);
117
125
  return;
@@ -4,125 +4,127 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { install } from "react-native-quick-crypto";
8
-
9
- install(); // Install the react-native crypto module
10
-
11
7
  import {
8
+ Bytes,
12
9
  Crypto,
13
- CRYPTO_HASH_ALGORITHM,
14
- CryptoDsaEncoding,
15
- CryptoVerifyError,
10
+ CRYPTO_HASH_LEN_BYTES,
11
+ CRYPTO_SYMMETRIC_KEY_LENGTH,
16
12
  Environment,
13
+ PrivateKey,
14
+ PublicKey,
17
15
  StandardCrypto,
16
+ WebCrypto,
18
17
  } from "#general";
19
- import { NodeJsCrypto } from "#nodejs";
20
-
21
- import jwk2pem from "jwk-to-pem";
22
-
23
- // @ts-expect-error No types but all fine
24
- crypto.hkdf = (
25
- digest: "sha1" | "sha256" | "sha384" | "sha512",
26
- ikm: Uint8Array,
27
- salt: Uint8Array,
28
- info: Uint8Array,
29
- keylen: number,
30
- ) => {
31
- const hashlen = parseInt(digest.substr(3), 10) >> 3 || 20;
32
- const prk = crypto
33
- // @ts-expect-error No types but all fine
34
- .createHmac(digest, salt.byteLength ? salt : new Uint8Array(hashlen))
35
- .update(ikm)
36
- .digest();
37
-
38
- // T(0) = empty
39
- // T(1) = HMAC(PRK, T(0) | info | 0x01)
40
- // T(2) = HMAC(PRK, T(1) | info | 0x02)
41
- // T(3) = HMAC(PRK, T(2) | info | 0x03)
42
- // ...
43
- // T(N) = HMAC(PRK, T(N-1) | info | N)
44
-
45
- const N = Math.ceil(keylen / hashlen);
46
-
47
- // Single T buffer to accomodate T = T(1) | T(2) | T(3) | ... | T(N)
48
- // with a little extra for info | N during T(N)
49
- const T = new Uint8Array(hashlen * N + info.byteLength + 1);
50
- let prev = 0;
51
- let start = 0;
52
- for (let c = 1; c <= N; c++) {
53
- T.set(info, start);
54
- T[start + info.byteLength] = c;
55
-
56
- T.set(
57
- crypto
58
- // @ts-expect-error No types but all fine
59
- .createHmac(digest, prk)
60
- .update(T.subarray(prev, start + info.byteLength + 1))
61
- .digest(),
62
- start,
63
- );
64
-
65
- prev = start;
66
- start += hashlen;
67
- }
18
+ import { Buffer } from "@craftzdog/react-native-buffer";
19
+ import QuickCrypto from "react-native-quick-crypto";
68
20
 
69
- // OKM, releasing T
70
- return T.slice(0, keylen);
71
- };
21
+ // The default export from QuickCrypto should be compatible with the standard `crypto` object but the type system
22
+ // seems confused by CJS exports. Use a forced cast to correct types.
23
+ const crypto = QuickCrypto as unknown as typeof QuickCrypto.default;
24
+
25
+ // QuickCrypto's `install()` function is documented as optional but QuickCrypto references it as a global in its subtle
26
+ // implementation, so we can't avoid mucking with global scope (as of QuickCrypto 0.7.6)
27
+ if (!("Buffer" in globalThis)) {
28
+ (globalThis as unknown as { Buffer: typeof Buffer }).Buffer = Buffer;
29
+ }
72
30
 
73
31
  /**
74
- * Crypto implementation for React Native
32
+ * Crypto implementation for React Native.
75
33
  */
76
- export class ReactNativeCrypto extends NodeJsCrypto {
77
- override implementationName = "React Native";
78
-
79
- override signEcdsa(
80
- privateKey: JsonWebKey,
81
- data: Uint8Array | Uint8Array[],
82
- dsaEncoding: CryptoDsaEncoding = "ieee-p1363",
83
- ): Uint8Array {
84
- // @ts-expect-error No types but all fine
85
- const signer = crypto.createSign(CRYPTO_HASH_ALGORITHM);
86
- if (Array.isArray(data)) {
87
- data.forEach(chunk => signer.update(chunk));
88
- } else {
89
- signer.update(data);
90
- }
91
- return new Uint8Array(
92
- signer.sign({
93
- key: jwk2pem(privateKey as jwk2pem.JWK, { private: true }),
94
- format: "pem",
95
- type: "pkcs8",
96
- dsaEncoding,
97
- }),
98
- );
34
+ export class ReactNativeCrypto extends StandardCrypto {
35
+ override implementationName = "ReactNativeCrypto";
36
+
37
+ static override provider() {
38
+ return new ReactNativeCrypto();
99
39
  }
100
40
 
101
- override verifyEcdsa(
102
- publicKey: JsonWebKey,
103
- data: Uint8Array,
104
- signature: Uint8Array,
105
- dsaEncoding: CryptoDsaEncoding = "ieee-p1363",
41
+ /**
42
+ * Quick Crypto doesn't currently support {@link SubtleCrypto#deriveBits}.
43
+ */
44
+ override async createHkdfKey(
45
+ secret: Uint8Array,
46
+ salt: Uint8Array,
47
+ info: Uint8Array,
48
+ length: number = CRYPTO_SYMMETRIC_KEY_LENGTH,
106
49
  ) {
107
- // @ts-expect-error No types but all fine
108
- const verifier = crypto.createVerify(CRYPTO_HASH_ALGORITHM);
109
- verifier.update(data);
110
- const success = verifier.verify(
111
- {
112
- key: jwk2pem(publicKey as jwk2pem.JWK),
113
- format: "pem",
114
- type: "spki",
115
- dsaEncoding,
116
- },
117
- signature,
118
- );
119
- if (!success) throw new CryptoVerifyError("Signature verification failed");
50
+ const prk = crypto
51
+ .createHmac("SHA-256", salt.byteLength ? salt : new Uint8Array(CRYPTO_HASH_LEN_BYTES))
52
+ .update(secret)
53
+ .digest();
54
+
55
+ // T(0) = empty
56
+ // T(1) = HMAC(PRK, T(0) | info | 0x01)
57
+ // T(2) = HMAC(PRK, T(1) | info | 0x02)
58
+ // T(3) = HMAC(PRK, T(2) | info | 0x03)
59
+ // ...
60
+ // T(N) = HMAC(PRK, T(N-1) | info | N)
61
+
62
+ const N = Math.ceil(length / CRYPTO_HASH_LEN_BYTES);
63
+
64
+ // Single T buffer to accomodate T = T(1) | T(2) | T(3) | ... | T(N)
65
+ // with a little extra for info | N during T(N)
66
+ const T = new Uint8Array(CRYPTO_HASH_LEN_BYTES * N + info.byteLength + 1);
67
+ let prev = 0;
68
+ let start = 0;
69
+ for (let c = 1; c <= N; c++) {
70
+ T.set(info, start);
71
+ T[start + info.byteLength] = c;
72
+
73
+ T.set(
74
+ crypto
75
+ .createHmac("SHA-256", prk)
76
+ .update(T.subarray(prev, start + info.byteLength + 1))
77
+ .digest(),
78
+ start,
79
+ );
80
+
81
+ prev = start;
82
+ start += CRYPTO_HASH_LEN_BYTES;
83
+ }
84
+
85
+ // OKM, releasing T
86
+ return T.slice(0, length);
120
87
  }
121
88
 
122
- // Quick crypto does not support AES-CCM. Install the JS version to compensate
123
- // TODO - remove this once we have proper feature detection to configure crypto
124
- override encrypt = StandardCrypto.prototype.encrypt;
125
- override decrypt = StandardCrypto.prototype.decrypt;
89
+ /**
90
+ * Quick Crypto apparently appends a "." to the base64 encoded properties. I'm not aware of a legitimate reason for
91
+ * this, seems likely just a bug. Regardless it trips us off so strip off.
92
+ */
93
+ override async generateJwk() {
94
+ const key = await super.generateJwk();
95
+
96
+ for (const prop of ["d", "x", "y"] as const) {
97
+ if (key[prop]?.endsWith(".")) {
98
+ key[prop] = key[prop].slice(0, key[prop].length - 1);
99
+ }
100
+ }
101
+
102
+ return key;
103
+ }
104
+
105
+ /**
106
+ * See comment on {@link createHkdfKey}.
107
+ */
108
+ override async generateDhSecret(key: PrivateKey, peerKey: PublicKey) {
109
+ return key.sharedSecretFor(peerKey);
110
+ }
111
+
112
+ /**
113
+ * Quick Crypto doesn't support import of raw keys so convert to JWK prior to import.
114
+ */
115
+ protected override importKey(
116
+ format: KeyFormat,
117
+ keyData: JsonWebKey | BufferSource,
118
+ algorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm,
119
+ extractable: boolean,
120
+ keyUsages: ReadonlyArray<KeyUsage>,
121
+ ) {
122
+ if (format === "raw") {
123
+ format = "jwk";
124
+ keyData = PrivateKey(Bytes.of(keyData as BufferSource));
125
+ }
126
+ return super.importKey(format, keyData, algorithm, extractable, keyUsages);
127
+ }
126
128
  }
127
129
 
128
- Environment.default.set(Crypto, new ReactNativeCrypto());
130
+ Environment.default.set(Crypto, new ReactNativeCrypto(crypto as unknown as WebCrypto));
@@ -8,21 +8,14 @@ import dgram from "react-native-udp";
8
8
  // @ts-expect-error globalThis is no index structure
9
9
  global.dgram = dgram;
10
10
 
11
- // @ts-expect-error globalThis is no index structure
11
+ // @ts-expect-error globalThis is no index structure, no bind needed because static method
12
12
  const rnDGramCreateSocket = dgram.createSocket;
13
13
  // @ts-expect-error globalThis is no index structure
14
14
  // Work around because React-Native UDP lib is not providing the setMulticastInterface method
15
15
  dgram.createSocket = (...args: any[]) => {
16
16
  const socket = rnDGramCreateSocket(...args);
17
- socket.setMulticastInterface = () => {}; // Stub for now
18
-
19
- const originalSend = socket.send;
20
- socket.send = (
21
- buffer: Uint8Array,
22
- port: number,
23
- address: string,
24
- callback: (error: Error | null, bytes: number) => void,
25
- ) => originalSend(buffer, 0, buffer.length, port, address, callback);
17
+ socket.setMulticastInterface = () => {}; // Stub for now, suppress warning logs
18
+
26
19
  return socket;
27
20
  };
28
21
 
@@ -19,6 +19,7 @@ import {
19
19
  UdpChannelOptions,
20
20
  UdpSocketType,
21
21
  } from "#general";
22
+ import { Platform } from "react-native";
22
23
  import { NetworkReactNative } from "./NetworkReactNative.js";
23
24
 
24
25
  const logger = Logger.get("UdpChannelNode");
@@ -52,14 +53,21 @@ interface SocketOptions {
52
53
 
53
54
  interface Socket {
54
55
  setBroadcast(flag: boolean): void;
55
- setMulticastInterface(interfaceAddress: string): void;
56
+ setMulticastInterface(interfaceAddress: string): void; // Not implemented
56
57
  addMembership(multicastAddress: string, multicastInterface?: string): void;
57
58
  dropMembership(multicastAddress: string, multicastInterface?: string): void;
58
59
  on(event: "message", listener: (msg: Uint8Array, rinfo: RemoteInfo) => void): void;
59
60
  on(event: "error", listener: (error: Error) => void): void;
60
61
  removeListener(event: "message", listener: (msg: Uint8Array, rinfo: RemoteInfo) => void): void;
61
62
  removeListener(event: "error", listener: (error: Error) => void): void;
62
- send(msg: Uint8Array, port: number, address: string, callback: (error: Error | null) => void): void;
63
+ send(
64
+ msg: Uint8Array,
65
+ offset: number,
66
+ length: number,
67
+ port: number,
68
+ address: string,
69
+ callback: (error: Error | null) => void,
70
+ ): void;
63
71
  close(): void;
64
72
  address(): { address: string; port: number };
65
73
  }
@@ -108,7 +116,10 @@ export class UdpChannelReactNative implements UdpChannel {
108
116
  socketOptions.ipv6Only = true;
109
117
  }
110
118
  const socket = await createDgramSocket(listeningAddress, listeningPort, socketOptions);
111
- socket.setBroadcast(true);
119
+ if (Platform.OS !== "android") {
120
+ socket.setBroadcast(true);
121
+ }
122
+
112
123
  let netInterfaceZone: string | undefined;
113
124
  if (netInterface !== undefined) {
114
125
  netInterfaceZone = netInterface;
@@ -198,8 +209,8 @@ export class UdpChannelReactNative implements UdpChannel {
198
209
 
199
210
  async send(host: string, port: number, data: Uint8Array) {
200
211
  return new Promise<void>((resolve, reject) => {
201
- this.#socket.send(data, port, host, error => {
202
- if (error !== null) {
212
+ this.#socket.send(data, 0, data.length, port, host, error => {
213
+ if (error) {
203
214
  reject(repackErrorAs(error, NetworkError) as Error);
204
215
  return;
205
216
  }