@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.
- package/dist/cjs/ble/BleReactNative.d.ts +5 -6
- package/dist/cjs/ble/BleReactNative.d.ts.map +1 -1
- package/dist/cjs/ble/BleReactNative.js +3 -6
- package/dist/cjs/ble/BleReactNative.js.map +1 -1
- package/dist/cjs/ble/ReactNativeBleChannel.d.ts.map +1 -1
- package/dist/cjs/ble/ReactNativeBleChannel.js +6 -3
- package/dist/cjs/ble/ReactNativeBleChannel.js.map +1 -1
- package/dist/cjs/ble/ReactNativeBleClient.d.ts.map +1 -1
- package/dist/cjs/ble/ReactNativeBleClient.js +27 -17
- package/dist/cjs/ble/ReactNativeBleClient.js.map +1 -1
- package/dist/cjs/crypto/ReactNativeCrypto.d.ts +21 -8
- package/dist/cjs/crypto/ReactNativeCrypto.d.ts.map +1 -1
- package/dist/cjs/crypto/ReactNativeCrypto.js +58 -56
- package/dist/cjs/crypto/ReactNativeCrypto.js.map +2 -2
- package/dist/cjs/net/NetworkReactNative.d.ts.map +1 -1
- package/dist/cjs/net/NetworkReactNative.js +0 -2
- package/dist/cjs/net/NetworkReactNative.js.map +1 -1
- package/dist/cjs/net/UdpChannelReactNative.d.ts +1 -1
- package/dist/cjs/net/UdpChannelReactNative.d.ts.map +1 -1
- package/dist/cjs/net/UdpChannelReactNative.js +6 -3
- package/dist/cjs/net/UdpChannelReactNative.js.map +1 -1
- package/dist/esm/ble/BleReactNative.d.ts +5 -6
- package/dist/esm/ble/BleReactNative.d.ts.map +1 -1
- package/dist/esm/ble/BleReactNative.js +3 -6
- package/dist/esm/ble/BleReactNative.js.map +1 -1
- package/dist/esm/ble/ReactNativeBleChannel.d.ts.map +1 -1
- package/dist/esm/ble/ReactNativeBleChannel.js +6 -3
- package/dist/esm/ble/ReactNativeBleChannel.js.map +1 -1
- package/dist/esm/ble/ReactNativeBleClient.d.ts.map +1 -1
- package/dist/esm/ble/ReactNativeBleClient.js +27 -17
- package/dist/esm/ble/ReactNativeBleClient.js.map +1 -1
- package/dist/esm/crypto/ReactNativeCrypto.d.ts +21 -8
- package/dist/esm/crypto/ReactNativeCrypto.d.ts.map +1 -1
- package/dist/esm/crypto/ReactNativeCrypto.js +62 -58
- package/dist/esm/crypto/ReactNativeCrypto.js.map +1 -1
- package/dist/esm/net/NetworkReactNative.d.ts.map +1 -1
- package/dist/esm/net/NetworkReactNative.js +0 -2
- package/dist/esm/net/NetworkReactNative.js.map +1 -1
- package/dist/esm/net/UdpChannelReactNative.d.ts +1 -1
- package/dist/esm/net/UdpChannelReactNative.d.ts.map +1 -1
- package/dist/esm/net/UdpChannelReactNative.js +6 -3
- package/dist/esm/net/UdpChannelReactNative.js.map +1 -1
- package/package.json +6 -7
- package/src/ble/BleReactNative.ts +5 -9
- package/src/ble/ReactNativeBleChannel.ts +6 -5
- package/src/ble/ReactNativeBleClient.ts +27 -19
- package/src/crypto/ReactNativeCrypto.ts +108 -106
- package/src/net/NetworkReactNative.ts +3 -10
- 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.
|
|
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(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
this.
|
|
83
|
-
|
|
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
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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 {
|
|
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
|
-
|
|
70
|
-
|
|
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
|
|
77
|
-
override implementationName = "
|
|
78
|
-
|
|
79
|
-
override
|
|
80
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
}
|