react-native-ble-mesh 1.1.1 → 2.0.0

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 (65) hide show
  1. package/README.md +288 -172
  2. package/docs/IOS-BACKGROUND-BLE.md +231 -0
  3. package/docs/OPTIMIZATION.md +70 -0
  4. package/docs/SPEC-v2.1.md +308 -0
  5. package/package.json +1 -1
  6. package/src/MeshNetwork.js +659 -465
  7. package/src/constants/index.js +1 -0
  8. package/src/crypto/AutoCrypto.js +79 -0
  9. package/src/crypto/CryptoProvider.js +99 -0
  10. package/src/crypto/index.js +15 -63
  11. package/src/crypto/providers/ExpoCryptoProvider.js +125 -0
  12. package/src/crypto/providers/QuickCryptoProvider.js +134 -0
  13. package/src/crypto/providers/TweetNaClProvider.js +124 -0
  14. package/src/crypto/providers/index.js +11 -0
  15. package/src/errors/MeshError.js +2 -1
  16. package/src/expo/withBLEMesh.js +102 -0
  17. package/src/hooks/useMesh.js +30 -9
  18. package/src/hooks/useMessages.js +2 -0
  19. package/src/index.js +23 -8
  20. package/src/mesh/dedup/DedupManager.js +36 -10
  21. package/src/mesh/fragment/Assembler.js +5 -0
  22. package/src/mesh/index.js +1 -1
  23. package/src/mesh/monitor/ConnectionQuality.js +408 -0
  24. package/src/mesh/monitor/NetworkMonitor.js +327 -316
  25. package/src/mesh/monitor/index.js +7 -3
  26. package/src/mesh/peer/PeerManager.js +6 -1
  27. package/src/mesh/router/MessageRouter.js +26 -15
  28. package/src/mesh/router/RouteTable.js +7 -1
  29. package/src/mesh/store/StoreAndForwardManager.js +295 -297
  30. package/src/mesh/store/index.js +1 -1
  31. package/src/service/BatteryOptimizer.js +282 -278
  32. package/src/service/EmergencyManager.js +224 -214
  33. package/src/service/HandshakeManager.js +167 -13
  34. package/src/service/MeshService.js +72 -6
  35. package/src/service/SessionManager.js +77 -2
  36. package/src/service/audio/AudioManager.js +8 -2
  37. package/src/service/file/FileAssembler.js +106 -0
  38. package/src/service/file/FileChunker.js +79 -0
  39. package/src/service/file/FileManager.js +307 -0
  40. package/src/service/file/FileMessage.js +122 -0
  41. package/src/service/file/index.js +15 -0
  42. package/src/service/text/broadcast/BroadcastManager.js +16 -0
  43. package/src/transport/BLETransport.js +131 -9
  44. package/src/transport/MockTransport.js +1 -1
  45. package/src/transport/MultiTransport.js +305 -0
  46. package/src/transport/WiFiDirectTransport.js +295 -0
  47. package/src/transport/adapters/NodeBLEAdapter.js +34 -0
  48. package/src/transport/adapters/RNBLEAdapter.js +56 -1
  49. package/src/transport/index.js +6 -0
  50. package/src/utils/compression.js +291 -291
  51. package/src/crypto/aead.js +0 -189
  52. package/src/crypto/chacha20.js +0 -181
  53. package/src/crypto/hkdf.js +0 -187
  54. package/src/crypto/hmac.js +0 -143
  55. package/src/crypto/keys/KeyManager.js +0 -271
  56. package/src/crypto/keys/KeyPair.js +0 -216
  57. package/src/crypto/keys/SecureStorage.js +0 -219
  58. package/src/crypto/keys/index.js +0 -32
  59. package/src/crypto/noise/handshake.js +0 -410
  60. package/src/crypto/noise/index.js +0 -27
  61. package/src/crypto/noise/session.js +0 -253
  62. package/src/crypto/noise/state.js +0 -268
  63. package/src/crypto/poly1305.js +0 -113
  64. package/src/crypto/sha256.js +0 -240
  65. package/src/crypto/x25519.js +0 -154
@@ -7,6 +7,7 @@
7
7
 
8
8
  const protocol = require('./protocol');
9
9
  const ble = require('./ble');
10
+ // Crypto constants kept for protocol compatibility
10
11
  const crypto = require('./crypto');
11
12
  const errors = require('./errors');
12
13
  const events = require('./events');
@@ -0,0 +1,79 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview Auto-detect best available crypto provider
5
+ * @module crypto/AutoCrypto
6
+ *
7
+ * Priority order:
8
+ * 1. react-native-quick-crypto (native speed)
9
+ * 2. expo-crypto + tweetnacl (Expo projects)
10
+ * 3. tweetnacl (universal fallback)
11
+ */
12
+
13
+ const QuickCryptoProvider = require('./providers/QuickCryptoProvider');
14
+ const ExpoCryptoProvider = require('./providers/ExpoCryptoProvider');
15
+ const TweetNaClProvider = require('./providers/TweetNaClProvider');
16
+
17
+ /**
18
+ * Detects and returns the best available crypto provider.
19
+ * @returns {import('./CryptoProvider')} Best available provider
20
+ * @throws {Error} If no crypto provider is available
21
+ */
22
+ function detectProvider() {
23
+ // 1. Native speed (react-native-quick-crypto)
24
+ if (QuickCryptoProvider.isAvailable()) {
25
+ return new QuickCryptoProvider();
26
+ }
27
+
28
+ // 2. Expo (expo-crypto + tweetnacl)
29
+ if (ExpoCryptoProvider.isAvailable()) {
30
+ return new ExpoCryptoProvider();
31
+ }
32
+
33
+ // 3. Universal (tweetnacl)
34
+ if (TweetNaClProvider.isAvailable()) {
35
+ return new TweetNaClProvider();
36
+ }
37
+
38
+ throw new Error(
39
+ 'No crypto provider available. Install one of:\n' +
40
+ ' npm install tweetnacl (works everywhere)\n' +
41
+ ' npm install react-native-quick-crypto (native speed)\n' +
42
+ ' npx expo install expo-crypto && npm install tweetnacl (Expo)'
43
+ );
44
+ }
45
+
46
+ /**
47
+ * Creates a crypto provider from a config value.
48
+ * @param {string|Object|null} config - 'auto', provider name, or provider instance
49
+ * @returns {import('./CryptoProvider')}
50
+ */
51
+ function createProvider(config) {
52
+ if (!config || config === 'auto') {
53
+ return detectProvider();
54
+ }
55
+
56
+ if (typeof config === 'object' && typeof config.generateKeyPair === 'function') {
57
+ return config; // Already a provider instance
58
+ }
59
+
60
+ if (typeof config === 'string') {
61
+ switch (config) {
62
+ case 'tweetnacl':
63
+ return new TweetNaClProvider();
64
+ case 'quick-crypto':
65
+ return new QuickCryptoProvider();
66
+ case 'expo-crypto':
67
+ return new ExpoCryptoProvider();
68
+ default:
69
+ throw new Error(`Unknown crypto provider: ${config}`);
70
+ }
71
+ }
72
+
73
+ throw new Error('Invalid crypto config: expected "auto", provider name, or provider instance');
74
+ }
75
+
76
+ module.exports = {
77
+ detectProvider,
78
+ createProvider
79
+ };
@@ -0,0 +1,99 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview Abstract CryptoProvider interface
5
+ * @module crypto/CryptoProvider
6
+ *
7
+ * Pluggable crypto backend. Consumers choose their provider:
8
+ * - TweetNaClProvider (tweetnacl) — works everywhere
9
+ * - QuickCryptoProvider (react-native-quick-crypto) — native speed
10
+ * - ExpoCryptoProvider (expo-crypto) — for Expo projects
11
+ */
12
+
13
+ /**
14
+ * Abstract crypto provider interface.
15
+ * All crypto operations go through this interface, allowing
16
+ * consumers to swap implementations without changing application code.
17
+ *
18
+ * @abstract
19
+ * @class CryptoProvider
20
+ */
21
+ class CryptoProvider {
22
+ /**
23
+ * Provider name for identification
24
+ * @returns {string}
25
+ */
26
+ get name() {
27
+ return 'abstract';
28
+ }
29
+
30
+ /**
31
+ * Generates a new X25519 key pair
32
+ * @returns {{ publicKey: Uint8Array, secretKey: Uint8Array }}
33
+ */
34
+ generateKeyPair() {
35
+ throw new Error('CryptoProvider.generateKeyPair() must be implemented');
36
+ }
37
+
38
+ /**
39
+ * Computes X25519 shared secret
40
+ * @param {Uint8Array} secretKey - Our secret key (32 bytes)
41
+ * @param {Uint8Array} publicKey - Their public key (32 bytes)
42
+ * @returns {Uint8Array} Shared secret (32 bytes)
43
+ */
44
+ sharedSecret(_secretKey, _publicKey) {
45
+ throw new Error('CryptoProvider.sharedSecret() must be implemented');
46
+ }
47
+
48
+ /**
49
+ * AEAD encrypt (XSalsa20-Poly1305 or ChaCha20-Poly1305)
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
+ * @returns {Uint8Array} Ciphertext with authentication tag
55
+ */
56
+ encrypt(_key, _nonce, _plaintext, _ad) {
57
+ throw new Error('CryptoProvider.encrypt() must be implemented');
58
+ }
59
+
60
+ /**
61
+ * AEAD decrypt
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
+ * @returns {Uint8Array|null} Plaintext or null if authentication fails
67
+ */
68
+ decrypt(_key, _nonce, _ciphertext, _ad) {
69
+ throw new Error('CryptoProvider.decrypt() must be implemented');
70
+ }
71
+
72
+ /**
73
+ * Computes SHA-256 hash
74
+ * @param {Uint8Array} data - Data to hash
75
+ * @returns {Uint8Array} Hash (32 bytes)
76
+ */
77
+ hash(_data) {
78
+ throw new Error('CryptoProvider.hash() must be implemented');
79
+ }
80
+
81
+ /**
82
+ * Generates cryptographically secure random bytes
83
+ * @param {number} length - Number of bytes
84
+ * @returns {Uint8Array} Random bytes
85
+ */
86
+ randomBytes(_length) {
87
+ throw new Error('CryptoProvider.randomBytes() must be implemented');
88
+ }
89
+
90
+ /**
91
+ * Checks if this provider is available in the current environment
92
+ * @returns {boolean}
93
+ */
94
+ static isAvailable() {
95
+ return false;
96
+ }
97
+ }
98
+
99
+ module.exports = CryptoProvider;
@@ -1,72 +1,24 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * Crypto Module
5
- * Main exports for all cryptographic primitives and protocols.
4
+ * @fileoverview Crypto module — pluggable provider system
6
5
  * @module crypto
6
+ *
7
+ * Provides a CryptoProvider interface with auto-detection:
8
+ * - TweetNaClProvider (tweetnacl) — works everywhere
9
+ * - QuickCryptoProvider (react-native-quick-crypto) — native speed
10
+ * - ExpoCryptoProvider (expo-crypto) — for Expo projects
7
11
  */
8
12
 
9
- // Hash functions
10
- const { hash, createHash, HashContext } = require('./sha256');
11
- const { hmacSha256, verifyHmac } = require('./hmac');
12
- const { extract, expand, derive, deriveMultiple } = require('./hkdf');
13
-
14
- // Symmetric encryption
15
- const { chacha20, chacha20Block } = require('./chacha20');
16
- const { poly1305 } = require('./poly1305');
17
- const { encrypt, decrypt } = require('./aead');
18
-
19
- // Asymmetric encryption (key exchange)
20
- const { generateKeyPair, scalarMult, scalarMultBase } = require('./x25519');
21
-
22
- // Noise Protocol
23
- const noise = require('./noise');
24
-
25
- // Key management
26
- const keys = require('./keys');
13
+ const CryptoProvider = require('./CryptoProvider');
14
+ const { TweetNaClProvider, QuickCryptoProvider, ExpoCryptoProvider } = require('./providers');
15
+ const { detectProvider, createProvider } = require('./AutoCrypto');
27
16
 
28
17
  module.exports = {
29
- // SHA-256
30
- hash,
31
- createHash,
32
- HashContext,
33
-
34
- // HMAC
35
- hmacSha256,
36
- verifyHmac,
37
-
38
- // HKDF
39
- hkdfExtract: extract,
40
- hkdfExpand: expand,
41
- hkdf: derive,
42
- hkdfMultiple: deriveMultiple,
43
-
44
- // ChaCha20
45
- chacha20,
46
- chacha20Block,
47
-
48
- // Poly1305
49
- poly1305,
50
-
51
- // AEAD (ChaCha20-Poly1305)
52
- encrypt,
53
- decrypt,
54
-
55
- // X25519
56
- generateKeyPair,
57
- scalarMult,
58
- scalarMultBase,
59
-
60
- // Noise Protocol (as namespace)
61
- noise,
62
-
63
- // Key management (as namespace)
64
- keys,
65
-
66
- // Re-export commonly used classes at top level
67
- NoiseHandshake: noise.NoiseHandshake,
68
- NoiseSession: noise.NoiseSession,
69
- SymmetricState: noise.SymmetricState,
70
- KeyPair: keys.KeyPair,
71
- KeyManager: keys.KeyManager
18
+ CryptoProvider,
19
+ TweetNaClProvider,
20
+ QuickCryptoProvider,
21
+ ExpoCryptoProvider,
22
+ detectProvider,
23
+ createProvider
72
24
  };
@@ -0,0 +1,125 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview Expo-crypto based provider
5
+ * @module crypto/providers/ExpoCryptoProvider
6
+ *
7
+ * Uses expo-crypto for Expo managed workflow projects.
8
+ * Note: expo-crypto provides hashing and random bytes but NOT key exchange or AEAD.
9
+ * Falls back to tweetnacl for those operations.
10
+ *
11
+ * Install: npx expo install expo-crypto tweetnacl
12
+ */
13
+
14
+ const CryptoProvider = require('../CryptoProvider');
15
+
16
+ /**
17
+ * Crypto provider for Expo projects.
18
+ * Uses expo-crypto for hashing/random, tweetnacl for key exchange and AEAD.
19
+ *
20
+ * @class ExpoCryptoProvider
21
+ * @extends CryptoProvider
22
+ */
23
+ class ExpoCryptoProvider extends CryptoProvider {
24
+ constructor(options = {}) {
25
+ super();
26
+ this._expoCrypto = options.expoCrypto || null;
27
+ this._nacl = options.nacl || null;
28
+ }
29
+
30
+ get name() {
31
+ return 'expo-crypto';
32
+ }
33
+
34
+ _getExpoCrypto() {
35
+ if (!this._expoCrypto) {
36
+ try {
37
+ this._expoCrypto = require('expo-crypto');
38
+ } catch (e) {
39
+ throw new Error('expo-crypto is required. Install: npx expo install expo-crypto');
40
+ }
41
+ }
42
+ return this._expoCrypto;
43
+ }
44
+
45
+ _getNacl() {
46
+ if (!this._nacl) {
47
+ try {
48
+ this._nacl = require('tweetnacl');
49
+ } catch (e) {
50
+ throw new Error('tweetnacl is required with ExpoCryptoProvider. Install: npm install tweetnacl');
51
+ }
52
+ }
53
+ return this._nacl;
54
+ }
55
+
56
+ /** @inheritdoc */
57
+ generateKeyPair() {
58
+ const nacl = this._getNacl();
59
+ const kp = nacl.box.keyPair();
60
+ return { publicKey: kp.publicKey, secretKey: kp.secretKey };
61
+ }
62
+
63
+ /** @inheritdoc */
64
+ sharedSecret(secretKey, publicKey) {
65
+ const nacl = this._getNacl();
66
+ return nacl.box.before(publicKey, secretKey);
67
+ }
68
+
69
+ /** @inheritdoc */
70
+ encrypt(key, nonce, plaintext, _ad) {
71
+ const nacl = this._getNacl();
72
+
73
+ // Ensure 24-byte nonce (pad short nonces with zeros)
74
+ let fullNonce = nonce;
75
+ if (nonce.length < 24) {
76
+ fullNonce = new Uint8Array(24);
77
+ fullNonce.set(nonce);
78
+ }
79
+
80
+ return nacl.secretbox(plaintext, fullNonce, key);
81
+ }
82
+
83
+ /** @inheritdoc */
84
+ decrypt(key, nonce, ciphertext, _ad) {
85
+ const nacl = this._getNacl();
86
+
87
+ // Ensure 24-byte nonce (pad short nonces with zeros)
88
+ let fullNonce = nonce;
89
+ if (nonce.length < 24) {
90
+ fullNonce = new Uint8Array(24);
91
+ fullNonce.set(nonce);
92
+ }
93
+
94
+ return nacl.secretbox.open(ciphertext, fullNonce, key) || null;
95
+ }
96
+
97
+ /** @inheritdoc */
98
+ hash(data) {
99
+ // expo-crypto's digestStringAsync is async — for sync compat, use tweetnacl
100
+ const nacl = this._getNacl();
101
+ return nacl.hash(data).slice(0, 32);
102
+ }
103
+
104
+ /** @inheritdoc */
105
+ randomBytes(length) {
106
+ const expoCrypto = this._getExpoCrypto();
107
+ if (expoCrypto.getRandomBytes) {
108
+ return new Uint8Array(expoCrypto.getRandomBytes(length));
109
+ }
110
+ // Fallback to tweetnacl
111
+ const nacl = this._getNacl();
112
+ return nacl.randomBytes(length);
113
+ }
114
+
115
+ static isAvailable() {
116
+ try {
117
+ require('expo-crypto');
118
+ return true;
119
+ } catch (e) {
120
+ return false;
121
+ }
122
+ }
123
+ }
124
+
125
+ module.exports = ExpoCryptoProvider;
@@ -0,0 +1,134 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview react-native-quick-crypto based provider
5
+ * @module crypto/providers/QuickCryptoProvider
6
+ *
7
+ * Uses native crypto via react-native-quick-crypto for maximum performance.
8
+ * Install: npm install react-native-quick-crypto
9
+ */
10
+
11
+ const CryptoProvider = require('../CryptoProvider');
12
+
13
+ /**
14
+ * Crypto provider using react-native-quick-crypto.
15
+ * Provides native-speed crypto on React Native (JSI binding).
16
+ *
17
+ * @class QuickCryptoProvider
18
+ * @extends CryptoProvider
19
+ */
20
+ class QuickCryptoProvider extends CryptoProvider {
21
+ constructor(options = {}) {
22
+ super();
23
+ this._crypto = options.crypto || null;
24
+ }
25
+
26
+ get name() {
27
+ return 'quick-crypto';
28
+ }
29
+
30
+ _getCrypto() {
31
+ if (!this._crypto) {
32
+ try {
33
+ this._crypto = require('react-native-quick-crypto');
34
+ } catch (e) {
35
+ throw new Error(
36
+ 'react-native-quick-crypto is required. Install: npm install react-native-quick-crypto'
37
+ );
38
+ }
39
+ }
40
+ return this._crypto;
41
+ }
42
+
43
+ /** @inheritdoc */
44
+ generateKeyPair() {
45
+ const crypto = this._getCrypto();
46
+ const { publicKey, privateKey } = crypto.generateKeyPairSync('x25519');
47
+ return {
48
+ publicKey: new Uint8Array(publicKey.export({ type: 'spki', format: 'der' }).slice(-32)),
49
+ secretKey: new Uint8Array(privateKey.export({ type: 'pkcs8', format: 'der' }).slice(-32))
50
+ };
51
+ }
52
+
53
+ /** @inheritdoc */
54
+ sharedSecret(secretKey, publicKey) {
55
+ const crypto = this._getCrypto();
56
+ const privKey = crypto.createPrivateKey({
57
+ key: Buffer.concat([
58
+ Buffer.from('302e020100300506032b656e04220420', 'hex'),
59
+ Buffer.from(secretKey)
60
+ ]),
61
+ format: 'der',
62
+ type: 'pkcs8'
63
+ });
64
+ const pubKey = crypto.createPublicKey({
65
+ key: Buffer.concat([
66
+ Buffer.from('302a300506032b656e032100', 'hex'),
67
+ Buffer.from(publicKey)
68
+ ]),
69
+ format: 'der',
70
+ type: 'spki'
71
+ });
72
+ const shared = crypto.diffieHellman({ privateKey: privKey, publicKey: pubKey });
73
+ return new Uint8Array(shared);
74
+ }
75
+
76
+ /** @inheritdoc */
77
+ encrypt(key, nonce, plaintext, _ad) {
78
+ // Use tweetnacl for encryption to ensure cross-provider compatibility
79
+ // QuickCrypto's advantage is in fast native key generation (X25519), not AEAD
80
+ const nacl = require('tweetnacl');
81
+
82
+ // Ensure 24-byte nonce for XSalsa20-Poly1305
83
+ let fullNonce = nonce;
84
+ if (nonce.length < 24) {
85
+ fullNonce = new Uint8Array(24);
86
+ fullNonce.set(nonce);
87
+ }
88
+
89
+ return nacl.secretbox(plaintext, fullNonce, key);
90
+ }
91
+
92
+ /** @inheritdoc */
93
+ decrypt(key, nonce, ciphertext, _ad) {
94
+ const nacl = require('tweetnacl');
95
+
96
+ // Ensure 24-byte nonce for XSalsa20-Poly1305
97
+ let fullNonce = nonce;
98
+ if (nonce.length < 24) {
99
+ fullNonce = new Uint8Array(24);
100
+ fullNonce.set(nonce);
101
+ }
102
+
103
+ const result = nacl.secretbox.open(ciphertext, fullNonce, key);
104
+ if (!result) {
105
+ return null; // Decryption failed: authentication error
106
+ }
107
+ return result;
108
+ }
109
+
110
+ /** @inheritdoc */
111
+ hash(data) {
112
+ // Use SHA-512 truncated to 32 bytes for cross-provider compatibility
113
+ const nacl = require('tweetnacl');
114
+ const full = nacl.hash(data); // SHA-512
115
+ return full.slice(0, 32);
116
+ }
117
+
118
+ /** @inheritdoc */
119
+ randomBytes(length) {
120
+ const crypto = this._getCrypto();
121
+ return new Uint8Array(crypto.randomBytes(length));
122
+ }
123
+
124
+ static isAvailable() {
125
+ try {
126
+ require('react-native-quick-crypto');
127
+ return true;
128
+ } catch (e) {
129
+ return false;
130
+ }
131
+ }
132
+ }
133
+
134
+ module.exports = QuickCryptoProvider;
@@ -0,0 +1,124 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview TweetNaCl-based crypto provider
5
+ * @module crypto/providers/TweetNaClProvider
6
+ *
7
+ * Uses the `tweetnacl` library — lightweight, audited, works everywhere.
8
+ * Install: npm install tweetnacl
9
+ */
10
+
11
+ const CryptoProvider = require('../CryptoProvider');
12
+
13
+ /**
14
+ * Crypto provider using tweetnacl.
15
+ * Provides X25519 key exchange, XSalsa20-Poly1305 AEAD, SHA-512 (for hashing).
16
+ *
17
+ * @class TweetNaClProvider
18
+ * @extends CryptoProvider
19
+ */
20
+ class TweetNaClProvider extends CryptoProvider {
21
+ /**
22
+ * @param {Object} [options={}]
23
+ * @param {Object} [options.nacl] - Injected tweetnacl instance (for testing)
24
+ */
25
+ constructor(options = {}) {
26
+ super();
27
+ this._nacl = options.nacl || null;
28
+ }
29
+
30
+ get name() {
31
+ return 'tweetnacl';
32
+ }
33
+
34
+ /**
35
+ * Lazily loads tweetnacl
36
+ * @returns {Object} nacl module
37
+ * @private
38
+ */
39
+ _getNacl() {
40
+ if (!this._nacl) {
41
+ try {
42
+ this._nacl = require('tweetnacl');
43
+ } catch (e) {
44
+ throw new Error(
45
+ 'tweetnacl is required for TweetNaClProvider. Install: npm install tweetnacl'
46
+ );
47
+ }
48
+ }
49
+ return this._nacl;
50
+ }
51
+
52
+ /** @inheritdoc */
53
+ generateKeyPair() {
54
+ const nacl = this._getNacl();
55
+ const kp = nacl.box.keyPair();
56
+ return { publicKey: kp.publicKey, secretKey: kp.secretKey };
57
+ }
58
+
59
+ /** @inheritdoc */
60
+ sharedSecret(secretKey, publicKey) {
61
+ const nacl = this._getNacl();
62
+ return nacl.box.before(publicKey, secretKey);
63
+ }
64
+
65
+ /** @inheritdoc */
66
+ encrypt(key, nonce, plaintext, _ad) {
67
+ const nacl = this._getNacl();
68
+ // tweetnacl uses XSalsa20-Poly1305 with 24-byte nonce
69
+ // nacl.secretbox includes authentication
70
+
71
+ // Ensure 24-byte nonce (pad short nonces with zeros)
72
+ let fullNonce = nonce;
73
+ if (nonce.length < 24) {
74
+ fullNonce = new Uint8Array(24);
75
+ fullNonce.set(nonce);
76
+ }
77
+
78
+ return nacl.secretbox(plaintext, fullNonce, key);
79
+ }
80
+
81
+ /** @inheritdoc */
82
+ decrypt(key, nonce, ciphertext, _ad) {
83
+ const nacl = this._getNacl();
84
+
85
+ // Ensure 24-byte nonce (pad short nonces with zeros)
86
+ let fullNonce = nonce;
87
+ if (nonce.length < 24) {
88
+ fullNonce = new Uint8Array(24);
89
+ fullNonce.set(nonce);
90
+ }
91
+
92
+ const result = nacl.secretbox.open(ciphertext, fullNonce, key);
93
+ return result || null; // returns null on auth failure
94
+ }
95
+
96
+ /** @inheritdoc */
97
+ hash(data) {
98
+ const nacl = this._getNacl();
99
+ // tweetnacl provides SHA-512; we return first 32 bytes for SHA-256 compatibility
100
+ const full = nacl.hash(data);
101
+ return full.slice(0, 32);
102
+ }
103
+
104
+ /** @inheritdoc */
105
+ randomBytes(length) {
106
+ const nacl = this._getNacl();
107
+ return nacl.randomBytes(length);
108
+ }
109
+
110
+ /**
111
+ * Checks if tweetnacl is available
112
+ * @returns {boolean}
113
+ */
114
+ static isAvailable() {
115
+ try {
116
+ require('tweetnacl');
117
+ return true;
118
+ } catch (e) {
119
+ return false;
120
+ }
121
+ }
122
+ }
123
+
124
+ module.exports = TweetNaClProvider;
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ const TweetNaClProvider = require('./TweetNaClProvider');
4
+ const QuickCryptoProvider = require('./QuickCryptoProvider');
5
+ const ExpoCryptoProvider = require('./ExpoCryptoProvider');
6
+
7
+ module.exports = {
8
+ TweetNaClProvider,
9
+ QuickCryptoProvider,
10
+ ExpoCryptoProvider
11
+ };
@@ -20,7 +20,8 @@ class MeshError extends Error {
20
20
  * @param {Object|null} [details=null] - Additional error context
21
21
  */
22
22
  constructor(message, code = 'E900', details = null) {
23
- super(message);
23
+ const className = new.target ? new.target.name : 'MeshError';
24
+ super(`${className}: ${message}`);
24
25
 
25
26
  /**
26
27
  * Error name