react-native-ble-mesh 2.0.0 → 2.1.2
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/README.md +2 -2
- package/docs/OPTIMIZATION.md +165 -52
- package/package.json +1 -1
- package/src/MeshNetwork.js +63 -53
- package/src/constants/audio.js +4 -4
- package/src/constants/ble.js +1 -1
- package/src/constants/crypto.js +1 -1
- package/src/constants/errors.js +2 -2
- package/src/constants/events.js +1 -1
- package/src/constants/protocol.js +2 -2
- package/src/crypto/AutoCrypto.js +16 -3
- package/src/crypto/CryptoProvider.js +17 -17
- package/src/crypto/providers/ExpoCryptoProvider.js +15 -9
- package/src/crypto/providers/QuickCryptoProvider.js +41 -12
- package/src/crypto/providers/TweetNaClProvider.js +10 -8
- package/src/errors/AudioError.js +2 -1
- package/src/errors/ConnectionError.js +2 -2
- package/src/errors/CryptoError.js +1 -1
- package/src/errors/HandshakeError.js +2 -2
- package/src/errors/MeshError.js +4 -4
- package/src/errors/MessageError.js +2 -2
- package/src/errors/ValidationError.js +3 -3
- package/src/expo/withBLEMesh.js +10 -10
- package/src/hooks/AppStateManager.js +11 -2
- package/src/hooks/useMesh.js +23 -10
- package/src/hooks/useMessages.js +17 -16
- package/src/hooks/usePeers.js +19 -14
- package/src/index.js +2 -2
- package/src/mesh/dedup/BloomFilter.js +45 -57
- package/src/mesh/dedup/DedupManager.js +36 -8
- package/src/mesh/dedup/MessageCache.js +3 -0
- package/src/mesh/fragment/Assembler.js +5 -4
- package/src/mesh/fragment/Fragmenter.js +3 -3
- package/src/mesh/monitor/ConnectionQuality.js +59 -25
- package/src/mesh/monitor/NetworkMonitor.js +80 -28
- package/src/mesh/peer/Peer.js +9 -11
- package/src/mesh/peer/PeerDiscovery.js +18 -19
- package/src/mesh/peer/PeerManager.js +29 -17
- package/src/mesh/router/MessageRouter.js +28 -20
- package/src/mesh/router/PathFinder.js +10 -13
- package/src/mesh/router/RouteTable.js +25 -14
- package/src/mesh/store/StoreAndForwardManager.js +32 -24
- package/src/protocol/deserializer.js +9 -10
- package/src/protocol/header.js +13 -7
- package/src/protocol/message.js +18 -14
- package/src/protocol/serializer.js +9 -12
- package/src/protocol/validator.js +29 -10
- package/src/service/BatteryOptimizer.js +22 -18
- package/src/service/EmergencyManager.js +18 -25
- package/src/service/HandshakeManager.js +112 -18
- package/src/service/MeshService.js +106 -22
- package/src/service/SessionManager.js +50 -13
- package/src/service/audio/AudioManager.js +80 -38
- package/src/service/audio/buffer/FrameBuffer.js +7 -8
- package/src/service/audio/buffer/JitterBuffer.js +1 -1
- package/src/service/audio/codec/LC3Codec.js +18 -19
- package/src/service/audio/codec/LC3Decoder.js +10 -10
- package/src/service/audio/codec/LC3Encoder.js +11 -9
- package/src/service/audio/session/AudioSession.js +14 -17
- package/src/service/audio/session/VoiceMessage.js +15 -22
- package/src/service/audio/transport/AudioFragmenter.js +17 -9
- package/src/service/audio/transport/AudioFramer.js +8 -12
- package/src/service/file/FileAssembler.js +4 -2
- package/src/service/file/FileChunker.js +1 -1
- package/src/service/file/FileManager.js +26 -20
- package/src/service/file/FileMessage.js +7 -12
- package/src/service/text/TextManager.js +75 -42
- package/src/service/text/broadcast/BroadcastManager.js +14 -17
- package/src/service/text/channel/Channel.js +10 -14
- package/src/service/text/channel/ChannelManager.js +10 -10
- package/src/service/text/message/TextMessage.js +12 -19
- package/src/service/text/message/TextSerializer.js +2 -2
- package/src/storage/AsyncStorageAdapter.js +17 -14
- package/src/storage/MemoryStorage.js +11 -8
- package/src/storage/MessageStore.js +77 -32
- package/src/storage/Storage.js +9 -9
- package/src/transport/BLETransport.js +27 -16
- package/src/transport/MockTransport.js +7 -2
- package/src/transport/MultiTransport.js +43 -11
- package/src/transport/Transport.js +9 -9
- package/src/transport/WiFiDirectTransport.js +26 -20
- package/src/transport/adapters/BLEAdapter.js +19 -19
- package/src/transport/adapters/NodeBLEAdapter.js +24 -23
- package/src/transport/adapters/RNBLEAdapter.js +14 -11
- package/src/utils/EventEmitter.js +15 -16
- package/src/utils/LRUCache.js +10 -4
- package/src/utils/RateLimiter.js +1 -1
- package/src/utils/bytes.js +12 -10
- package/src/utils/compression.js +10 -8
- package/src/utils/encoding.js +39 -8
- package/src/utils/retry.js +11 -13
- package/src/utils/time.js +9 -4
- package/src/utils/validation.js +1 -1
package/src/constants/audio.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* LC3 codec configuration
|
|
10
|
-
* @constant {
|
|
10
|
+
* @constant {any}
|
|
11
11
|
*/
|
|
12
12
|
const LC3_CONFIG = Object.freeze({
|
|
13
13
|
/** Supported sample rates (Hz) */
|
|
@@ -62,7 +62,7 @@ const AUDIO_QUALITY = Object.freeze({
|
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* Voice message configuration
|
|
65
|
-
* @constant {
|
|
65
|
+
* @constant {any}
|
|
66
66
|
*/
|
|
67
67
|
const VOICE_MESSAGE_CONFIG = Object.freeze({
|
|
68
68
|
/** Maximum voice message duration in seconds */
|
|
@@ -79,7 +79,7 @@ const VOICE_MESSAGE_CONFIG = Object.freeze({
|
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* Audio streaming configuration
|
|
82
|
-
* @constant {
|
|
82
|
+
* @constant {any}
|
|
83
83
|
*/
|
|
84
84
|
const AUDIO_STREAM_CONFIG = Object.freeze({
|
|
85
85
|
/** Default jitter buffer size in frames */
|
|
@@ -98,7 +98,7 @@ const AUDIO_STREAM_CONFIG = Object.freeze({
|
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
100
|
* Audio session states
|
|
101
|
-
* @
|
|
101
|
+
* @type {Record<string, string>}
|
|
102
102
|
*/
|
|
103
103
|
const AUDIO_SESSION_STATE = Object.freeze({
|
|
104
104
|
IDLE: 'idle',
|
package/src/constants/ble.js
CHANGED
package/src/constants/crypto.js
CHANGED
package/src/constants/errors.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Error codes organized by category
|
|
10
|
-
* @
|
|
10
|
+
* @type {Record<string, string>}
|
|
11
11
|
*/
|
|
12
12
|
const ERROR_CODE = Object.freeze({
|
|
13
13
|
// Initialization errors (E0xx)
|
|
@@ -108,7 +108,7 @@ const ERROR_CODE = Object.freeze({
|
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
110
|
* Error messages for each error code
|
|
111
|
-
* @
|
|
111
|
+
* @type {Record<string, string>}
|
|
112
112
|
*/
|
|
113
113
|
const ERROR_MESSAGES = Object.freeze({
|
|
114
114
|
E001: 'Initialization failed',
|
package/src/constants/events.js
CHANGED
|
@@ -115,7 +115,7 @@ const MESH_CONFIG = Object.freeze({
|
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
117
|
* Connection states
|
|
118
|
-
* @
|
|
118
|
+
* @type {Record<string, string>}
|
|
119
119
|
*/
|
|
120
120
|
const CONNECTION_STATE = Object.freeze({
|
|
121
121
|
DISCONNECTED: 'disconnected',
|
|
@@ -129,7 +129,7 @@ const CONNECTION_STATE = Object.freeze({
|
|
|
129
129
|
|
|
130
130
|
/**
|
|
131
131
|
* Service states
|
|
132
|
-
* @
|
|
132
|
+
* @type {Record<string, string>}
|
|
133
133
|
*/
|
|
134
134
|
const SERVICE_STATE = Object.freeze({
|
|
135
135
|
UNINITIALIZED: 'uninitialized',
|
package/src/crypto/AutoCrypto.js
CHANGED
|
@@ -14,25 +14,36 @@ const QuickCryptoProvider = require('./providers/QuickCryptoProvider');
|
|
|
14
14
|
const ExpoCryptoProvider = require('./providers/ExpoCryptoProvider');
|
|
15
15
|
const TweetNaClProvider = require('./providers/TweetNaClProvider');
|
|
16
16
|
|
|
17
|
+
/** @type {import('./CryptoProvider')|null} Cached singleton provider */
|
|
18
|
+
let _cachedProvider = null;
|
|
19
|
+
|
|
17
20
|
/**
|
|
18
21
|
* Detects and returns the best available crypto provider.
|
|
22
|
+
* The result is cached as a singleton for subsequent calls.
|
|
19
23
|
* @returns {import('./CryptoProvider')} Best available provider
|
|
20
24
|
* @throws {Error} If no crypto provider is available
|
|
21
25
|
*/
|
|
22
26
|
function detectProvider() {
|
|
27
|
+
if (_cachedProvider) {
|
|
28
|
+
return _cachedProvider;
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
// 1. Native speed (react-native-quick-crypto)
|
|
24
32
|
if (QuickCryptoProvider.isAvailable()) {
|
|
25
|
-
|
|
33
|
+
_cachedProvider = new QuickCryptoProvider();
|
|
34
|
+
return _cachedProvider;
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
// 2. Expo (expo-crypto + tweetnacl)
|
|
29
38
|
if (ExpoCryptoProvider.isAvailable()) {
|
|
30
|
-
|
|
39
|
+
_cachedProvider = new ExpoCryptoProvider();
|
|
40
|
+
return _cachedProvider;
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
// 3. Universal (tweetnacl)
|
|
34
44
|
if (TweetNaClProvider.isAvailable()) {
|
|
35
|
-
|
|
45
|
+
_cachedProvider = new TweetNaClProvider();
|
|
46
|
+
return _cachedProvider;
|
|
36
47
|
}
|
|
37
48
|
|
|
38
49
|
throw new Error(
|
|
@@ -53,7 +64,9 @@ function createProvider(config) {
|
|
|
53
64
|
return detectProvider();
|
|
54
65
|
}
|
|
55
66
|
|
|
67
|
+
// @ts-ignore
|
|
56
68
|
if (typeof config === 'object' && typeof config.generateKeyPair === 'function') {
|
|
69
|
+
// @ts-ignore
|
|
57
70
|
return config; // Already a provider instance
|
|
58
71
|
}
|
|
59
72
|
|
|
@@ -37,53 +37,53 @@ class CryptoProvider {
|
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Computes X25519 shared secret
|
|
40
|
-
* @param {Uint8Array}
|
|
41
|
-
* @param {Uint8Array}
|
|
40
|
+
* @param {Uint8Array} _secretKey - Our secret key (32 bytes)
|
|
41
|
+
* @param {Uint8Array} _publicKey - Their public key (32 bytes)
|
|
42
42
|
* @returns {Uint8Array} Shared secret (32 bytes)
|
|
43
43
|
*/
|
|
44
|
-
sharedSecret(_secretKey, _publicKey) {
|
|
44
|
+
sharedSecret(/** @type {any} */ _secretKey, _publicKey) {
|
|
45
45
|
throw new Error('CryptoProvider.sharedSecret() must be implemented');
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* AEAD encrypt (XSalsa20-Poly1305 or ChaCha20-Poly1305)
|
|
50
|
-
* @param {Uint8Array}
|
|
51
|
-
* @param {Uint8Array}
|
|
52
|
-
* @param {Uint8Array}
|
|
53
|
-
* @param {Uint8Array} [
|
|
50
|
+
* @param {Uint8Array} _key - Encryption key (32 bytes)
|
|
51
|
+
* @param {Uint8Array} _nonce - Nonce (24 bytes for XSalsa20, 12 for ChaCha20)
|
|
52
|
+
* @param {Uint8Array} _plaintext - Data to encrypt
|
|
53
|
+
* @param {Uint8Array} [_ad] - Additional authenticated data (optional)
|
|
54
54
|
* @returns {Uint8Array} Ciphertext with authentication tag
|
|
55
55
|
*/
|
|
56
|
-
encrypt(_key, _nonce, _plaintext, _ad) {
|
|
56
|
+
encrypt(/** @type {any} */ _key, _nonce, _plaintext, _ad) {
|
|
57
57
|
throw new Error('CryptoProvider.encrypt() must be implemented');
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* AEAD decrypt
|
|
62
|
-
* @param {Uint8Array}
|
|
63
|
-
* @param {Uint8Array}
|
|
64
|
-
* @param {Uint8Array}
|
|
65
|
-
* @param {Uint8Array} [
|
|
62
|
+
* @param {Uint8Array} _key - Encryption key (32 bytes)
|
|
63
|
+
* @param {Uint8Array} _nonce - Nonce
|
|
64
|
+
* @param {Uint8Array} _ciphertext - Ciphertext with auth tag
|
|
65
|
+
* @param {Uint8Array} [_ad] - Additional authenticated data (optional)
|
|
66
66
|
* @returns {Uint8Array|null} Plaintext or null if authentication fails
|
|
67
67
|
*/
|
|
68
|
-
decrypt(_key, _nonce, _ciphertext, _ad) {
|
|
68
|
+
decrypt(/** @type {any} */ _key, _nonce, _ciphertext, _ad) {
|
|
69
69
|
throw new Error('CryptoProvider.decrypt() must be implemented');
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
73
|
* Computes SHA-256 hash
|
|
74
|
-
* @param {Uint8Array}
|
|
74
|
+
* @param {Uint8Array} _data - Data to hash
|
|
75
75
|
* @returns {Uint8Array} Hash (32 bytes)
|
|
76
76
|
*/
|
|
77
|
-
hash(_data) {
|
|
77
|
+
hash(/** @type {any} */ _data) {
|
|
78
78
|
throw new Error('CryptoProvider.hash() must be implemented');
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
82
|
* Generates cryptographically secure random bytes
|
|
83
|
-
* @param {number}
|
|
83
|
+
* @param {number} _length - Number of bytes
|
|
84
84
|
* @returns {Uint8Array} Random bytes
|
|
85
85
|
*/
|
|
86
|
-
randomBytes(_length) {
|
|
86
|
+
randomBytes(/** @type {any} */ _length) {
|
|
87
87
|
throw new Error('CryptoProvider.randomBytes() must be implemented');
|
|
88
88
|
}
|
|
89
89
|
|
|
@@ -23,8 +23,10 @@ const CryptoProvider = require('../CryptoProvider');
|
|
|
23
23
|
class ExpoCryptoProvider extends CryptoProvider {
|
|
24
24
|
constructor(options = {}) {
|
|
25
25
|
super();
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
/** @type {any} */
|
|
27
|
+
const opts = options;
|
|
28
|
+
this._expoCrypto = opts.expoCrypto || null;
|
|
29
|
+
this._nacl = opts.nacl || null;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
get name() {
|
|
@@ -34,6 +36,7 @@ class ExpoCryptoProvider extends CryptoProvider {
|
|
|
34
36
|
_getExpoCrypto() {
|
|
35
37
|
if (!this._expoCrypto) {
|
|
36
38
|
try {
|
|
39
|
+
// @ts-ignore
|
|
37
40
|
this._expoCrypto = require('expo-crypto');
|
|
38
41
|
} catch (e) {
|
|
39
42
|
throw new Error('expo-crypto is required. Install: npx expo install expo-crypto');
|
|
@@ -45,6 +48,7 @@ class ExpoCryptoProvider extends CryptoProvider {
|
|
|
45
48
|
_getNacl() {
|
|
46
49
|
if (!this._nacl) {
|
|
47
50
|
try {
|
|
51
|
+
// @ts-ignore
|
|
48
52
|
this._nacl = require('tweetnacl');
|
|
49
53
|
} catch (e) {
|
|
50
54
|
throw new Error('tweetnacl is required with ExpoCryptoProvider. Install: npm install tweetnacl');
|
|
@@ -61,13 +65,13 @@ class ExpoCryptoProvider extends CryptoProvider {
|
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
/** @inheritdoc */
|
|
64
|
-
sharedSecret(secretKey, publicKey) {
|
|
68
|
+
sharedSecret(/** @type {any} */ secretKey, /** @type {any} */ publicKey) {
|
|
65
69
|
const nacl = this._getNacl();
|
|
66
70
|
return nacl.box.before(publicKey, secretKey);
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
/** @inheritdoc */
|
|
70
|
-
encrypt(key, nonce, plaintext, _ad) {
|
|
74
|
+
encrypt(/** @type {any} */ key, /** @type {any} */ nonce, /** @type {any} */ plaintext, /** @type {any} */ _ad) {
|
|
71
75
|
const nacl = this._getNacl();
|
|
72
76
|
|
|
73
77
|
// Ensure 24-byte nonce (pad short nonces with zeros)
|
|
@@ -81,7 +85,7 @@ class ExpoCryptoProvider extends CryptoProvider {
|
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
/** @inheritdoc */
|
|
84
|
-
decrypt(key, nonce, ciphertext, _ad) {
|
|
88
|
+
decrypt(/** @type {any} */ key, /** @type {any} */ nonce, /** @type {any} */ ciphertext, /** @type {any} */ _ad) {
|
|
85
89
|
const nacl = this._getNacl();
|
|
86
90
|
|
|
87
91
|
// Ensure 24-byte nonce (pad short nonces with zeros)
|
|
@@ -95,17 +99,18 @@ class ExpoCryptoProvider extends CryptoProvider {
|
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
/** @inheritdoc */
|
|
98
|
-
hash(data) {
|
|
102
|
+
hash(/** @type {any} */ data) {
|
|
99
103
|
// expo-crypto's digestStringAsync is async — for sync compat, use tweetnacl
|
|
100
104
|
const nacl = this._getNacl();
|
|
101
|
-
return nacl.hash(data).
|
|
105
|
+
return nacl.hash(data).subarray(0, 32);
|
|
102
106
|
}
|
|
103
107
|
|
|
104
108
|
/** @inheritdoc */
|
|
105
|
-
randomBytes(length) {
|
|
109
|
+
randomBytes(/** @type {any} */ length) {
|
|
106
110
|
const expoCrypto = this._getExpoCrypto();
|
|
107
111
|
if (expoCrypto.getRandomBytes) {
|
|
108
|
-
|
|
112
|
+
const bytes = expoCrypto.getRandomBytes(length);
|
|
113
|
+
return bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
|
109
114
|
}
|
|
110
115
|
// Fallback to tweetnacl
|
|
111
116
|
const nacl = this._getNacl();
|
|
@@ -114,6 +119,7 @@ class ExpoCryptoProvider extends CryptoProvider {
|
|
|
114
119
|
|
|
115
120
|
static isAvailable() {
|
|
116
121
|
try {
|
|
122
|
+
// @ts-ignore
|
|
117
123
|
require('expo-crypto');
|
|
118
124
|
return true;
|
|
119
125
|
} catch (e) {
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
const CryptoProvider = require('../CryptoProvider');
|
|
12
12
|
|
|
13
|
+
/** DER header for PKCS8 private key wrapping (X25519) */
|
|
14
|
+
const PKCS8_HEADER = Buffer.from('302e020100300506032b656e04220420', 'hex');
|
|
15
|
+
/** DER header for SPKI public key wrapping (X25519) */
|
|
16
|
+
const SPKI_HEADER = Buffer.from('302a300506032b656e032100', 'hex');
|
|
17
|
+
|
|
13
18
|
/**
|
|
14
19
|
* Crypto provider using react-native-quick-crypto.
|
|
15
20
|
* Provides native-speed crypto on React Native (JSI binding).
|
|
@@ -20,7 +25,10 @@ const CryptoProvider = require('../CryptoProvider');
|
|
|
20
25
|
class QuickCryptoProvider extends CryptoProvider {
|
|
21
26
|
constructor(options = {}) {
|
|
22
27
|
super();
|
|
23
|
-
|
|
28
|
+
/** @type {any} */
|
|
29
|
+
const opts = options;
|
|
30
|
+
this._crypto = opts.crypto || null;
|
|
31
|
+
this._nacl = null;
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
get name() {
|
|
@@ -30,6 +38,7 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
30
38
|
_getCrypto() {
|
|
31
39
|
if (!this._crypto) {
|
|
32
40
|
try {
|
|
41
|
+
// @ts-ignore
|
|
33
42
|
this._crypto = require('react-native-quick-crypto');
|
|
34
43
|
} catch (e) {
|
|
35
44
|
throw new Error(
|
|
@@ -51,11 +60,11 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
/** @inheritdoc */
|
|
54
|
-
sharedSecret(secretKey, publicKey) {
|
|
63
|
+
sharedSecret(/** @type {any} */ secretKey, /** @type {any} */ publicKey) {
|
|
55
64
|
const crypto = this._getCrypto();
|
|
56
65
|
const privKey = crypto.createPrivateKey({
|
|
57
66
|
key: Buffer.concat([
|
|
58
|
-
|
|
67
|
+
PKCS8_HEADER,
|
|
59
68
|
Buffer.from(secretKey)
|
|
60
69
|
]),
|
|
61
70
|
format: 'der',
|
|
@@ -63,7 +72,7 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
63
72
|
});
|
|
64
73
|
const pubKey = crypto.createPublicKey({
|
|
65
74
|
key: Buffer.concat([
|
|
66
|
-
|
|
75
|
+
SPKI_HEADER,
|
|
67
76
|
Buffer.from(publicKey)
|
|
68
77
|
]),
|
|
69
78
|
format: 'der',
|
|
@@ -73,11 +82,30 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
73
82
|
return new Uint8Array(shared);
|
|
74
83
|
}
|
|
75
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Lazily loads tweetnacl (cached)
|
|
87
|
+
* @returns {any} nacl module
|
|
88
|
+
* @private
|
|
89
|
+
*/
|
|
90
|
+
_getNacl() {
|
|
91
|
+
if (!this._nacl) {
|
|
92
|
+
try {
|
|
93
|
+
// @ts-ignore
|
|
94
|
+
this._nacl = require('tweetnacl');
|
|
95
|
+
} catch (e) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
'tweetnacl is required for QuickCryptoProvider encrypt/decrypt/hash. Install: npm install tweetnacl'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return this._nacl;
|
|
102
|
+
}
|
|
103
|
+
|
|
76
104
|
/** @inheritdoc */
|
|
77
|
-
encrypt(key, nonce, plaintext, _ad) {
|
|
105
|
+
encrypt(/** @type {any} */ key, /** @type {any} */ nonce, /** @type {any} */ plaintext, /** @type {any} */ _ad) {
|
|
78
106
|
// Use tweetnacl for encryption to ensure cross-provider compatibility
|
|
79
107
|
// QuickCrypto's advantage is in fast native key generation (X25519), not AEAD
|
|
80
|
-
const nacl =
|
|
108
|
+
const nacl = this._getNacl();
|
|
81
109
|
|
|
82
110
|
// Ensure 24-byte nonce for XSalsa20-Poly1305
|
|
83
111
|
let fullNonce = nonce;
|
|
@@ -90,8 +118,8 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
90
118
|
}
|
|
91
119
|
|
|
92
120
|
/** @inheritdoc */
|
|
93
|
-
decrypt(key, nonce, ciphertext, _ad) {
|
|
94
|
-
const nacl =
|
|
121
|
+
decrypt(/** @type {any} */ key, /** @type {any} */ nonce, /** @type {any} */ ciphertext, /** @type {any} */ _ad) {
|
|
122
|
+
const nacl = this._getNacl();
|
|
95
123
|
|
|
96
124
|
// Ensure 24-byte nonce for XSalsa20-Poly1305
|
|
97
125
|
let fullNonce = nonce;
|
|
@@ -108,21 +136,22 @@ class QuickCryptoProvider extends CryptoProvider {
|
|
|
108
136
|
}
|
|
109
137
|
|
|
110
138
|
/** @inheritdoc */
|
|
111
|
-
hash(data) {
|
|
139
|
+
hash(/** @type {any} */ data) {
|
|
112
140
|
// Use SHA-512 truncated to 32 bytes for cross-provider compatibility
|
|
113
|
-
const nacl =
|
|
141
|
+
const nacl = this._getNacl();
|
|
114
142
|
const full = nacl.hash(data); // SHA-512
|
|
115
|
-
return full.
|
|
143
|
+
return full.subarray(0, 32);
|
|
116
144
|
}
|
|
117
145
|
|
|
118
146
|
/** @inheritdoc */
|
|
119
|
-
randomBytes(length) {
|
|
147
|
+
randomBytes(/** @type {any} */ length) {
|
|
120
148
|
const crypto = this._getCrypto();
|
|
121
149
|
return new Uint8Array(crypto.randomBytes(length));
|
|
122
150
|
}
|
|
123
151
|
|
|
124
152
|
static isAvailable() {
|
|
125
153
|
try {
|
|
154
|
+
// @ts-ignore
|
|
126
155
|
require('react-native-quick-crypto');
|
|
127
156
|
return true;
|
|
128
157
|
} catch (e) {
|
|
@@ -20,7 +20,7 @@ const CryptoProvider = require('../CryptoProvider');
|
|
|
20
20
|
class TweetNaClProvider extends CryptoProvider {
|
|
21
21
|
/**
|
|
22
22
|
* @param {Object} [options={}]
|
|
23
|
-
* @param {
|
|
23
|
+
* @param {any} [options.nacl] - Injected tweetnacl instance (for testing)
|
|
24
24
|
*/
|
|
25
25
|
constructor(options = {}) {
|
|
26
26
|
super();
|
|
@@ -33,12 +33,13 @@ class TweetNaClProvider extends CryptoProvider {
|
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Lazily loads tweetnacl
|
|
36
|
-
* @returns {
|
|
36
|
+
* @returns {any} nacl module
|
|
37
37
|
* @private
|
|
38
38
|
*/
|
|
39
39
|
_getNacl() {
|
|
40
40
|
if (!this._nacl) {
|
|
41
41
|
try {
|
|
42
|
+
// @ts-ignore
|
|
42
43
|
this._nacl = require('tweetnacl');
|
|
43
44
|
} catch (e) {
|
|
44
45
|
throw new Error(
|
|
@@ -57,13 +58,13 @@ class TweetNaClProvider extends CryptoProvider {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
/** @inheritdoc */
|
|
60
|
-
sharedSecret(secretKey, publicKey) {
|
|
61
|
+
sharedSecret(/** @type {any} */ secretKey, /** @type {any} */ publicKey) {
|
|
61
62
|
const nacl = this._getNacl();
|
|
62
63
|
return nacl.box.before(publicKey, secretKey);
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
/** @inheritdoc */
|
|
66
|
-
encrypt(key, nonce, plaintext, _ad) {
|
|
67
|
+
encrypt(/** @type {any} */ key, /** @type {any} */ nonce, /** @type {any} */ plaintext, /** @type {any} */ _ad) {
|
|
67
68
|
const nacl = this._getNacl();
|
|
68
69
|
// tweetnacl uses XSalsa20-Poly1305 with 24-byte nonce
|
|
69
70
|
// nacl.secretbox includes authentication
|
|
@@ -79,7 +80,7 @@ class TweetNaClProvider extends CryptoProvider {
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
/** @inheritdoc */
|
|
82
|
-
decrypt(key, nonce, ciphertext, _ad) {
|
|
83
|
+
decrypt(/** @type {any} */ key, /** @type {any} */ nonce, /** @type {any} */ ciphertext, /** @type {any} */ _ad) {
|
|
83
84
|
const nacl = this._getNacl();
|
|
84
85
|
|
|
85
86
|
// Ensure 24-byte nonce (pad short nonces with zeros)
|
|
@@ -94,15 +95,15 @@ class TweetNaClProvider extends CryptoProvider {
|
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
/** @inheritdoc */
|
|
97
|
-
hash(data) {
|
|
98
|
+
hash(/** @type {any} */ data) {
|
|
98
99
|
const nacl = this._getNacl();
|
|
99
100
|
// tweetnacl provides SHA-512; we return first 32 bytes for SHA-256 compatibility
|
|
100
101
|
const full = nacl.hash(data);
|
|
101
|
-
return full.
|
|
102
|
+
return full.subarray(0, 32);
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
/** @inheritdoc */
|
|
105
|
-
randomBytes(length) {
|
|
106
|
+
randomBytes(/** @type {any} */ length) {
|
|
106
107
|
const nacl = this._getNacl();
|
|
107
108
|
return nacl.randomBytes(length);
|
|
108
109
|
}
|
|
@@ -113,6 +114,7 @@ class TweetNaClProvider extends CryptoProvider {
|
|
|
113
114
|
*/
|
|
114
115
|
static isAvailable() {
|
|
115
116
|
try {
|
|
117
|
+
// @ts-ignore
|
|
116
118
|
require('tweetnacl');
|
|
117
119
|
return true;
|
|
118
120
|
} catch (e) {
|
package/src/errors/AudioError.js
CHANGED
|
@@ -32,7 +32,7 @@ class AudioError extends MeshError {
|
|
|
32
32
|
* @returns {AudioError}
|
|
33
33
|
*/
|
|
34
34
|
static fromCode(code, details = null) {
|
|
35
|
-
const message = ERROR_MESSAGES[code] || 'Audio operation failed';
|
|
35
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || 'Audio operation failed';
|
|
36
36
|
return new AudioError(message, code, details);
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -73,6 +73,7 @@ class AudioError extends MeshError {
|
|
|
73
73
|
return new AudioError(
|
|
74
74
|
`Audio session failed with peer ${peerId}`,
|
|
75
75
|
'EA04',
|
|
76
|
+
// @ts-ignore
|
|
76
77
|
{ peerId, ...details }
|
|
77
78
|
);
|
|
78
79
|
}
|
|
@@ -40,7 +40,7 @@ class ConnectionError extends MeshError {
|
|
|
40
40
|
* @returns {ConnectionError} New ConnectionError instance
|
|
41
41
|
*/
|
|
42
42
|
static fromCode(code, peerId = null, details = null) {
|
|
43
|
-
const message = ERROR_MESSAGES[code] || ERROR_MESSAGES.E200;
|
|
43
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || /** @type {Record<string, string>} */ (ERROR_MESSAGES).E200;
|
|
44
44
|
return new ConnectionError(message, code, peerId, details);
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -96,7 +96,7 @@ class ConnectionError extends MeshError {
|
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
98
|
* Converts error to a JSON-serializable object
|
|
99
|
-
* @returns {
|
|
99
|
+
* @returns {any} JSON representation of the error
|
|
100
100
|
*/
|
|
101
101
|
toJSON() {
|
|
102
102
|
return {
|
|
@@ -32,7 +32,7 @@ class CryptoError extends MeshError {
|
|
|
32
32
|
* @returns {CryptoError} New CryptoError instance
|
|
33
33
|
*/
|
|
34
34
|
static fromCode(code, details = null) {
|
|
35
|
-
const message = ERROR_MESSAGES[code] || ERROR_MESSAGES.E400;
|
|
35
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || /** @type {Record<string, string>} */ (ERROR_MESSAGES).E400;
|
|
36
36
|
return new CryptoError(message, code, details);
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -48,7 +48,7 @@ class HandshakeError extends MeshError {
|
|
|
48
48
|
* @returns {HandshakeError} New HandshakeError instance
|
|
49
49
|
*/
|
|
50
50
|
static fromCode(code, peerId = null, step = null, details = null) {
|
|
51
|
-
const message = ERROR_MESSAGES[code] || ERROR_MESSAGES.E300;
|
|
51
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || /** @type {Record<string, string>} */ (ERROR_MESSAGES).E300;
|
|
52
52
|
return new HandshakeError(message, code, peerId, step, details);
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -108,7 +108,7 @@ class HandshakeError extends MeshError {
|
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
110
|
* Converts error to a JSON-serializable object
|
|
111
|
-
* @returns {
|
|
111
|
+
* @returns {any} JSON representation of the error
|
|
112
112
|
*/
|
|
113
113
|
toJSON() {
|
|
114
114
|
return {
|
package/src/errors/MeshError.js
CHANGED
|
@@ -37,7 +37,7 @@ class MeshError extends Error {
|
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Additional error details
|
|
40
|
-
* @type {
|
|
40
|
+
* @type {any}
|
|
41
41
|
*/
|
|
42
42
|
this.details = details;
|
|
43
43
|
|
|
@@ -60,7 +60,7 @@ class MeshError extends Error {
|
|
|
60
60
|
* @returns {MeshError} New MeshError instance
|
|
61
61
|
*/
|
|
62
62
|
static fromCode(code, details = null) {
|
|
63
|
-
const message = ERROR_MESSAGES[code] || ERROR_MESSAGES.E900;
|
|
63
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || /** @type {Record<string, string>} */ (ERROR_MESSAGES).E900;
|
|
64
64
|
return new MeshError(message, code, details);
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -69,12 +69,12 @@ class MeshError extends Error {
|
|
|
69
69
|
* @returns {string} Error type name
|
|
70
70
|
*/
|
|
71
71
|
getTypeName() {
|
|
72
|
-
return ERROR_CODE[this.code] || 'UNKNOWN_ERROR';
|
|
72
|
+
return /** @type {Record<string, string>} */ (ERROR_CODE)[this.code] || 'UNKNOWN_ERROR';
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* Converts error to a JSON-serializable object
|
|
77
|
-
* @returns {
|
|
77
|
+
* @returns {any} JSON representation of the error
|
|
78
78
|
*/
|
|
79
79
|
toJSON() {
|
|
80
80
|
return {
|
|
@@ -40,7 +40,7 @@ class MessageError extends MeshError {
|
|
|
40
40
|
* @returns {MessageError} New MessageError instance
|
|
41
41
|
*/
|
|
42
42
|
static fromCode(code, messageId = null, details = null) {
|
|
43
|
-
const message = ERROR_MESSAGES[code] || ERROR_MESSAGES.E500;
|
|
43
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || /** @type {Record<string, string>} */ (ERROR_MESSAGES).E500;
|
|
44
44
|
return new MessageError(message, code, messageId, details);
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -116,7 +116,7 @@ class MessageError extends MeshError {
|
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
118
|
* Converts error to a JSON-serializable object
|
|
119
|
-
* @returns {
|
|
119
|
+
* @returns {any} JSON representation of the error
|
|
120
120
|
*/
|
|
121
121
|
toJSON() {
|
|
122
122
|
return {
|
|
@@ -87,7 +87,7 @@ class ValidationError extends MeshError {
|
|
|
87
87
|
* @returns {ValidationError} New ValidationError instance
|
|
88
88
|
*/
|
|
89
89
|
static fromCode(code, field = null, value = undefined, details = null) {
|
|
90
|
-
const message = ERROR_MESSAGES[code] || ERROR_MESSAGES.E800;
|
|
90
|
+
const message = /** @type {Record<string, string>} */ (ERROR_MESSAGES)[code] || /** @type {Record<string, string>} */ (ERROR_MESSAGES).E800;
|
|
91
91
|
return new ValidationError(message, code, field, value, details);
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -127,7 +127,7 @@ class ValidationError extends MeshError {
|
|
|
127
127
|
* Creates an out of range error
|
|
128
128
|
* @param {string} field - Name of the field
|
|
129
129
|
* @param {*} value - The invalid value
|
|
130
|
-
* @param {
|
|
130
|
+
* @param {any} range - Expected range { min, max }
|
|
131
131
|
* @returns {ValidationError} New ValidationError instance
|
|
132
132
|
*/
|
|
133
133
|
static outOfRange(field, value, range) {
|
|
@@ -136,7 +136,7 @@ class ValidationError extends MeshError {
|
|
|
136
136
|
|
|
137
137
|
/**
|
|
138
138
|
* Converts error to a JSON-serializable object
|
|
139
|
-
* @returns {
|
|
139
|
+
* @returns {any} JSON representation of the error
|
|
140
140
|
*/
|
|
141
141
|
toJSON() {
|
|
142
142
|
return {
|