@ethora/chat-component-rn 26.10.6 → 26.10.7

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 (73) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/README.md +6 -3
  3. package/lib/commonjs/helpers/secureStoreRuntime.js +10 -1
  4. package/lib/commonjs/helpers/secureStoreRuntime.js.map +1 -1
  5. package/lib/commonjs/main.js +81 -0
  6. package/lib/commonjs/main.js.map +1 -1
  7. package/lib/commonjs/networking/api-requests/push.api.js +22 -7
  8. package/lib/commonjs/networking/api-requests/push.api.js.map +1 -1
  9. package/lib/commonjs/push/pushKeyStore.js +169 -0
  10. package/lib/commonjs/push/pushKeyStore.js.map +1 -0
  11. package/lib/commonjs/push/sealedBox.js +106 -0
  12. package/lib/commonjs/push/sealedBox.js.map +1 -0
  13. package/lib/commonjs/push/sealedPush.js +124 -0
  14. package/lib/commonjs/push/sealedPush.js.map +1 -0
  15. package/lib/commonjs/push/sealedPushTask.js +195 -0
  16. package/lib/commonjs/push/sealedPushTask.js.map +1 -0
  17. package/lib/commonjs/services/pushRegistration.js +12 -7
  18. package/lib/commonjs/services/pushRegistration.js.map +1 -1
  19. package/lib/commonjs/version.js +1 -1
  20. package/lib/module/helpers/secureStoreRuntime.js +10 -1
  21. package/lib/module/helpers/secureStoreRuntime.js.map +1 -1
  22. package/lib/module/main.js +6 -0
  23. package/lib/module/main.js.map +1 -1
  24. package/lib/module/networking/api-requests/push.api.js +21 -6
  25. package/lib/module/networking/api-requests/push.api.js.map +1 -1
  26. package/lib/module/push/pushKeyStore.js +159 -0
  27. package/lib/module/push/pushKeyStore.js.map +1 -0
  28. package/lib/module/push/sealedBox.js +97 -0
  29. package/lib/module/push/sealedBox.js.map +1 -0
  30. package/lib/module/push/sealedPush.js +117 -0
  31. package/lib/module/push/sealedPush.js.map +1 -0
  32. package/lib/module/push/sealedPushTask.js +187 -0
  33. package/lib/module/push/sealedPushTask.js.map +1 -0
  34. package/lib/module/services/pushRegistration.js +12 -7
  35. package/lib/module/services/pushRegistration.js.map +1 -1
  36. package/lib/module/version.js +1 -1
  37. package/lib/typescript/helpers/secureStoreRuntime.d.ts +17 -3
  38. package/lib/typescript/helpers/secureStoreRuntime.d.ts.map +1 -1
  39. package/lib/typescript/main.d.ts +6 -0
  40. package/lib/typescript/main.d.ts.map +1 -1
  41. package/lib/typescript/networking/api-requests/push.api.d.ts +14 -2
  42. package/lib/typescript/networking/api-requests/push.api.d.ts.map +1 -1
  43. package/lib/typescript/push/pushKeyStore.d.ts +35 -0
  44. package/lib/typescript/push/pushKeyStore.d.ts.map +1 -0
  45. package/lib/typescript/push/sealedBox.d.ts +24 -0
  46. package/lib/typescript/push/sealedBox.d.ts.map +1 -0
  47. package/lib/typescript/push/sealedPush.d.ts +61 -0
  48. package/lib/typescript/push/sealedPush.d.ts.map +1 -0
  49. package/lib/typescript/push/sealedPushTask.d.ts +33 -0
  50. package/lib/typescript/push/sealedPushTask.d.ts.map +1 -0
  51. package/lib/typescript/services/pushRegistration.d.ts +8 -0
  52. package/lib/typescript/services/pushRegistration.d.ts.map +1 -1
  53. package/lib/typescript/types/types.d.ts +6 -0
  54. package/lib/typescript/types/types.d.ts.map +1 -1
  55. package/lib/typescript/version.d.ts +1 -1
  56. package/package.json +11 -1
  57. package/plugins/notification-service/NotificationService.swift +113 -0
  58. package/plugins/notification-service/SealedBox.swift +355 -0
  59. package/plugins/notification-service/check/gen-vectors.mjs +24 -0
  60. package/plugins/notification-service/check/main.swift +37 -0
  61. package/plugins/notification-service/check/run.sh +12 -0
  62. package/plugins/notification-service/check/vectors.json +1 -0
  63. package/plugins/withNotificationServiceExtension.js +70 -18
  64. package/src/helpers/secureStoreRuntime.ts +21 -3
  65. package/src/main.ts +30 -0
  66. package/src/networking/api-requests/push.api.ts +29 -4
  67. package/src/push/pushKeyStore.ts +163 -0
  68. package/src/push/sealedBox.ts +104 -0
  69. package/src/push/sealedPush.ts +127 -0
  70. package/src/push/sealedPushTask.ts +175 -0
  71. package/src/services/pushRegistration.ts +25 -5
  72. package/src/types/types.ts +6 -0
  73. package/src/version.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -7,7 +7,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this pr
7
7
 
8
8
  ### Added
9
9
 
10
- - **Attachment previews in iOS pushes.** New Expo config plugin (`"plugins": ["@ethora/chat-component-rn"]` in the host app.json) that adds a Notification Service Extension target, `EthoraNotificationService`, on `expo prebuild`. It downloads the thumbnail the push service sends as `previewUrl` for photo and video messages and shows it in the notification, the way Android already does. Text pushes and failed downloads show the notification unchanged; sealed pushes are not handled yet. Setup in the README, Install, step 4.
10
+ - **Encrypted pushes.** A device key per install (`ensurePushKeyPair`, X25519 in expo-secure-store) sent with the token as `pushKey` (`registerPushToken(token, { pushKey })`, `getPushTokens` entries, `PushSubscriptionPayload`); the push service then seals every chat push for it (libsodium sealed box) and Google and Apple relay ciphertext only. iOS: the notification service extension from the config plugin opens the box (plain Swift, CryptoKit X25519 and tweetnacl ports, no pod) and shows the real title and text; the plugin now shares a keychain access group between the app and the extension. Android: `registerSealedPushTask({ channelId })` defines the expo-task-manager task that opens the box and posts one notification per chat, also with the app killed. A box that does not open leaves the "New message" placeholder. A backend without `pushKey` refuses the registration instead of delivering in the clear. New optional peers `expo-notifications` and `expo-task-manager` (Android only). Setup and checks in docs/push-encryption.md.
11
+
12
+ - **Attachment previews in iOS pushes.** New Expo config plugin (`"plugins": ["@ethora/chat-component-rn"]` in the host app.json) that adds a Notification Service Extension target, `EthoraNotificationService`, on `expo prebuild`. It downloads the thumbnail the push service sends as `previewUrl` for photo and video messages and shows it in the notification, the way Android already does. Text pushes and failed downloads show the notification unchanged. Setup in the README, Install, step 4.
11
13
 
12
14
  - **Sending several files in one message.** The composer picks several files at once (documents and the library; the camera adds one shot), and every pick adds to the tray instead of replacing it. A message carries up to `config.attachments.maxFiles` files (default and hard ceiling 10), and `config.attachments.maxSizeMB` can lower the per-file size below the 50 MB upload limit; both refuse with a localized message. The files upload in parallel (fewer at a time in an e2ee room, where each is sealed first) and go out as ONE message in selection order. Text typed with the files is their caption: a plain room carries it on `<data>`, an e2ee room inside the sealed body. Each file shows its own upload progress on the bubble, and a file that did not upload gets its own Retry; a failed send keeps every file, so Retry re-uploads only what failed, also after a restart. A single file sends exactly the stanza it always did.
13
15
 
package/README.md CHANGED
@@ -96,9 +96,9 @@ cd ios && pod install
96
96
 
97
97
  After `npx expo prebuild` (or first `npm run ios` in an Expo project), the pods install runs automatically.
98
98
 
99
- ### 4. Attachment previews in iOS pushes (optional)
99
+ ### 4. Pushes: the iOS extension, encrypted pushes (optional)
100
100
 
101
- The push service sends a photo or video message with its thumbnail (`previewUrl`, `mutable-content: 1`). Android shows it by itself; iOS needs a Notification Service Extension in the app. The SDK ships one as an Expo config plugin:
101
+ The SDK ships an Expo config plugin that adds a Notification Service Extension, `EthoraNotificationService`, to the app:
102
102
 
103
103
  ```json
104
104
  {
@@ -109,7 +109,10 @@ The push service sends a photo or video message with its thumbnail (`previewUrl`
109
109
  }
110
110
  ```
111
111
 
112
- `npx expo prebuild` then adds the `EthoraNotificationService` target (bundle id `<app bundle id>.EthoraNotificationService`), embeds it in the app and registers it for EAS Build credentials (`extra.eas.build.experimental.ios.appExtensions`). The signing team comes from the plugin option `appleTeamId`, else `ios.appleTeamId`, else the team already set on the app target. The extension downloads the thumbnail and attaches it to the notification; text pushes and failed downloads (an expired link, no network, the iOS time limit) show the notification unchanged. The sender avatar is not attached. Encrypted (sealed) pushes are not handled yet.
112
+ `npx expo prebuild` then adds the target (bundle id `<app bundle id>.EthoraNotificationService`), embeds it in the app, shares a keychain access group between the two and registers the extension for EAS Build credentials (`extra.eas.build.experimental.ios.appExtensions`). The signing team comes from the plugin option `appleTeamId`, else `ios.appleTeamId`, else the team already set on the app target. The extension does two things:
113
+
114
+ - **Attachment previews.** A photo or video push carries its thumbnail (`previewUrl`, `mutable-content: 1`); Android shows it by itself, the extension attaches it on iOS. Text pushes and failed downloads show the notification unchanged.
115
+ - **Encrypted pushes.** A device that registers an X25519 public key with its token gets its chat pushes sealed (libsodium sealed box), so Google and Apple relay ciphertext only; the extension decrypts and shows the real text. Android does the same in a background task. Setup, the API (`ensurePushKeyPair`, `registerPushToken(token, { pushKey })`, `registerSealedPushTask`) and what to check are in [docs/push-encryption.md](docs/push-encryption.md).
113
116
 
114
117
  ## Quick start
115
118
 
@@ -18,6 +18,12 @@ exports.loadSecureStore = exports.isSecureStoreAvailable = exports.__resetSecure
18
18
  * AsyncStorage rather than crash (see `secureKeyValue.ts`).
19
19
  */
20
20
 
21
+ /**
22
+ * The subset of expo-secure-store's options the SDK uses. `keychainService`
23
+ * must be repeated on every read of an item written with it; the
24
+ * accessibility constants come from `SecureStoreRuntime.accessible`.
25
+ */
26
+
21
27
  let cached;
22
28
 
23
29
  /**
@@ -39,7 +45,10 @@ const loadSecureStore = () => {
39
45
  cached = {
40
46
  getItemAsync: mod.getItemAsync,
41
47
  setItemAsync: mod.setItemAsync,
42
- deleteItemAsync: mod.deleteItemAsync
48
+ deleteItemAsync: mod.deleteItemAsync,
49
+ accessible: {
50
+ afterFirstUnlockThisDeviceOnly: mod.AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY
51
+ }
43
52
  };
44
53
  }
45
54
  } catch {
@@ -1 +1 @@
1
- {"version":3,"names":["cached","loadSecureStore","undefined","mod","require","getItemAsync","setItemAsync","deleteItemAsync","exports","isSecureStoreAvailable","__resetSecureStoreRuntimeForTests"],"sourceRoot":"../../../src","sources":["helpers/secureStoreRuntime.ts"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAQA,IAAIA,MAA6C;;AAEjD;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,eAAe,GAAGA,CAAA,KAAiC;EAC9D,IAAID,MAAM,KAAKE,SAAS,EAAE;IACxB,OAAOF,MAAM;EACf;EAEA,IAAI;IACF;IACA,MAAMG,GAAG,GAAGC,OAAO,CAAC,mBAAmB,CAAC;IACxC,IACE,OAAOD,GAAG,EAAEE,YAAY,KAAK,UAAU,IACvC,OAAOF,GAAG,EAAEG,YAAY,KAAK,UAAU,IACvC,OAAOH,GAAG,EAAEI,eAAe,KAAK,UAAU,EAC1C;MACAP,MAAM,GAAG,IAAI;IACf,CAAC,MAAM;MACLA,MAAM,GAAG;QACPK,YAAY,EAAEF,GAAG,CAACE,YAAY;QAC9BC,YAAY,EAAEH,GAAG,CAACG,YAAY;QAC9BC,eAAe,EAAEJ,GAAG,CAACI;MACvB,CAAC;IACH;EACF,CAAC,CAAC,MAAM;IACNP,MAAM,GAAG,IAAI;EACf;EAEA,OAAOA,MAAM;AACf,CAAC;AAACQ,OAAA,CAAAP,eAAA,GAAAA,eAAA;AAEK,MAAMQ,sBAAsB,GAAGA,CAAA,KAAeR,eAAe,CAAC,CAAC,KAAK,IAAI;;AAE/E;AAAAO,OAAA,CAAAC,sBAAA,GAAAA,sBAAA;AACO,MAAMC,iCAAiC,GAAGA,CAAA,KAAY;EAC3DV,MAAM,GAAGE,SAAS;AACpB,CAAC;AAACM,OAAA,CAAAE,iCAAA,GAAAA,iCAAA","ignoreList":[]}
1
+ {"version":3,"names":["cached","loadSecureStore","undefined","mod","require","getItemAsync","setItemAsync","deleteItemAsync","accessible","afterFirstUnlockThisDeviceOnly","AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY","exports","isSecureStoreAvailable","__resetSecureStoreRuntimeForTests"],"sourceRoot":"../../../src","sources":["helpers/secureStoreRuntime.ts"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAiBA,IAAIA,MAA6C;;AAEjD;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,eAAe,GAAGA,CAAA,KAAiC;EAC9D,IAAID,MAAM,KAAKE,SAAS,EAAE;IACxB,OAAOF,MAAM;EACf;EAEA,IAAI;IACF;IACA,MAAMG,GAAG,GAAGC,OAAO,CAAC,mBAAmB,CAAC;IACxC,IACE,OAAOD,GAAG,EAAEE,YAAY,KAAK,UAAU,IACvC,OAAOF,GAAG,EAAEG,YAAY,KAAK,UAAU,IACvC,OAAOH,GAAG,EAAEI,eAAe,KAAK,UAAU,EAC1C;MACAP,MAAM,GAAG,IAAI;IACf,CAAC,MAAM;MACLA,MAAM,GAAG;QACPK,YAAY,EAAEF,GAAG,CAACE,YAAY;QAC9BC,YAAY,EAAEH,GAAG,CAACG,YAAY;QAC9BC,eAAe,EAAEJ,GAAG,CAACI,eAAe;QACpCC,UAAU,EAAE;UACVC,8BAA8B,EAAEN,GAAG,CAACO;QACtC;MACF,CAAC;IACH;EACF,CAAC,CAAC,MAAM;IACNV,MAAM,GAAG,IAAI;EACf;EAEA,OAAOA,MAAM;AACf,CAAC;AAACW,OAAA,CAAAV,eAAA,GAAAA,eAAA;AAEK,MAAMW,sBAAsB,GAAGA,CAAA,KAAeX,eAAe,CAAC,CAAC,KAAK,IAAI;;AAE/E;AAAAU,OAAA,CAAAC,sBAAA,GAAAA,sBAAA;AACO,MAAMC,iCAAiC,GAAGA,CAAA,KAAY;EAC3Db,MAAM,GAAGE,SAAS;AACpB,CAAC;AAACS,OAAA,CAAAE,iCAAA,GAAAA,iCAAA","ignoreList":[]}
@@ -33,6 +33,18 @@ Object.defineProperty(exports, "LIGHT_THEME", {
33
33
  return _theme.LIGHT_THEME;
34
34
  }
35
35
  });
36
+ Object.defineProperty(exports, "PUSH_KEY_KEYCHAIN_SERVICE", {
37
+ enumerable: true,
38
+ get: function () {
39
+ return _pushKeyStore.PUSH_KEY_KEYCHAIN_SERVICE;
40
+ }
41
+ });
42
+ Object.defineProperty(exports, "PUSH_SECRET_KEY_ITEM", {
43
+ enumerable: true,
44
+ get: function () {
45
+ return _pushKeyStore.PUSH_SECRET_KEY_ITEM;
46
+ }
47
+ });
36
48
  Object.defineProperty(exports, "PushRegistrationError", {
37
49
  enumerable: true,
38
50
  get: function () {
@@ -45,18 +57,48 @@ Object.defineProperty(exports, "RefreshFatalError", {
45
57
  return _authRefresh.RefreshFatalError;
46
58
  }
47
59
  });
60
+ Object.defineProperty(exports, "SEALED_PUSH_ALG", {
61
+ enumerable: true,
62
+ get: function () {
63
+ return _sealedPush.SEALED_PUSH_ALG;
64
+ }
65
+ });
66
+ Object.defineProperty(exports, "SEALED_PUSH_TASK", {
67
+ enumerable: true,
68
+ get: function () {
69
+ return _sealedPushTask.SEALED_PUSH_TASK;
70
+ }
71
+ });
48
72
  Object.defineProperty(exports, "XmppProvider", {
49
73
  enumerable: true,
50
74
  get: function () {
51
75
  return _xmppProvider.XmppProvider;
52
76
  }
53
77
  });
78
+ Object.defineProperty(exports, "clearPushKeyPair", {
79
+ enumerable: true,
80
+ get: function () {
81
+ return _pushKeyStore.clearPushKeyPair;
82
+ }
83
+ });
54
84
  Object.defineProperty(exports, "detectPushTokenType", {
55
85
  enumerable: true,
56
86
  get: function () {
57
87
  return _push.detectPushTokenType;
58
88
  }
59
89
  });
90
+ Object.defineProperty(exports, "ensurePushKeyPair", {
91
+ enumerable: true,
92
+ get: function () {
93
+ return _pushKeyStore.ensurePushKeyPair;
94
+ }
95
+ });
96
+ Object.defineProperty(exports, "getPushKeyPair", {
97
+ enumerable: true,
98
+ get: function () {
99
+ return _pushKeyStore.getPushKeyPair;
100
+ }
101
+ });
60
102
  Object.defineProperty(exports, "getRegisteredPushToken", {
61
103
  enumerable: true,
62
104
  get: function () {
@@ -105,6 +147,12 @@ Object.defineProperty(exports, "isRefreshFatalError", {
105
147
  return _authRefresh.isRefreshFatalError;
106
148
  }
107
149
  });
150
+ Object.defineProperty(exports, "isSealedPush", {
151
+ enumerable: true,
152
+ get: function () {
153
+ return _sealedPush.isSealedPush;
154
+ }
155
+ });
108
156
  Object.defineProperty(exports, "logoutService", {
109
157
  enumerable: true,
110
158
  get: function () {
@@ -123,6 +171,18 @@ Object.defineProperty(exports, "openRoomFromPush", {
123
171
  return _pushPayload.openRoomFromPush;
124
172
  }
125
173
  });
174
+ Object.defineProperty(exports, "openSealedPush", {
175
+ enumerable: true,
176
+ get: function () {
177
+ return _sealedPush.openSealedPush;
178
+ }
179
+ });
180
+ Object.defineProperty(exports, "presentSealedPush", {
181
+ enumerable: true,
182
+ get: function () {
183
+ return _sealedPushTask.presentSealedPush;
184
+ }
185
+ });
126
186
  Object.defineProperty(exports, "refreshAuthTokens", {
127
187
  enumerable: true,
128
188
  get: function () {
@@ -141,12 +201,24 @@ Object.defineProperty(exports, "registerPushToken", {
141
201
  return _pushRegistration.registerPushToken;
142
202
  }
143
203
  });
204
+ Object.defineProperty(exports, "registerSealedPushTask", {
205
+ enumerable: true,
206
+ get: function () {
207
+ return _sealedPushTask.registerSealedPushTask;
208
+ }
209
+ });
144
210
  Object.defineProperty(exports, "resendMessage", {
145
211
  enumerable: true,
146
212
  get: function () {
147
213
  return _resendMessage.resendMessage;
148
214
  }
149
215
  });
216
+ Object.defineProperty(exports, "resetPushKeyPair", {
217
+ enumerable: true,
218
+ get: function () {
219
+ return _pushKeyStore.resetPushKeyPair;
220
+ }
221
+ });
150
222
  Object.defineProperty(exports, "resolveStringTable", {
151
223
  enumerable: true,
152
224
  get: function () {
@@ -159,6 +231,12 @@ Object.defineProperty(exports, "resolveTheme", {
159
231
  return _theme.resolveTheme;
160
232
  }
161
233
  });
234
+ Object.defineProperty(exports, "sealedPushEnvelopeOf", {
235
+ enumerable: true,
236
+ get: function () {
237
+ return _sealedPush.sealedPushEnvelopeOf;
238
+ }
239
+ });
162
240
  Object.defineProperty(exports, "setPushEnabled", {
163
241
  enumerable: true,
164
242
  get: function () {
@@ -244,6 +322,9 @@ var _useUnreadMessagesCounter = require("./hooks/useUnreadMessagesCounter");
244
322
  var _pushPayload = require("./helpers/pushPayload");
245
323
  var _pushRegistration = require("./services/pushRegistration");
246
324
  var _push = require("./networking/api-requests/push.api");
325
+ var _pushKeyStore = require("./push/pushKeyStore");
326
+ var _sealedPushTask = require("./push/sealedPushTask");
327
+ var _sealedPush = require("./push/sealedPush");
247
328
  var _useChatRoomFocus = require("./hooks/useChatRoomFocus");
248
329
  var _useLogout = require("./hooks/useLogout");
249
330
  var _useQRCodeChatHandler = require("./hooks/useQRCodeChatHandler");
@@ -1 +1 @@
1
- {"version":3,"names":["require","_ReduxWrapper","_xmppProvider","_useUnreadMessagesCounter","_pushPayload","_pushRegistration","_push","_useChatRoomFocus","_useLogout","_useQRCodeChatHandler","_resendMessage","_useChatFonts","_useT","_strings","_authRefresh","_theme","_chatSettingsSlice","_e2ee"],"sourceRoot":"../../src","sources":["main.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBAA,OAAA;AAGA,IAAAC,aAAA,GAAAD,OAAA;AACA,IAAAE,aAAA,GAAAF,OAAA;AAGA,IAAAG,yBAAA,GAAAH,OAAA;AAEA,IAAAI,YAAA,GAAAJ,OAAA;AAEA,IAAAK,iBAAA,GAAAL,OAAA;AAYA,IAAAM,KAAA,GAAAN,OAAA;AAUA,IAAAO,iBAAA,GAAAP,OAAA;AACA,IAAAQ,UAAA,GAAAR,OAAA;AACA,IAAAS,qBAAA,GAAAT,OAAA;AACA,IAAAU,cAAA,GAAAV,OAAA;AAEA,IAAAW,aAAA,GAAAX,OAAA;AAKA,IAAAY,KAAA,GAAAZ,OAAA;AACA,IAAAa,QAAA,GAAAb,OAAA;AAqCA,IAAAc,YAAA,GAAAd,OAAA;AAYA,IAAAe,MAAA,GAAAf,OAAA;AASA,IAAAgB,kBAAA,GAAAhB,OAAA;AACA,IAAAiB,KAAA,GAAAjB,OAAA","ignoreList":[]}
1
+ {"version":3,"names":["require","_ReduxWrapper","_xmppProvider","_useUnreadMessagesCounter","_pushPayload","_pushRegistration","_push","_pushKeyStore","_sealedPushTask","_sealedPush","_useChatRoomFocus","_useLogout","_useQRCodeChatHandler","_resendMessage","_useChatFonts","_useT","_strings","_authRefresh","_theme","_chatSettingsSlice","_e2ee"],"sourceRoot":"../../src","sources":["main.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBAA,OAAA;AAGA,IAAAC,aAAA,GAAAD,OAAA;AACA,IAAAE,aAAA,GAAAF,OAAA;AAGA,IAAAG,yBAAA,GAAAH,OAAA;AAEA,IAAAI,YAAA,GAAAJ,OAAA;AAEA,IAAAK,iBAAA,GAAAL,OAAA;AAYA,IAAAM,KAAA,GAAAN,OAAA;AAaA,IAAAO,aAAA,GAAAP,OAAA;AASA,IAAAQ,eAAA,GAAAR,OAAA;AAMA,IAAAS,WAAA,GAAAT,OAAA;AAYA,IAAAU,iBAAA,GAAAV,OAAA;AACA,IAAAW,UAAA,GAAAX,OAAA;AACA,IAAAY,qBAAA,GAAAZ,OAAA;AACA,IAAAa,cAAA,GAAAb,OAAA;AAEA,IAAAc,aAAA,GAAAd,OAAA;AAKA,IAAAe,KAAA,GAAAf,OAAA;AACA,IAAAgB,QAAA,GAAAhB,OAAA;AAqCA,IAAAiB,YAAA,GAAAjB,OAAA;AAYA,IAAAkB,MAAA,GAAAlB,OAAA;AASA,IAAAmB,kBAAA,GAAAnB,OAAA;AACA,IAAAoB,KAAA,GAAApB,OAAA","ignoreList":[]}
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.isPushRegistrationError = exports.detectPushTokenType = exports.buildPushSubscriptionPayload = exports.buildPushGatewayPayload = exports.PushRegistrationError = void 0;
6
+ exports.isPushRegistrationError = exports.detectPushTokenType = exports.buildPushSubscriptionPayload = exports.buildPushGatewayPayload = exports.PushRegistrationError = exports.PUSH_KEY_ALG = void 0;
7
7
  exports.registerPushSubscription = registerPushSubscription;
8
8
  exports.resolvePushDeviceType = void 0;
9
9
  exports.subscribeToPushGateway = subscribeToPushGateway;
@@ -51,32 +51,47 @@ const toRegistrationError = (error, what) => {
51
51
  return new PushRegistrationError(status ? `${what} refused (HTTP ${status}): ${detail}` : `${what} failed: ${detail}`, status, body);
52
52
  };
53
53
  const isUnknownFieldError = error => error?.response?.status === 422 && /not allowed/i.test(String(error?.response?.data?.error ?? ''));
54
- const buildPushSubscriptionPayload = (token, tokenType) => ({
54
+
55
+ /** The only sealing scheme the push service knows (README, "Encrypted pushes"). */
56
+ const PUSH_KEY_ALG = exports.PUSH_KEY_ALG = 'sealed-box-x25519';
57
+ const buildPushSubscriptionPayload = (token, tokenType, pushKey, pushKeyId) => ({
55
58
  registrationToken: token,
56
59
  deviceType: resolvePushDeviceType(),
57
- tokenType
60
+ tokenType,
61
+ ...(pushKey ? {
62
+ pushKey,
63
+ pushKeyAlg: PUSH_KEY_ALG,
64
+ ...(pushKeyId ? {
65
+ pushKeyId
66
+ } : {})
67
+ } : {})
58
68
  });
59
69
  exports.buildPushSubscriptionPayload = buildPushSubscriptionPayload;
60
70
  async function registerPushSubscription({
61
71
  appId,
62
72
  token,
63
73
  tokenType,
64
- authToken
74
+ authToken,
75
+ pushKey,
76
+ pushKeyId
65
77
  }) {
66
78
  const path = `/v1/push/subscription/${encodeURIComponent(appId)}`;
67
79
  const headers = {
68
80
  Authorization: authToken
69
81
  };
70
- const payload = buildPushSubscriptionPayload(token, tokenType);
82
+ const payload = buildPushSubscriptionPayload(token, tokenType, pushKey, pushKeyId);
71
83
  try {
72
84
  await _apiClient.default.post(path, payload, {
73
85
  headers
74
86
  });
75
87
  return;
76
88
  } catch (error) {
77
- const canDowngrade = tokenType === 'apns' || tokenType === 'fcm';
89
+ // A backend that predates `tokenType` still delivers to apns / fcm
90
+ // tokens. One that predates `pushKey` would deliver in the clear: the
91
+ // host asked for encryption, so that is an error, never a quiet fallback.
92
+ const canDowngrade = (tokenType === 'apns' || tokenType === 'fcm') && !pushKey;
78
93
  if (!(canDowngrade && isUnknownFieldError(error))) {
79
- throw toRegistrationError(error, tokenType === 'expo' ? 'Push registration (the backend does not accept Expo push tokens yet)' : 'Push registration');
94
+ throw toRegistrationError(error, tokenType === 'expo' ? 'Push registration (the backend does not accept Expo push tokens yet)' : pushKey && isUnknownFieldError(error) ? 'Push registration (the backend does not support encrypted pushes)' : 'Push registration');
80
95
  }
81
96
  }
82
97
  try {
@@ -1 +1 @@
1
- {"version":3,"names":["_axios","_interopRequireDefault","require","_reactNative","_roomStore","_apiClient","e","__esModule","default","PushRegistrationError","Error","constructor","message","status","body","name","exports","isPushRegistrationError","error","EXPO_PUSH_TOKEN","APNS_TOKEN","detectPushTokenType","token","t","trim","test","resolvePushDeviceType","Platform","OS","toRegistrationError","what","response","data","detail","isUnknownFieldError","String","buildPushSubscriptionPayload","tokenType","registrationToken","deviceType","registerPushSubscription","appId","authToken","path","encodeURIComponent","headers","Authorization","payload","http","post","canDowngrade","unregisterPushSubscription","delete","endpoint","DEFAULT_XMPP_HOST","resolveXmppHost","config","store","getState","chatSettingStore","xmppSettings","host","gatewayAxios","axios","create","buildPushGatewayPayload","userJid","projectName","jid","includes","subscribeToPushGateway","args","baseURL","apiUrl"],"sourceRoot":"../../../../src","sources":["networking/api-requests/push.api.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_axios","_interopRequireDefault","require","_reactNative","_roomStore","_apiClient","e","__esModule","default","PushRegistrationError","Error","constructor","message","status","body","name","exports","isPushRegistrationError","error","EXPO_PUSH_TOKEN","APNS_TOKEN","detectPushTokenType","token","t","trim","test","resolvePushDeviceType","Platform","OS","toRegistrationError","what","response","data","detail","isUnknownFieldError","String","PUSH_KEY_ALG","buildPushSubscriptionPayload","tokenType","pushKey","pushKeyId","registrationToken","deviceType","pushKeyAlg","registerPushSubscription","appId","authToken","path","encodeURIComponent","headers","Authorization","payload","http","post","canDowngrade","unregisterPushSubscription","delete","endpoint","DEFAULT_XMPP_HOST","resolveXmppHost","config","store","getState","chatSettingStore","xmppSettings","host","gatewayAxios","axios","create","buildPushGatewayPayload","userJid","projectName","jid","includes","subscribeToPushGateway","args","baseURL","apiUrl"],"sourceRoot":"../../../../src","sources":["networking/api-requests/push.api.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.__resetPushKeyStoreForTests = exports.PUSH_SECRET_KEY_ITEM = exports.PUSH_PUBLIC_KEY_ITEM = exports.PUSH_KEY_KEYCHAIN_SERVICE = void 0;
7
+ exports.clearPushKeyPair = clearPushKeyPair;
8
+ exports.ensurePushKeyPair = ensurePushKeyPair;
9
+ exports.getPushKeyPair = getPushKeyPair;
10
+ exports.loadPushSecretKey = loadPushSecretKey;
11
+ exports.resetPushKeyPair = resetPushKeyPair;
12
+ var _crypto = require("../e2ee/crypto");
13
+ var _secureStoreRuntime = require("../helpers/secureStoreRuntime");
14
+ var _devLogger = require("../utils/devLogger");
15
+ var _sealedBox = require("./sealedBox");
16
+ // The device key for encrypted pushes: one X25519 pair per install, in the
17
+ // platform keychain / keystore through expo-secure-store. The public half
18
+ // goes to the push service with the token (`pushKey`); the secret half
19
+ // never leaves the device and is read by whoever renders a sealed push —
20
+ // the Android background task (`sealedPushTask.ts`, same process, same
21
+ // store) and the iOS notification service extension, which reads the
22
+ // keychain item directly (plugins/notification-service), so the item's
23
+ // attributes below are a contract with the Swift side:
24
+ // service `ethora.push` (expo-secure-store stores it as `ethora.push:no-auth`)
25
+ // account `ethora_push_secret_key`, UTF-8
26
+ // value base64 of the 32-byte secret key, UTF-8
27
+ // group the app's first keychain access group, which the config
28
+ // plugin shares with the extension
29
+ // Readable after the first unlock so a push that arrives while the phone
30
+ // is locked still decrypts; this device only, so an iCloud keychain copy
31
+ // on another device is never a key for pushes that go to this one.
32
+
33
+ const PUSH_KEY_KEYCHAIN_SERVICE = exports.PUSH_KEY_KEYCHAIN_SERVICE = 'ethora.push';
34
+ const PUSH_SECRET_KEY_ITEM = exports.PUSH_SECRET_KEY_ITEM = 'ethora_push_secret_key';
35
+ const PUSH_PUBLIC_KEY_ITEM = exports.PUSH_PUBLIC_KEY_ITEM = 'ethora_push_public_key';
36
+ let warnedMissingStore = false;
37
+ const warnMissingStoreOnce = () => {
38
+ if (warnedMissingStore) {
39
+ return;
40
+ }
41
+ warnedMissingStore = true;
42
+ (0, _devLogger.pushLog)('warn', 'push: expo-secure-store is not installed, so no device key for encrypted ' + 'pushes can be kept; pushes stay in the clear.');
43
+ };
44
+ const storeOptions = () => {
45
+ const store = (0, _secureStoreRuntime.loadSecureStore)();
46
+ if (!store) {
47
+ return null;
48
+ }
49
+ const accessible = store.accessible.afterFirstUnlockThisDeviceOnly;
50
+ return {
51
+ keychainService: PUSH_KEY_KEYCHAIN_SERVICE,
52
+ ...(accessible !== undefined ? {
53
+ keychainAccessible: accessible
54
+ } : {})
55
+ };
56
+ };
57
+ const readItem = async item => {
58
+ const store = (0, _secureStoreRuntime.loadSecureStore)();
59
+ const options = storeOptions();
60
+ if (!store || !options) {
61
+ return null;
62
+ }
63
+ try {
64
+ return await store.getItemAsync(item, options);
65
+ } catch (e) {
66
+ (0, _devLogger.pushLog)('warn', `push: reading ${item} failed`, e);
67
+ return null;
68
+ }
69
+ };
70
+ const decodeKey = (raw, length) => {
71
+ if (!raw) {
72
+ return null;
73
+ }
74
+ try {
75
+ const bytes = (0, _crypto.fromBase64)(raw);
76
+ return bytes.length === length ? bytes : null;
77
+ } catch {
78
+ return null;
79
+ }
80
+ };
81
+
82
+ /**
83
+ * The stored pair, or null when there is none (or it is unreadable).
84
+ * The public key is re-derived from the secret, so the pair is consistent
85
+ * even if only the secret item survived.
86
+ */
87
+ async function loadPushSecretKey() {
88
+ const secretKey = decodeKey(await readItem(PUSH_SECRET_KEY_ITEM), _sealedBox.SEALED_BOX_SECRET_KEY_BYTES);
89
+ if (!secretKey) {
90
+ return null;
91
+ }
92
+ const stored = decodeKey(await readItem(PUSH_PUBLIC_KEY_ITEM), _sealedBox.SEALED_BOX_PUBLIC_KEY_BYTES);
93
+ return {
94
+ publicKey: stored ?? (0, _sealedBox.sealedBoxPublicKeyOf)(secretKey),
95
+ secretKey
96
+ };
97
+ }
98
+ const describe = publicKey => ({
99
+ publicKey: (0, _crypto.toBase64)(publicKey),
100
+ keyId: (0, _sealedBox.sealedBoxKeyId)(publicKey)
101
+ });
102
+
103
+ /** The public half of the stored pair, null when the device has no key. */
104
+ async function getPushKeyPair() {
105
+ const key = await loadPushSecretKey();
106
+ return key ? describe(key.publicKey) : null;
107
+ }
108
+
109
+ /**
110
+ * Generate and store a new pair, replacing any existing one. Pushes sealed
111
+ * for the old key stop opening at once, so re-register the token with the
112
+ * new `publicKey` right after. Null when expo-secure-store is missing or
113
+ * the keychain refused the write.
114
+ */
115
+ async function resetPushKeyPair() {
116
+ const store = (0, _secureStoreRuntime.loadSecureStore)();
117
+ const options = storeOptions();
118
+ if (!store || !options) {
119
+ warnMissingStoreOnce();
120
+ return null;
121
+ }
122
+ const pair = (0, _sealedBox.generateSealedBoxKeyPair)();
123
+ try {
124
+ await store.setItemAsync(PUSH_SECRET_KEY_ITEM, (0, _crypto.toBase64)(pair.secretKey), options);
125
+ await store.setItemAsync(PUSH_PUBLIC_KEY_ITEM, (0, _crypto.toBase64)(pair.publicKey), options);
126
+ } catch (e) {
127
+ (0, _devLogger.pushLog)('warn', 'push: storing the device key failed', e);
128
+ return null;
129
+ }
130
+ const check = await loadPushSecretKey();
131
+ if (!check || (0, _crypto.toBase64)(check.publicKey) !== (0, _crypto.toBase64)(pair.publicKey)) {
132
+ (0, _devLogger.pushLog)('warn', 'push: the device key was not kept');
133
+ return null;
134
+ }
135
+ (0, _devLogger.pushLog)('rn', `push: new device key ${(0, _sealedBox.sealedBoxKeyId)(pair.publicKey)}`);
136
+ return describe(pair.publicKey);
137
+ }
138
+
139
+ /**
140
+ * The device key for encrypted pushes, created on first call. Hand
141
+ * `publicKey` to `registerPushToken` (or send it as `pushKey` yourself).
142
+ */
143
+ async function ensurePushKeyPair() {
144
+ const existing = await getPushKeyPair();
145
+ return existing ?? resetPushKeyPair();
146
+ }
147
+
148
+ /** Remove the pair; the next `ensurePushKeyPair` creates a new one. */
149
+ async function clearPushKeyPair() {
150
+ const store = (0, _secureStoreRuntime.loadSecureStore)();
151
+ const options = storeOptions();
152
+ if (!store || !options) {
153
+ return;
154
+ }
155
+ for (const item of [PUSH_SECRET_KEY_ITEM, PUSH_PUBLIC_KEY_ITEM]) {
156
+ try {
157
+ await store.deleteItemAsync(item, options);
158
+ } catch {
159
+ // Not there — nothing to remove.
160
+ }
161
+ }
162
+ }
163
+
164
+ /** Test seam only. */
165
+ const __resetPushKeyStoreForTests = () => {
166
+ warnedMissingStore = false;
167
+ };
168
+ exports.__resetPushKeyStoreForTests = __resetPushKeyStoreForTests;
169
+ //# sourceMappingURL=pushKeyStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_crypto","require","_secureStoreRuntime","_devLogger","_sealedBox","PUSH_KEY_KEYCHAIN_SERVICE","exports","PUSH_SECRET_KEY_ITEM","PUSH_PUBLIC_KEY_ITEM","warnedMissingStore","warnMissingStoreOnce","pushLog","storeOptions","store","loadSecureStore","accessible","afterFirstUnlockThisDeviceOnly","keychainService","undefined","keychainAccessible","readItem","item","options","getItemAsync","e","decodeKey","raw","length","bytes","fromBase64","loadPushSecretKey","secretKey","SEALED_BOX_SECRET_KEY_BYTES","stored","SEALED_BOX_PUBLIC_KEY_BYTES","publicKey","sealedBoxPublicKeyOf","describe","toBase64","keyId","sealedBoxKeyId","getPushKeyPair","key","resetPushKeyPair","pair","generateSealedBoxKeyPair","setItemAsync","check","ensurePushKeyPair","existing","clearPushKeyPair","deleteItemAsync","__resetPushKeyStoreForTests"],"sourceRoot":"../../../src","sources":["push/pushKeyStore.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.sealedBoxKeyId = exports.generateSealedBoxKeyPair = exports.SEALED_BOX_SECRET_KEY_BYTES = exports.SEALED_BOX_PUBLIC_KEY_BYTES = exports.SEALED_BOX_OVERHEAD_BYTES = void 0;
7
+ exports.sealedBoxOpen = sealedBoxOpen;
8
+ exports.sealedBoxPublicKeyOf = void 0;
9
+ exports.sealedBoxSeal = sealedBoxSeal;
10
+ var _salsa = require("@noble/ciphers/salsa.js");
11
+ var _utils = require("@noble/ciphers/utils.js");
12
+ var _ed = require("@noble/curves/ed25519.js");
13
+ var _blake = require("@noble/hashes/blake2.js");
14
+ var _sha = require("@noble/hashes/sha2.js");
15
+ var _utils2 = require("@noble/hashes/utils.js");
16
+ // libsodium's sealed box (`crypto_box_seal` / `crypto_box_seal_open`) on
17
+ // the noble primitives the e2ee code already ships, so the JS side needs
18
+ // no native libsodium. The push service seals every chat push for a
19
+ // device that registered an X25519 public key (ethora-backend,
20
+ // services/push/README.md, "Encrypted pushes"); this is the matching
21
+ // receiver. The iOS notification service extension decrypts the same
22
+ // boxes in Swift with libsodium (plugins/notification-service).
23
+ //
24
+ // Layout: ephemeral public key (32) || crypto_box(message, nonce,
25
+ // recipient pk, ephemeral sk), with crypto_box = XSalsa20-Poly1305 under
26
+ // HSalsa20(X25519 shared secret) and nonce = BLAKE2b-192(epk || rpk).
27
+ // `__tests__/sealedBox.test.ts` pins this against libsodium itself.
28
+
29
+ const SEALED_BOX_PUBLIC_KEY_BYTES = exports.SEALED_BOX_PUBLIC_KEY_BYTES = 32;
30
+ const SEALED_BOX_SECRET_KEY_BYTES = exports.SEALED_BOX_SECRET_KEY_BYTES = 32;
31
+ /** Ephemeral public key (32) + Poly1305 tag (16). */
32
+ const SEALED_BOX_OVERHEAD_BYTES = exports.SEALED_BOX_OVERHEAD_BYTES = 48;
33
+ const NONCE_BYTES = 24;
34
+ const SIGMA = (0, _utils.u32)((0, _utils.utf8ToBytes)('expand 32-byte k'));
35
+ const ZERO_NONCE_PREFIX = new Uint32Array(4);
36
+ const generateSealedBoxKeyPair = () => {
37
+ const {
38
+ secretKey,
39
+ publicKey
40
+ } = _ed.x25519.keygen();
41
+ return {
42
+ publicKey,
43
+ secretKey
44
+ };
45
+ };
46
+ exports.generateSealedBoxKeyPair = generateSealedBoxKeyPair;
47
+ const sealedBoxPublicKeyOf = secretKey => _ed.x25519.getPublicKey(secretKey);
48
+
49
+ /**
50
+ * The key id the push service derives when the registration carries none:
51
+ * the first 16 hex characters of SHA-256 over the raw public key. Sent back
52
+ * as `kid` in every sealed push.
53
+ */
54
+ exports.sealedBoxPublicKeyOf = sealedBoxPublicKeyOf;
55
+ const sealedBoxKeyId = publicKey => (0, _utils2.bytesToHex)((0, _sha.sha256)(publicKey)).slice(0, 16);
56
+
57
+ // crypto_box_beforenm: HSalsa20 over the X25519 shared secret.
58
+ exports.sealedBoxKeyId = sealedBoxKeyId;
59
+ const boxSharedKey = (theirPublicKey, mySecretKey) => {
60
+ const shared = _ed.x25519.getSharedSecret(mySecretKey, theirPublicKey);
61
+ const words = new Uint32Array(8);
62
+ (0, _salsa.hsalsa)(SIGMA, (0, _utils.u32)(shared), ZERO_NONCE_PREFIX, words);
63
+ (0, _utils.clean)(shared);
64
+ return (0, _utils.u8)(words);
65
+ };
66
+ const sealNonce = (ephemeralPublicKey, recipientPublicKey) => (0, _blake.blake2b)((0, _utils.concatBytes)(ephemeralPublicKey, recipientPublicKey), {
67
+ dkLen: NONCE_BYTES
68
+ });
69
+
70
+ /** `crypto_box_seal`: used by the tests and the key tool, not by the app. */
71
+ function sealedBoxSeal(message, recipientPublicKey) {
72
+ if (recipientPublicKey.length !== SEALED_BOX_PUBLIC_KEY_BYTES) {
73
+ throw new Error('sealedBoxSeal: the public key must be 32 bytes');
74
+ }
75
+ const ephemeral = generateSealedBoxKeyPair();
76
+ const key = boxSharedKey(recipientPublicKey, ephemeral.secretKey);
77
+ const nonce = sealNonce(ephemeral.publicKey, recipientPublicKey);
78
+ const box = (0, _salsa.xsalsa20poly1305)(key, nonce).encrypt(message);
79
+ (0, _utils.clean)(key, ephemeral.secretKey);
80
+ return (0, _utils.concatBytes)(ephemeral.publicKey, box);
81
+ }
82
+
83
+ /**
84
+ * `crypto_box_seal_open`. Returns null for anything that does not open:
85
+ * a wrong key, a box for another key, a damaged or truncated box.
86
+ */
87
+ function sealedBoxOpen(sealed, publicKey, secretKey) {
88
+ if (sealed.length < SEALED_BOX_OVERHEAD_BYTES || publicKey.length !== SEALED_BOX_PUBLIC_KEY_BYTES || secretKey.length !== SEALED_BOX_SECRET_KEY_BYTES) {
89
+ return null;
90
+ }
91
+ const ephemeralPublicKey = sealed.subarray(0, SEALED_BOX_PUBLIC_KEY_BYTES);
92
+ const box = sealed.subarray(SEALED_BOX_PUBLIC_KEY_BYTES);
93
+ let key = null;
94
+ try {
95
+ key = boxSharedKey(ephemeralPublicKey, secretKey);
96
+ const nonce = sealNonce(ephemeralPublicKey, publicKey);
97
+ return (0, _salsa.xsalsa20poly1305)(key, nonce).decrypt(box);
98
+ } catch {
99
+ return null;
100
+ } finally {
101
+ if (key) {
102
+ (0, _utils.clean)(key);
103
+ }
104
+ }
105
+ }
106
+ //# sourceMappingURL=sealedBox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_salsa","require","_utils","_ed","_blake","_sha","_utils2","SEALED_BOX_PUBLIC_KEY_BYTES","exports","SEALED_BOX_SECRET_KEY_BYTES","SEALED_BOX_OVERHEAD_BYTES","NONCE_BYTES","SIGMA","u32","utf8ToBytes","ZERO_NONCE_PREFIX","Uint32Array","generateSealedBoxKeyPair","secretKey","publicKey","x25519","keygen","sealedBoxPublicKeyOf","getPublicKey","sealedBoxKeyId","bytesToHex","sha256","slice","boxSharedKey","theirPublicKey","mySecretKey","shared","getSharedSecret","words","hsalsa","clean","u8","sealNonce","ephemeralPublicKey","recipientPublicKey","blake2b","concatBytes","dkLen","sealedBoxSeal","message","length","Error","ephemeral","key","nonce","box","xsalsa20poly1305","encrypt","sealedBoxOpen","sealed","subarray","decrypt"],"sourceRoot":"../../../src","sources":["push/sealedBox.ts"],"mappings":"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","ignoreList":[]}