fca-riyad 37.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 (242) hide show
  1. package/CHANGELOG.md +325 -0
  2. package/DOCS.md +2712 -0
  3. package/README.md +410 -0
  4. package/func/checkUpdate.js +7 -0
  5. package/func/logAdapter.js +33 -0
  6. package/func/logger.js +48 -0
  7. package/index.d.ts +751 -0
  8. package/index.js +22 -0
  9. package/module/config.js +40 -0
  10. package/module/login.js +133 -0
  11. package/module/loginHelper.js +1359 -0
  12. package/module/options.js +44 -0
  13. package/package.json +95 -0
  14. package/src/api/action/addExternalModule.js +25 -0
  15. package/src/api/action/changeAvatar.js +137 -0
  16. package/src/api/action/changeBio.js +75 -0
  17. package/src/api/action/enableAutoSaveAppState.js +73 -0
  18. package/src/api/action/getCurrentUserID.js +7 -0
  19. package/src/api/action/handleFriendRequest.js +57 -0
  20. package/src/api/action/logout.js +76 -0
  21. package/src/api/action/refreshFb_dtsg.js +48 -0
  22. package/src/api/action/setPostReaction.js +106 -0
  23. package/src/api/action/unfriend.js +54 -0
  24. package/src/api/http/httpGet.js +46 -0
  25. package/src/api/http/httpPost.js +52 -0
  26. package/src/api/http/postFormData.js +47 -0
  27. package/src/api/messaging/addUserToGroup.js +68 -0
  28. package/src/api/messaging/changeAdminStatus.js +126 -0
  29. package/src/api/messaging/changeArchivedStatus.js +55 -0
  30. package/src/api/messaging/changeBlockedStatus.js +48 -0
  31. package/src/api/messaging/changeGroupImage.js +91 -0
  32. package/src/api/messaging/changeNickname.js +70 -0
  33. package/src/api/messaging/changeThreadColor.js +79 -0
  34. package/src/api/messaging/changeThreadEmoji.js +111 -0
  35. package/src/api/messaging/createNewGroup.js +88 -0
  36. package/src/api/messaging/createPoll.js +46 -0
  37. package/src/api/messaging/createThemeAI.js +98 -0
  38. package/src/api/messaging/deleteMessage.js +136 -0
  39. package/src/api/messaging/deleteThread.js +56 -0
  40. package/src/api/messaging/editMessage.js +90 -0
  41. package/src/api/messaging/forwardAttachment.js +57 -0
  42. package/src/api/messaging/forwardMessage.js +134 -0
  43. package/src/api/messaging/getEmojiUrl.js +29 -0
  44. package/src/api/messaging/getFriendsList.js +82 -0
  45. package/src/api/messaging/getMessage.js +829 -0
  46. package/src/api/messaging/getThemePictures.js +62 -0
  47. package/src/api/messaging/handleMessageRequest.js +65 -0
  48. package/src/api/messaging/markAsDelivered.js +57 -0
  49. package/src/api/messaging/markAsRead.js +88 -0
  50. package/src/api/messaging/markAsReadAll.js +49 -0
  51. package/src/api/messaging/markAsSeen.js +61 -0
  52. package/src/api/messaging/muteThread.js +50 -0
  53. package/src/api/messaging/pinMessage.js +115 -0
  54. package/src/api/messaging/removeUserFromGroup.js +62 -0
  55. package/src/api/messaging/resolvePhotoUrl.js +43 -0
  56. package/src/api/messaging/scheduler.js +264 -0
  57. package/src/api/messaging/searchForThread.js +53 -0
  58. package/src/api/messaging/sendBroadcast.js +93 -0
  59. package/src/api/messaging/sendMessage.js +269 -0
  60. package/src/api/messaging/sendTypingIndicator.js +74 -0
  61. package/src/api/messaging/sessionGuard.js +130 -0
  62. package/src/api/messaging/setMessageReaction.js +95 -0
  63. package/src/api/messaging/setTitle.js +124 -0
  64. package/src/api/messaging/shareContact.js +49 -0
  65. package/src/api/messaging/threadColors.js +128 -0
  66. package/src/api/messaging/unsendMessage.js +81 -0
  67. package/src/api/messaging/uploadAttachment.js +492 -0
  68. package/src/api/socket/OldMessage.js +186 -0
  69. package/src/api/socket/core/connectMqtt.js +269 -0
  70. package/src/api/socket/core/emitAuth.js +103 -0
  71. package/src/api/socket/core/getSeqID.js +321 -0
  72. package/src/api/socket/core/getTaskResponseData.js +25 -0
  73. package/src/api/socket/core/parseDelta.js +387 -0
  74. package/src/api/socket/detail/buildStream.js +215 -0
  75. package/src/api/socket/detail/constants.js +28 -0
  76. package/src/api/socket/e2ee/crypto.js +173 -0
  77. package/src/api/socket/e2ee/e2ee/crypto.js +173 -0
  78. package/src/api/socket/e2ee/e2ee/index.js +421 -0
  79. package/src/api/socket/e2ee/e2ee/proto/ArmadilloApplication.proto +281 -0
  80. package/src/api/socket/e2ee/e2ee/proto/ArmadilloICDC.proto +14 -0
  81. package/src/api/socket/e2ee/e2ee/proto/ConsumerApplication.proto +232 -0
  82. package/src/api/socket/e2ee/e2ee/proto/MessageApplication.proto +82 -0
  83. package/src/api/socket/e2ee/e2ee/proto/MessageTransport.proto +77 -0
  84. package/src/api/socket/e2ee/e2ee/proto/WACommon.proto +66 -0
  85. package/src/api/socket/e2ee/e2ee/proto/WAMediaTransport.proto +176 -0
  86. package/src/api/socket/e2ee/e2ee/proto/proto-writer.ts +76 -0
  87. package/src/api/socket/e2ee/e2ee/protocol.js +196 -0
  88. package/src/api/socket/e2ee/e2ee/ratchet.js +219 -0
  89. package/src/api/socket/e2ee/e2ee/store.js +182 -0
  90. package/src/api/socket/e2ee/e2ee/vendor/fb-e2ee.cjs +9162 -0
  91. package/src/api/socket/e2ee/e2ee/vendor/proto/ArmadilloApplication.proto +281 -0
  92. package/src/api/socket/e2ee/e2ee/vendor/proto/ArmadilloICDC.proto +14 -0
  93. package/src/api/socket/e2ee/e2ee/vendor/proto/ConsumerApplication.proto +232 -0
  94. package/src/api/socket/e2ee/e2ee/vendor/proto/MessageApplication.proto +82 -0
  95. package/src/api/socket/e2ee/e2ee/vendor/proto/MessageTransport.proto +77 -0
  96. package/src/api/socket/e2ee/e2ee/vendor/proto/WACommon.proto +66 -0
  97. package/src/api/socket/e2ee/e2ee/vendor/proto/WAMediaTransport.proto +176 -0
  98. package/src/api/socket/e2ee/e2ee/vendor/proto/proto-writer.ts +76 -0
  99. package/src/api/socket/e2ee/index.js +437 -0
  100. package/src/api/socket/e2ee/proto/ArmadilloApplication.proto +281 -0
  101. package/src/api/socket/e2ee/proto/ArmadilloICDC.proto +14 -0
  102. package/src/api/socket/e2ee/proto/ConsumerApplication.proto +232 -0
  103. package/src/api/socket/e2ee/proto/MessageApplication.proto +82 -0
  104. package/src/api/socket/e2ee/proto/MessageTransport.proto +77 -0
  105. package/src/api/socket/e2ee/proto/WACommon.proto +66 -0
  106. package/src/api/socket/e2ee/proto/WAMediaTransport.proto +176 -0
  107. package/src/api/socket/e2ee/proto/proto-writer.ts +76 -0
  108. package/src/api/socket/e2ee/protocol.js +196 -0
  109. package/src/api/socket/e2ee/ratchet.js +219 -0
  110. package/src/api/socket/e2ee/store.js +182 -0
  111. package/src/api/socket/e2ee/vendor/fb-e2ee.cjs +9162 -0
  112. package/src/api/socket/e2ee/vendor/proto/ArmadilloApplication.proto +281 -0
  113. package/src/api/socket/e2ee/vendor/proto/ArmadilloICDC.proto +14 -0
  114. package/src/api/socket/e2ee/vendor/proto/ConsumerApplication.proto +232 -0
  115. package/src/api/socket/e2ee/vendor/proto/MessageApplication.proto +82 -0
  116. package/src/api/socket/e2ee/vendor/proto/MessageTransport.proto +77 -0
  117. package/src/api/socket/e2ee/vendor/proto/WACommon.proto +66 -0
  118. package/src/api/socket/e2ee/vendor/proto/WAMediaTransport.proto +176 -0
  119. package/src/api/socket/e2ee/vendor/proto/proto-writer.ts +76 -0
  120. package/src/api/socket/listenE2EE.js +75 -0
  121. package/src/api/socket/listenMqtt.js +388 -0
  122. package/src/api/socket/middleware/index.js +216 -0
  123. package/src/api/socket/sendMessage.js +309 -0
  124. package/src/api/socket/sendMessageMqtt.js +69 -0
  125. package/src/api/threads/getThreadHistory.js +664 -0
  126. package/src/api/threads/getThreadInfo.js +296 -0
  127. package/src/api/threads/getThreadList.js +293 -0
  128. package/src/api/threads/getThreadPictures.js +78 -0
  129. package/src/api/users/getUserID.js +65 -0
  130. package/src/api/users/getUserInfo.js +402 -0
  131. package/src/api/users/getUserInfoV2.js +134 -0
  132. package/src/app/MessengerBot.js +209 -0
  133. package/src/app/MessengerContext.js +32 -0
  134. package/src/app/createFcaClient.js +136 -0
  135. package/src/app/threadInfoRealtimeSync.js +284 -0
  136. package/src/core/sendReqMqtt.js +96 -0
  137. package/src/database/helpers.js +53 -0
  138. package/src/database/models/index.js +88 -0
  139. package/src/database/models/thread.js +50 -0
  140. package/src/database/models/user.js +46 -0
  141. package/src/database/threadData.js +94 -0
  142. package/src/database/userData.js +98 -0
  143. package/src/remote/remoteClient.js +123 -0
  144. package/src/utils/broadcast.js +51 -0
  145. package/src/utils/client.js +10 -0
  146. package/src/utils/constants.js +23 -0
  147. package/src/utils/cookies.js +68 -0
  148. package/src/utils/format/attachment.js +357 -0
  149. package/src/utils/format/cookie.js +9 -0
  150. package/src/utils/format/date.js +50 -0
  151. package/src/utils/format/decode.js +44 -0
  152. package/src/utils/format/delta.js +194 -0
  153. package/src/utils/format/ids.js +64 -0
  154. package/src/utils/format/index.js +64 -0
  155. package/src/utils/format/message.js +88 -0
  156. package/src/utils/format/presence.js +132 -0
  157. package/src/utils/format/readTyp.js +44 -0
  158. package/src/utils/format/thread.js +42 -0
  159. package/src/utils/format/utils.js +141 -0
  160. package/src/utils/headers.js +115 -0
  161. package/src/utils/loginParser/autoLogin.js +125 -0
  162. package/src/utils/loginParser/helpers.js +43 -0
  163. package/src/utils/loginParser/index.js +10 -0
  164. package/src/utils/loginParser/parseAndCheckLogin.js +220 -0
  165. package/src/utils/loginParser/textUtils.js +28 -0
  166. package/src/utils/nexca-logger.js +144 -0
  167. package/src/utils/nexca-utils.js +686 -0
  168. package/src/utils/request/client.js +26 -0
  169. package/src/utils/request/config.js +23 -0
  170. package/src/utils/request/defaults.js +46 -0
  171. package/src/utils/request/helpers.js +46 -0
  172. package/src/utils/request/index.js +17 -0
  173. package/src/utils/request/methods.js +163 -0
  174. package/src/utils/request/proxy.js +21 -0
  175. package/src/utils/request/retry.js +77 -0
  176. package/src/utils/request/sanitize.js +49 -0
  177. package/src/utils/versionCheck.js +47 -0
  178. package/src/vendor/fca-unofficial/.github/workflows/nodejs.yml +26 -0
  179. package/src/vendor/fca-unofficial/.github/workflows/npmpublish.yml +30 -0
  180. package/src/vendor/fca-unofficial/.travis.yml +6 -0
  181. package/src/vendor/fca-unofficial/DOCS.md +1731 -0
  182. package/src/vendor/fca-unofficial/LICENSE-MIT +21 -0
  183. package/src/vendor/fca-unofficial/README.md +225 -0
  184. package/src/vendor/fca-unofficial/index.js +619 -0
  185. package/src/vendor/fca-unofficial/package.json +76 -0
  186. package/src/vendor/fca-unofficial/src/addExternalModule.js +19 -0
  187. package/src/vendor/fca-unofficial/src/addUserToGroup.js +113 -0
  188. package/src/vendor/fca-unofficial/src/changeAdminStatus.js +79 -0
  189. package/src/vendor/fca-unofficial/src/changeArchivedStatus.js +55 -0
  190. package/src/vendor/fca-unofficial/src/changeBio.js +77 -0
  191. package/src/vendor/fca-unofficial/src/changeBlockedStatus.js +47 -0
  192. package/src/vendor/fca-unofficial/src/changeGroupImage.js +129 -0
  193. package/src/vendor/fca-unofficial/src/changeNickname.js +59 -0
  194. package/src/vendor/fca-unofficial/src/changeThreadColor.js +71 -0
  195. package/src/vendor/fca-unofficial/src/changeThreadEmoji.js +55 -0
  196. package/src/vendor/fca-unofficial/src/createNewGroup.js +86 -0
  197. package/src/vendor/fca-unofficial/src/createPoll.js +71 -0
  198. package/src/vendor/fca-unofficial/src/deleteMessage.js +56 -0
  199. package/src/vendor/fca-unofficial/src/deleteThread.js +56 -0
  200. package/src/vendor/fca-unofficial/src/forwardAttachment.js +60 -0
  201. package/src/vendor/fca-unofficial/src/getCurrentUserID.js +7 -0
  202. package/src/vendor/fca-unofficial/src/getEmojiUrl.js +29 -0
  203. package/src/vendor/fca-unofficial/src/getFriendsList.js +84 -0
  204. package/src/vendor/fca-unofficial/src/getThreadHistory.js +645 -0
  205. package/src/vendor/fca-unofficial/src/getThreadHistoryDeprecated.js +93 -0
  206. package/src/vendor/fca-unofficial/src/getThreadInfo.js +201 -0
  207. package/src/vendor/fca-unofficial/src/getThreadInfoDeprecated.js +80 -0
  208. package/src/vendor/fca-unofficial/src/getThreadList.js +238 -0
  209. package/src/vendor/fca-unofficial/src/getThreadListDeprecated.js +75 -0
  210. package/src/vendor/fca-unofficial/src/getThreadPictures.js +79 -0
  211. package/src/vendor/fca-unofficial/src/getUserID.js +66 -0
  212. package/src/vendor/fca-unofficial/src/getUserInfo.js +72 -0
  213. package/src/vendor/fca-unofficial/src/handleFriendRequest.js +61 -0
  214. package/src/vendor/fca-unofficial/src/handleMessageRequest.js +65 -0
  215. package/src/vendor/fca-unofficial/src/httpGet.js +52 -0
  216. package/src/vendor/fca-unofficial/src/httpPost.js +52 -0
  217. package/src/vendor/fca-unofficial/src/listenMqtt.js +788 -0
  218. package/src/vendor/fca-unofficial/src/logout.js +75 -0
  219. package/src/vendor/fca-unofficial/src/markAsDelivered.js +58 -0
  220. package/src/vendor/fca-unofficial/src/markAsRead.js +80 -0
  221. package/src/vendor/fca-unofficial/src/markAsReadAll.js +50 -0
  222. package/src/vendor/fca-unofficial/src/markAsSeen.js +59 -0
  223. package/src/vendor/fca-unofficial/src/muteThread.js +52 -0
  224. package/src/vendor/fca-unofficial/src/removeUserFromGroup.js +79 -0
  225. package/src/vendor/fca-unofficial/src/resolvePhotoUrl.js +45 -0
  226. package/src/vendor/fca-unofficial/src/searchForThread.js +53 -0
  227. package/src/vendor/fca-unofficial/src/sendMessage.js +459 -0
  228. package/src/vendor/fca-unofficial/src/sendTypingIndicator.js +103 -0
  229. package/src/vendor/fca-unofficial/src/setMessageReaction.js +117 -0
  230. package/src/vendor/fca-unofficial/src/setPostReaction.js +76 -0
  231. package/src/vendor/fca-unofficial/src/setTitle.js +86 -0
  232. package/src/vendor/fca-unofficial/src/threadColors.js +57 -0
  233. package/src/vendor/fca-unofficial/src/unfriend.js +52 -0
  234. package/src/vendor/fca-unofficial/src/unsendMessage.js +49 -0
  235. package/src/vendor/fca-unofficial/test/data/shareAttach.js +146 -0
  236. package/src/vendor/fca-unofficial/test/data/something.mov +0 -0
  237. package/src/vendor/fca-unofficial/test/data/test.png +0 -0
  238. package/src/vendor/fca-unofficial/test/data/test.txt +7 -0
  239. package/src/vendor/fca-unofficial/test/example-config.json +18 -0
  240. package/src/vendor/fca-unofficial/test/test-page.js +140 -0
  241. package/src/vendor/fca-unofficial/test/test.js +385 -0
  242. package/src/vendor/fca-unofficial/utils.js +1358 -0
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * NEXCA E2EE Crypto — All primitives using Node.js built-in crypto only.
5
+ * Implements: X25519 DH, HKDF-SHA256, AES-256-GCM, HMAC-SHA256
6
+ * Node.js >= 15 required (crypto.hkdfSync, crypto.generateKeyPairSync x25519).
7
+ */
8
+
9
+ const crypto = require("crypto");
10
+
11
+ // ─────────────────────────────────────────────────────────
12
+ // X25519 Key Management
13
+ // ─────────────────────────────────────────────────────────
14
+
15
+ function generateKeyPair() {
16
+ const { privateKey, publicKey } = crypto.generateKeyPairSync("x25519", {
17
+ privateKeyEncoding: { type: "pkcs8", format: "der" },
18
+ publicKeyEncoding: { type: "spki", format: "der" }
19
+ });
20
+ return {
21
+ privateKey,
22
+ publicKey,
23
+ publicKeyRaw: exportRawPublicKey(publicKey)
24
+ };
25
+ }
26
+
27
+ function exportRawPublicKey(spkiDer) {
28
+ // X25519 SPKI DER: 12 bytes header + 32 bytes key
29
+ return Buffer.from(spkiDer).slice(-32);
30
+ }
31
+
32
+ function importPublicKey(raw32bytes) {
33
+ if (raw32bytes.length !== 32) throw new Error("X25519 public key must be 32 bytes");
34
+ // Build minimal SPKI wrapper for X25519
35
+ const header = Buffer.from("302a300506032b656e032100", "hex");
36
+ return Buffer.concat([header, raw32bytes]);
37
+ }
38
+
39
+ function dh(privateKeyPkcs8Der, peerPublicRaw32) {
40
+ const privKey = crypto.createPrivateKey({ key: Buffer.from(privateKeyPkcs8Der), format: "der", type: "pkcs8" });
41
+ const pubKeyDer = importPublicKey(Buffer.from(peerPublicRaw32));
42
+ const pubKey = crypto.createPublicKey({ key: pubKeyDer, format: "der", type: "spki" });
43
+ return crypto.diffieHellman({ privateKey: privKey, publicKey: pubKey });
44
+ }
45
+
46
+ // ─────────────────────────────────────────────────────────
47
+ // HKDF-SHA256
48
+ // ─────────────────────────────────────────────────────────
49
+
50
+ function hkdf(ikm, length, salt, info) {
51
+ if (!salt) salt = Buffer.alloc(32, 0);
52
+ if (!info) info = Buffer.alloc(0);
53
+ if (typeof info === "string") info = Buffer.from(info, "utf8");
54
+ return Buffer.from(crypto.hkdfSync("sha256", ikm, salt, info, length));
55
+ }
56
+
57
+ function hkdfExtract(salt, ikm) {
58
+ return crypto.createHmac("sha256", salt).update(ikm).digest();
59
+ }
60
+
61
+ function hkdfExpand(prk, info, length) {
62
+ const results = [];
63
+ let t = Buffer.alloc(0);
64
+ let i = 0;
65
+ while (results.reduce((s, b) => s + b.length, 0) < length) {
66
+ i++;
67
+ const hmac = crypto.createHmac("sha256", prk);
68
+ hmac.update(t);
69
+ if (info) hmac.update(info);
70
+ hmac.update(Buffer.from([i]));
71
+ t = hmac.digest();
72
+ results.push(t);
73
+ }
74
+ return Buffer.concat(results).slice(0, length);
75
+ }
76
+
77
+ // ─────────────────────────────────────────────────────────
78
+ // AES-256-GCM
79
+ // ─────────────────────────────────────────────────────────
80
+
81
+ function encrypt(key32, plaintext, aad) {
82
+ if (key32.length !== 32) throw new Error("AES-GCM key must be 32 bytes");
83
+ const iv = crypto.randomBytes(12);
84
+ const cipher = crypto.createCipheriv("aes-256-gcm", key32, iv);
85
+ if (aad) cipher.setAAD(aad);
86
+ const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
87
+ const authTag = cipher.getAuthTag();
88
+ return Buffer.concat([iv, authTag, encrypted]);
89
+ }
90
+
91
+ function decrypt(key32, ciphertext, aad) {
92
+ if (key32.length !== 32) throw new Error("AES-GCM key must be 32 bytes");
93
+ if (ciphertext.length < 28) throw new Error("Ciphertext too short");
94
+ const iv = ciphertext.slice(0, 12);
95
+ const authTag = ciphertext.slice(12, 28);
96
+ const data = ciphertext.slice(28);
97
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key32, iv);
98
+ decipher.setAuthTag(authTag);
99
+ if (aad) decipher.setAAD(aad);
100
+ return Buffer.concat([decipher.update(data), decipher.final()]);
101
+ }
102
+
103
+ // ─────────────────────────────────────────────────────────
104
+ // HMAC-SHA256
105
+ // ─────────────────────────────────────────────────────────
106
+
107
+ function hmacSha256(key, data) {
108
+ return crypto.createHmac("sha256", key).update(data).digest();
109
+ }
110
+
111
+ function constantTimeEqual(a, b) {
112
+ if (a.length !== b.length) return false;
113
+ return crypto.timingSafeEqual(a, b);
114
+ }
115
+
116
+ // ─────────────────────────────────────────────────────────
117
+ // X3DH Key Agreement (Extended Triple Diffie-Hellman)
118
+ // ─────────────────────────────────────────────────────────
119
+
120
+ /**
121
+ * Sender side of X3DH.
122
+ * @param {object} senderIdKey - { privateKey, publicKey, publicKeyRaw } of sender identity key
123
+ * @param {object} ephemeralKey - { privateKey, publicKey, publicKeyRaw } of ephemeral key
124
+ * @param {Buffer} recipientIdPub - recipient's identity public key (raw 32 bytes)
125
+ * @param {Buffer} recipientSpkPub - recipient's signed pre-key (raw 32 bytes)
126
+ * @param {Buffer} [recipientOpkPub] - recipient's one-time pre-key (raw 32 bytes), optional
127
+ * @returns {Buffer} 32-byte shared master secret
128
+ */
129
+ function x3dhSend(senderIdKey, ephemeralKey, recipientIdPub, recipientSpkPub, recipientOpkPub) {
130
+ const dh1 = dh(senderIdKey.privateKey, recipientSpkPub);
131
+ const dh2 = dh(ephemeralKey.privateKey, recipientIdPub);
132
+ const dh3 = dh(ephemeralKey.privateKey, recipientSpkPub);
133
+ const parts = recipientOpkPub
134
+ ? [dh1, dh2, dh3, dh(ephemeralKey.privateKey, recipientOpkPub)]
135
+ : [dh1, dh2, dh3];
136
+ const ikm = Buffer.concat(parts);
137
+ const F = Buffer.alloc(32, 0xFF);
138
+ const salt = Buffer.alloc(32, 0);
139
+ return hkdf(Buffer.concat([F, ikm]), 32, salt, Buffer.from("WhisperText", "utf8"));
140
+ }
141
+
142
+ /**
143
+ * Receiver side of X3DH.
144
+ */
145
+ function x3dhReceive(recipientIdKey, recipientSpkKey, recipientOpkKey, senderIdPub, senderEphemeralPub) {
146
+ const dh1 = dh(recipientSpkKey.privateKey, senderIdPub);
147
+ const dh2 = dh(recipientIdKey.privateKey, senderEphemeralPub);
148
+ const dh3 = dh(recipientSpkKey.privateKey, senderEphemeralPub);
149
+ const parts = recipientOpkKey
150
+ ? [dh1, dh2, dh3, dh(recipientOpkKey.privateKey, senderEphemeralPub)]
151
+ : [dh1, dh2, dh3];
152
+ const ikm = Buffer.concat(parts);
153
+ const F = Buffer.alloc(32, 0xFF);
154
+ const salt = Buffer.alloc(32, 0);
155
+ return hkdf(Buffer.concat([F, ikm]), 32, salt, Buffer.from("WhisperText", "utf8"));
156
+ }
157
+
158
+ module.exports = {
159
+ generateKeyPair,
160
+ exportRawPublicKey,
161
+ importPublicKey,
162
+ dh,
163
+ hkdf,
164
+ hkdfExtract,
165
+ hkdfExpand,
166
+ encrypt,
167
+ decrypt,
168
+ hmacSha256,
169
+ constantTimeEqual,
170
+ x3dhSend,
171
+ x3dhReceive,
172
+ randomBytes: crypto.randomBytes.bind(crypto)
173
+ };
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * NEXCA E2EE Crypto — All primitives using Node.js built-in crypto only.
5
+ * Implements: X25519 DH, HKDF-SHA256, AES-256-GCM, HMAC-SHA256
6
+ * Node.js >= 15 required (crypto.hkdfSync, crypto.generateKeyPairSync x25519).
7
+ */
8
+
9
+ const crypto = require("crypto");
10
+
11
+ // ─────────────────────────────────────────────────────────
12
+ // X25519 Key Management
13
+ // ─────────────────────────────────────────────────────────
14
+
15
+ function generateKeyPair() {
16
+ const { privateKey, publicKey } = crypto.generateKeyPairSync("x25519", {
17
+ privateKeyEncoding: { type: "pkcs8", format: "der" },
18
+ publicKeyEncoding: { type: "spki", format: "der" }
19
+ });
20
+ return {
21
+ privateKey,
22
+ publicKey,
23
+ publicKeyRaw: exportRawPublicKey(publicKey)
24
+ };
25
+ }
26
+
27
+ function exportRawPublicKey(spkiDer) {
28
+ // X25519 SPKI DER: 12 bytes header + 32 bytes key
29
+ return Buffer.from(spkiDer).slice(-32);
30
+ }
31
+
32
+ function importPublicKey(raw32bytes) {
33
+ if (raw32bytes.length !== 32) throw new Error("X25519 public key must be 32 bytes");
34
+ // Build minimal SPKI wrapper for X25519
35
+ const header = Buffer.from("302a300506032b656e032100", "hex");
36
+ return Buffer.concat([header, raw32bytes]);
37
+ }
38
+
39
+ function dh(privateKeyPkcs8Der, peerPublicRaw32) {
40
+ const privKey = crypto.createPrivateKey({ key: Buffer.from(privateKeyPkcs8Der), format: "der", type: "pkcs8" });
41
+ const pubKeyDer = importPublicKey(Buffer.from(peerPublicRaw32));
42
+ const pubKey = crypto.createPublicKey({ key: pubKeyDer, format: "der", type: "spki" });
43
+ return crypto.diffieHellman({ privateKey: privKey, publicKey: pubKey });
44
+ }
45
+
46
+ // ─────────────────────────────────────────────────────────
47
+ // HKDF-SHA256
48
+ // ─────────────────────────────────────────────────────────
49
+
50
+ function hkdf(ikm, length, salt, info) {
51
+ if (!salt) salt = Buffer.alloc(32, 0);
52
+ if (!info) info = Buffer.alloc(0);
53
+ if (typeof info === "string") info = Buffer.from(info, "utf8");
54
+ return Buffer.from(crypto.hkdfSync("sha256", ikm, salt, info, length));
55
+ }
56
+
57
+ function hkdfExtract(salt, ikm) {
58
+ return crypto.createHmac("sha256", salt).update(ikm).digest();
59
+ }
60
+
61
+ function hkdfExpand(prk, info, length) {
62
+ const results = [];
63
+ let t = Buffer.alloc(0);
64
+ let i = 0;
65
+ while (results.reduce((s, b) => s + b.length, 0) < length) {
66
+ i++;
67
+ const hmac = crypto.createHmac("sha256", prk);
68
+ hmac.update(t);
69
+ if (info) hmac.update(info);
70
+ hmac.update(Buffer.from([i]));
71
+ t = hmac.digest();
72
+ results.push(t);
73
+ }
74
+ return Buffer.concat(results).slice(0, length);
75
+ }
76
+
77
+ // ─────────────────────────────────────────────────────────
78
+ // AES-256-GCM
79
+ // ─────────────────────────────────────────────────────────
80
+
81
+ function encrypt(key32, plaintext, aad) {
82
+ if (key32.length !== 32) throw new Error("AES-GCM key must be 32 bytes");
83
+ const iv = crypto.randomBytes(12);
84
+ const cipher = crypto.createCipheriv("aes-256-gcm", key32, iv);
85
+ if (aad) cipher.setAAD(aad);
86
+ const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
87
+ const authTag = cipher.getAuthTag();
88
+ return Buffer.concat([iv, authTag, encrypted]);
89
+ }
90
+
91
+ function decrypt(key32, ciphertext, aad) {
92
+ if (key32.length !== 32) throw new Error("AES-GCM key must be 32 bytes");
93
+ if (ciphertext.length < 28) throw new Error("Ciphertext too short");
94
+ const iv = ciphertext.slice(0, 12);
95
+ const authTag = ciphertext.slice(12, 28);
96
+ const data = ciphertext.slice(28);
97
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key32, iv);
98
+ decipher.setAuthTag(authTag);
99
+ if (aad) decipher.setAAD(aad);
100
+ return Buffer.concat([decipher.update(data), decipher.final()]);
101
+ }
102
+
103
+ // ─────────────────────────────────────────────────────────
104
+ // HMAC-SHA256
105
+ // ─────────────────────────────────────────────────────────
106
+
107
+ function hmacSha256(key, data) {
108
+ return crypto.createHmac("sha256", key).update(data).digest();
109
+ }
110
+
111
+ function constantTimeEqual(a, b) {
112
+ if (a.length !== b.length) return false;
113
+ return crypto.timingSafeEqual(a, b);
114
+ }
115
+
116
+ // ─────────────────────────────────────────────────────────
117
+ // X3DH Key Agreement (Extended Triple Diffie-Hellman)
118
+ // ─────────────────────────────────────────────────────────
119
+
120
+ /**
121
+ * Sender side of X3DH.
122
+ * @param {object} senderIdKey - { privateKey, publicKey, publicKeyRaw } of sender identity key
123
+ * @param {object} ephemeralKey - { privateKey, publicKey, publicKeyRaw } of ephemeral key
124
+ * @param {Buffer} recipientIdPub - recipient's identity public key (raw 32 bytes)
125
+ * @param {Buffer} recipientSpkPub - recipient's signed pre-key (raw 32 bytes)
126
+ * @param {Buffer} [recipientOpkPub] - recipient's one-time pre-key (raw 32 bytes), optional
127
+ * @returns {Buffer} 32-byte shared master secret
128
+ */
129
+ function x3dhSend(senderIdKey, ephemeralKey, recipientIdPub, recipientSpkPub, recipientOpkPub) {
130
+ const dh1 = dh(senderIdKey.privateKey, recipientSpkPub);
131
+ const dh2 = dh(ephemeralKey.privateKey, recipientIdPub);
132
+ const dh3 = dh(ephemeralKey.privateKey, recipientSpkPub);
133
+ const parts = recipientOpkPub
134
+ ? [dh1, dh2, dh3, dh(ephemeralKey.privateKey, recipientOpkPub)]
135
+ : [dh1, dh2, dh3];
136
+ const ikm = Buffer.concat(parts);
137
+ const F = Buffer.alloc(32, 0xFF);
138
+ const salt = Buffer.alloc(32, 0);
139
+ return hkdf(Buffer.concat([F, ikm]), 32, salt, Buffer.from("WhisperText", "utf8"));
140
+ }
141
+
142
+ /**
143
+ * Receiver side of X3DH.
144
+ */
145
+ function x3dhReceive(recipientIdKey, recipientSpkKey, recipientOpkKey, senderIdPub, senderEphemeralPub) {
146
+ const dh1 = dh(recipientSpkKey.privateKey, senderIdPub);
147
+ const dh2 = dh(recipientIdKey.privateKey, senderEphemeralPub);
148
+ const dh3 = dh(recipientSpkKey.privateKey, senderEphemeralPub);
149
+ const parts = recipientOpkKey
150
+ ? [dh1, dh2, dh3, dh(recipientOpkKey.privateKey, senderEphemeralPub)]
151
+ : [dh1, dh2, dh3];
152
+ const ikm = Buffer.concat(parts);
153
+ const F = Buffer.alloc(32, 0xFF);
154
+ const salt = Buffer.alloc(32, 0);
155
+ return hkdf(Buffer.concat([F, ikm]), 32, salt, Buffer.from("WhisperText", "utf8"));
156
+ }
157
+
158
+ module.exports = {
159
+ generateKeyPair,
160
+ exportRawPublicKey,
161
+ importPublicKey,
162
+ dh,
163
+ hkdf,
164
+ hkdfExtract,
165
+ hkdfExpand,
166
+ encrypt,
167
+ decrypt,
168
+ hmacSha256,
169
+ constantTimeEqual,
170
+ x3dhSend,
171
+ x3dhReceive,
172
+ randomBytes: crypto.randomBytes.bind(crypto)
173
+ };