shogun-core 5.2.0 → 5.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +145 -1143
  2. package/dist/browser/defaultVendors-node_modules_hpke_chacha20poly1305_esm_mod_js.shogun-core.js +1220 -0
  3. package/dist/browser/defaultVendors-node_modules_hpke_chacha20poly1305_esm_mod_js.shogun-core.js.map +1 -0
  4. package/dist/browser/defaultVendors-node_modules_hpke_hybridkem-x-wing_esm_mod_js.shogun-core.js +844 -0
  5. package/dist/browser/defaultVendors-node_modules_hpke_hybridkem-x-wing_esm_mod_js.shogun-core.js.map +1 -0
  6. package/dist/browser/defaultVendors-node_modules_mlkem_esm_mod_js.shogun-core.js +2335 -0
  7. package/dist/browser/defaultVendors-node_modules_mlkem_esm_mod_js.shogun-core.js.map +1 -0
  8. package/dist/browser/defaultVendors-node_modules_noble_ciphers_chacha_js.shogun-core.js +999 -0
  9. package/dist/browser/defaultVendors-node_modules_noble_ciphers_chacha_js.shogun-core.js.map +1 -0
  10. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_curve_js-node_modules_noble_curves_esm_-1ce4ed.shogun-core.js +1651 -0
  11. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_curve_js-node_modules_noble_curves_esm_-1ce4ed.shogun-core.js.map +1 -0
  12. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_edwards_js-node_modules_noble_curves_es-a82056.shogun-core.js +825 -0
  13. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_edwards_js-node_modules_noble_curves_es-a82056.shogun-core.js.map +1 -0
  14. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed25519_js.shogun-core.js +508 -0
  15. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed25519_js.shogun-core.js.map +1 -0
  16. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed448_js.shogun-core.js +747 -0
  17. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed448_js.shogun-core.js.map +1 -0
  18. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_nist_js.shogun-core.js +1608 -0
  19. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_nist_js.shogun-core.js.map +1 -0
  20. package/dist/browser/defaultVendors-node_modules_noble_post-quantum_ml-dsa_js.shogun-core.js +2117 -0
  21. package/dist/browser/defaultVendors-node_modules_noble_post-quantum_ml-dsa_js.shogun-core.js.map +1 -0
  22. package/dist/browser/defaultVendors-node_modules_openpgp_dist_openpgp_min_mjs.shogun-core.js +86 -0
  23. package/dist/browser/defaultVendors-node_modules_openpgp_dist_openpgp_min_mjs.shogun-core.js.map +1 -0
  24. package/dist/browser/node_modules_hpke_ml-kem_esm_mod_js.shogun-core.js +539 -0
  25. package/dist/browser/node_modules_hpke_ml-kem_esm_mod_js.shogun-core.js.map +1 -0
  26. package/dist/browser/shogun-core.js +160386 -0
  27. package/dist/browser/shogun-core.js.map +1 -0
  28. package/dist/config/simplified-config.js +236 -0
  29. package/dist/core.js +329 -0
  30. package/dist/crypto/asymmetric.js +99 -0
  31. package/dist/crypto/double-ratchet.js +370 -0
  32. package/dist/crypto/file-encryption.js +213 -0
  33. package/dist/crypto/hashing.js +87 -0
  34. package/dist/crypto/index.js +34 -0
  35. package/dist/crypto/mls-codec.js +202 -0
  36. package/dist/crypto/mls.js +550 -0
  37. package/dist/crypto/pgp.js +390 -0
  38. package/dist/crypto/random-generation.js +341 -0
  39. package/dist/crypto/sframe.js +350 -0
  40. package/dist/crypto/signal-protocol.js +376 -0
  41. package/dist/crypto/symmetric.js +91 -0
  42. package/dist/crypto/types.js +2 -0
  43. package/dist/crypto/utils.js +140 -0
  44. package/dist/examples/auth-test.js +253 -0
  45. package/dist/examples/crypto-identity-example.js +151 -0
  46. package/dist/examples/crypto-working-test.js +83 -0
  47. package/dist/examples/double-ratchet-test.js +155 -0
  48. package/dist/examples/mls-advanced-example.js +294 -0
  49. package/dist/examples/mls-sframe-test.js +304 -0
  50. package/dist/examples/pgp-example.js +200 -0
  51. package/dist/examples/quick-auth-test.js +61 -0
  52. package/dist/examples/random-generation-test.js +151 -0
  53. package/dist/examples/signal-protocol-test.js +38 -0
  54. package/dist/examples/simple-api-test.js +114 -0
  55. package/dist/examples/simple-crypto-identity-example.js +84 -0
  56. package/dist/examples/timeout-test.js +227 -0
  57. package/dist/examples/zkproof-credentials-example.js +212 -0
  58. package/dist/examples/zkproof-example.js +201 -0
  59. package/dist/gundb/api.js +435 -0
  60. package/dist/gundb/crypto.js +283 -0
  61. package/dist/gundb/db.js +1946 -0
  62. package/dist/gundb/derive.js +232 -0
  63. package/dist/gundb/errors.js +76 -0
  64. package/dist/gundb/index.js +22 -0
  65. package/dist/gundb/rxjs.js +447 -0
  66. package/dist/gundb/types.js +5 -0
  67. package/dist/index.js +58 -0
  68. package/dist/interfaces/common.js +2 -0
  69. package/dist/interfaces/events.js +40 -0
  70. package/dist/interfaces/plugin.js +2 -0
  71. package/dist/interfaces/shogun.js +37 -0
  72. package/dist/managers/AuthManager.js +226 -0
  73. package/dist/managers/CoreInitializer.js +228 -0
  74. package/dist/managers/CryptoIdentityManager.js +366 -0
  75. package/dist/managers/EventManager.js +70 -0
  76. package/dist/managers/PluginManager.js +299 -0
  77. package/dist/plugins/base.js +50 -0
  78. package/dist/plugins/index.js +32 -0
  79. package/dist/plugins/nostr/index.js +20 -0
  80. package/dist/plugins/nostr/nostrConnector.js +419 -0
  81. package/dist/plugins/nostr/nostrConnectorPlugin.js +453 -0
  82. package/dist/plugins/nostr/nostrSigner.js +319 -0
  83. package/dist/plugins/nostr/types.js +2 -0
  84. package/dist/plugins/smartwallet/index.js +18 -0
  85. package/dist/plugins/smartwallet/smartWalletPlugin.js +511 -0
  86. package/dist/plugins/smartwallet/types.js +2 -0
  87. package/dist/plugins/web3/index.js +20 -0
  88. package/dist/plugins/web3/types.js +2 -0
  89. package/dist/plugins/web3/web3Connector.js +533 -0
  90. package/dist/plugins/web3/web3ConnectorPlugin.js +455 -0
  91. package/dist/plugins/web3/web3Signer.js +314 -0
  92. package/dist/plugins/webauthn/index.js +19 -0
  93. package/dist/plugins/webauthn/types.js +14 -0
  94. package/dist/plugins/webauthn/webauthn.js +496 -0
  95. package/dist/plugins/webauthn/webauthnPlugin.js +489 -0
  96. package/dist/plugins/webauthn/webauthnSigner.js +310 -0
  97. package/dist/plugins/zkproof/index.js +53 -0
  98. package/dist/plugins/zkproof/types.js +2 -0
  99. package/dist/plugins/zkproof/zkCredentials.js +213 -0
  100. package/dist/plugins/zkproof/zkProofConnector.js +198 -0
  101. package/dist/plugins/zkproof/zkProofPlugin.js +272 -0
  102. package/dist/storage/storage.js +145 -0
  103. package/dist/types/config/simplified-config.d.ts +114 -0
  104. package/dist/types/core.d.ts +305 -0
  105. package/dist/types/crypto/asymmetric.d.ts +6 -0
  106. package/dist/types/crypto/double-ratchet.d.ts +22 -0
  107. package/dist/types/crypto/file-encryption.d.ts +19 -0
  108. package/dist/types/crypto/hashing.d.ts +9 -0
  109. package/dist/types/crypto/index.d.ts +13 -0
  110. package/dist/types/crypto/mls-codec.d.ts +39 -0
  111. package/dist/types/crypto/mls.d.ts +130 -0
  112. package/dist/types/crypto/pgp.d.ts +95 -0
  113. package/dist/types/crypto/random-generation.d.ts +35 -0
  114. package/dist/types/crypto/sframe.d.ts +102 -0
  115. package/dist/types/crypto/signal-protocol.d.ts +26 -0
  116. package/dist/types/crypto/symmetric.d.ts +9 -0
  117. package/dist/types/crypto/types.d.ts +144 -0
  118. package/dist/types/crypto/utils.d.ts +22 -0
  119. package/dist/types/examples/auth-test.d.ts +8 -0
  120. package/dist/types/examples/crypto-identity-example.d.ts +5 -0
  121. package/dist/types/examples/crypto-working-test.d.ts +1 -0
  122. package/dist/types/examples/double-ratchet-test.d.ts +1 -0
  123. package/dist/types/examples/mls-advanced-example.d.ts +53 -0
  124. package/dist/types/examples/mls-sframe-test.d.ts +1 -0
  125. package/dist/types/examples/pgp-example.d.ts +75 -0
  126. package/dist/types/examples/quick-auth-test.d.ts +8 -0
  127. package/dist/types/examples/random-generation-test.d.ts +1 -0
  128. package/dist/types/examples/signal-protocol-test.d.ts +1 -0
  129. package/dist/types/examples/simple-api-test.d.ts +10 -0
  130. package/dist/types/examples/simple-crypto-identity-example.d.ts +6 -0
  131. package/dist/types/examples/timeout-test.d.ts +8 -0
  132. package/dist/types/examples/zkproof-credentials-example.d.ts +12 -0
  133. package/dist/types/examples/zkproof-example.d.ts +11 -0
  134. package/dist/types/gundb/api.d.ts +185 -0
  135. package/dist/types/gundb/crypto.d.ts +95 -0
  136. package/dist/types/gundb/db.d.ts +397 -0
  137. package/dist/types/gundb/derive.d.ts +21 -0
  138. package/dist/types/gundb/errors.d.ts +42 -0
  139. package/dist/types/gundb/index.d.ts +3 -0
  140. package/dist/types/gundb/rxjs.d.ts +110 -0
  141. package/dist/types/gundb/types.d.ts +255 -0
  142. package/dist/types/index.d.ts +16 -0
  143. package/dist/types/interfaces/common.d.ts +85 -0
  144. package/dist/types/interfaces/events.d.ts +131 -0
  145. package/dist/types/interfaces/plugin.d.ts +162 -0
  146. package/dist/types/interfaces/shogun.d.ts +208 -0
  147. package/dist/types/managers/AuthManager.d.ts +72 -0
  148. package/dist/types/managers/CoreInitializer.d.ts +40 -0
  149. package/dist/types/managers/CryptoIdentityManager.d.ts +102 -0
  150. package/dist/types/managers/EventManager.d.ts +49 -0
  151. package/dist/types/managers/PluginManager.d.ts +145 -0
  152. package/dist/types/plugins/base.d.ts +35 -0
  153. package/dist/types/plugins/index.d.ts +18 -0
  154. package/dist/types/plugins/nostr/index.d.ts +4 -0
  155. package/dist/types/plugins/nostr/nostrConnector.d.ts +119 -0
  156. package/dist/types/plugins/nostr/nostrConnectorPlugin.d.ts +163 -0
  157. package/dist/types/plugins/nostr/nostrSigner.d.ts +105 -0
  158. package/dist/types/plugins/nostr/types.d.ts +122 -0
  159. package/dist/types/plugins/smartwallet/index.d.ts +2 -0
  160. package/dist/types/plugins/smartwallet/smartWalletPlugin.d.ts +67 -0
  161. package/dist/types/plugins/smartwallet/types.d.ts +80 -0
  162. package/dist/types/plugins/web3/index.d.ts +4 -0
  163. package/dist/types/plugins/web3/types.d.ts +107 -0
  164. package/dist/types/plugins/web3/web3Connector.d.ts +129 -0
  165. package/dist/types/plugins/web3/web3ConnectorPlugin.d.ts +160 -0
  166. package/dist/types/plugins/web3/web3Signer.d.ts +114 -0
  167. package/dist/types/plugins/webauthn/index.d.ts +3 -0
  168. package/dist/types/plugins/webauthn/types.d.ts +183 -0
  169. package/dist/types/plugins/webauthn/webauthn.d.ts +129 -0
  170. package/dist/types/plugins/webauthn/webauthnPlugin.d.ts +179 -0
  171. package/dist/types/plugins/webauthn/webauthnSigner.d.ts +91 -0
  172. package/dist/types/plugins/zkproof/index.d.ts +48 -0
  173. package/dist/types/plugins/zkproof/types.d.ts +123 -0
  174. package/dist/types/plugins/zkproof/zkCredentials.d.ts +112 -0
  175. package/dist/types/plugins/zkproof/zkProofConnector.d.ts +46 -0
  176. package/dist/types/plugins/zkproof/zkProofPlugin.d.ts +76 -0
  177. package/dist/types/storage/storage.d.ts +51 -0
  178. package/dist/types/utils/errorHandler.d.ts +119 -0
  179. package/dist/types/utils/eventEmitter.d.ts +39 -0
  180. package/dist/types/utils/seedPhrase.d.ts +50 -0
  181. package/dist/types/utils/validation.d.ts +27 -0
  182. package/dist/utils/errorHandler.js +246 -0
  183. package/dist/utils/eventEmitter.js +79 -0
  184. package/dist/utils/seedPhrase.js +97 -0
  185. package/dist/utils/validation.js +81 -0
  186. package/package.json +10 -57
@@ -0,0 +1,370 @@
1
+ "use strict";
2
+ // Double Ratchet Protocol Implementation for shogun-core
3
+ // Based on the Signal Protocol specification for ongoing secure messaging
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.demonstrateDoubleRatchet = exports.cleanupSkippedMessageKeys = exports.serializeDoubleRatchetState = exports.doubleRatchetDecrypt = exports.doubleRatchetEncrypt = exports.initializeDoubleRatchet = void 0;
6
+ const signal_protocol_1 = require("./signal-protocol");
7
+ // Double Ratchet Protocol Constants
8
+ const DOUBLE_RATCHET_INFO_MESSAGE_KEY = new TextEncoder().encode("DoubleRatchet_MessageKey");
9
+ const DOUBLE_RATCHET_INFO_CHAIN_KEY = new TextEncoder().encode("DoubleRatchet_ChainKey");
10
+ const DOUBLE_RATCHET_INFO_ROOT_KEY = new TextEncoder().encode("DoubleRatchet_RootKey");
11
+ const DOUBLE_RATCHET_CHAIN_KEY_CONSTANT = new Uint8Array(1).fill(0x02);
12
+ const DOUBLE_RATCHET_MESSAGE_KEY_CONSTANT = new Uint8Array(1).fill(0x01);
13
+ const MAX_SKIPPED_MESSAGE_KEYS = 1000;
14
+ // HKDF implementation for Double Ratchet
15
+ const doubleRatchetHKDF = async (salt, inputKeyMaterial, info, length = 32) => {
16
+ // Extract phase
17
+ const saltKey = await crypto.subtle.importKey("raw", salt.length > 0 ? salt.buffer : new ArrayBuffer(32), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
18
+ const prk = await crypto.subtle.sign("HMAC", saltKey, inputKeyMaterial);
19
+ // Expand phase
20
+ const prkKey = await crypto.subtle.importKey("raw", prk, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
21
+ const okm = new Uint8Array(length);
22
+ let t = new Uint8Array(0);
23
+ let counter = 1;
24
+ let pos = 0;
25
+ while (pos < length) {
26
+ const input = new Uint8Array(t.length + info.length + 1);
27
+ input.set(t);
28
+ input.set(info, t.length);
29
+ input[t.length + info.length] = counter;
30
+ t = new Uint8Array(await crypto.subtle.sign("HMAC", prkKey, input));
31
+ const remaining = length - pos;
32
+ const copyLength = Math.min(t.length, remaining);
33
+ okm.set(t.subarray(0, copyLength), pos);
34
+ pos += copyLength;
35
+ counter++;
36
+ }
37
+ return okm.buffer;
38
+ };
39
+ // HMAC-SHA256 for chain key updates
40
+ const doubleRatchetHMAC = async (key, data) => {
41
+ const hmacKey = await crypto.subtle.importKey("raw", key, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
42
+ return await crypto.subtle.sign("HMAC", hmacKey, data.buffer);
43
+ };
44
+ // Initialize Double Ratchet state from X3DH shared secret
45
+ const initializeDoubleRatchet = async (sharedSecret, isInitiator, remotePublicKey = null) => {
46
+ console.log(`🔄 Initializing Double Ratchet (${isInitiator ? "Initiator" : "Responder"})`);
47
+ // Derive initial root key from X3DH shared secret
48
+ const initialRootKey = await doubleRatchetHKDF(new Uint8Array(0), // Empty salt
49
+ sharedSecret, DOUBLE_RATCHET_INFO_ROOT_KEY, 32);
50
+ console.log("✓ Initial root key derived from X3DH secret");
51
+ const state = {
52
+ // Core ratchet state
53
+ rootKey: initialRootKey,
54
+ sendingChainKey: null,
55
+ receivingChainKey: null,
56
+ // DH key pairs
57
+ sendingDHKeyPair: null,
58
+ receivingDHPublicKey: remotePublicKey,
59
+ // Message counters
60
+ sendingMessageNumber: 0,
61
+ receivingMessageNumber: 0,
62
+ previousChainLength: 0,
63
+ // Skipped message keys storage
64
+ skippedMessageKeys: new Map(),
65
+ // State flags
66
+ isInitiator,
67
+ initialized: Date.now(),
68
+ };
69
+ if (isInitiator) {
70
+ // Initiator: Generate initial DH key pair and derive sending chain directly from root key
71
+ console.log("🔑 Generating initial DH key pair for initiator");
72
+ state.sendingDHKeyPair = await (0, signal_protocol_1.generateSignalKeyPair)();
73
+ // Derive initial sending chain directly from root key for first message
74
+ const hkdfOutput = await doubleRatchetHKDF(new Uint8Array(0), // Empty salt for direct derivation
75
+ initialRootKey, DOUBLE_RATCHET_INFO_CHAIN_KEY, 64);
76
+ state.rootKey = hkdfOutput.slice(0, 32);
77
+ state.sendingChainKey = hkdfOutput.slice(32, 64);
78
+ console.log("✓ Initial sending chain derived directly from root key");
79
+ }
80
+ else {
81
+ // Responder: Start with receiving mode, will derive receiving chain from root key on first message
82
+ console.log("📥 Responder initialized, waiting for first message");
83
+ }
84
+ console.log("✅ Double Ratchet state initialized successfully");
85
+ return state;
86
+ };
87
+ exports.initializeDoubleRatchet = initializeDoubleRatchet;
88
+ // Derive message key from chain key
89
+ const deriveMessageKey = async (chainKey) => {
90
+ const messageKey = await doubleRatchetHMAC(chainKey, DOUBLE_RATCHET_MESSAGE_KEY_CONSTANT);
91
+ return new Uint8Array(messageKey);
92
+ };
93
+ // Derive next chain key from current chain key
94
+ const deriveNextChainKey = async (chainKey) => {
95
+ const nextChainKey = await doubleRatchetHMAC(chainKey, DOUBLE_RATCHET_CHAIN_KEY_CONSTANT);
96
+ return nextChainKey;
97
+ };
98
+ // Perform DH ratchet step (when receiving new DH public key)
99
+ const performDHRatchetStep = async (state, newRemotePublicKey) => {
100
+ console.log("🔄 Performing DH ratchet step");
101
+ // Save current receiving chain info for skipped messages
102
+ state.previousChainLength = state.receivingMessageNumber;
103
+ state.receivingMessageNumber = 0;
104
+ state.receivingDHPublicKey = newRemotePublicKey;
105
+ // Generate a receiving DH key pair if we don't have one
106
+ if (!state.sendingDHKeyPair) {
107
+ state.sendingDHKeyPair = await (0, signal_protocol_1.generateSignalKeyPair)();
108
+ }
109
+ // Check if this is the responder's first message from the initiator
110
+ const isResponderFirstReceive = !state.isInitiator &&
111
+ !state.receivingChainKey &&
112
+ state.receivingMessageNumber === 0;
113
+ if (isResponderFirstReceive) {
114
+ console.log("🔄 First receive: matching initiator's direct root key derivation (responder only)");
115
+ // Alice derived: HKDF(empty_salt, rootKey, CHAIN_KEY_INFO) -> [newRootKey, sendingChain]
116
+ // Bob must derive the exact same way to get the matching receiving chain
117
+ const hkdfResult = await doubleRatchetHKDF(new Uint8Array(0), // Empty salt - same as initiator used
118
+ state.rootKey, // Same root key as initiator had
119
+ DOUBLE_RATCHET_INFO_CHAIN_KEY, 64);
120
+ // Update our root key to match initiator's updated root key
121
+ state.rootKey = hkdfResult.slice(0, 32);
122
+ // Set receiving chain to match initiator's sending chain
123
+ state.receivingChainKey = hkdfResult.slice(32, 64);
124
+ console.log("🔄 Receiving chain set to match initiator's sending chain");
125
+ // Generate our sending key pair for future messages
126
+ console.log("🔑 Generating DH key pair for responder's future sending");
127
+ state.sendingDHKeyPair = await (0, signal_protocol_1.generateSignalKeyPair)();
128
+ }
129
+ else if (state.sendingDHKeyPair) {
130
+ console.log("🔄 Deriving receiving chain from: DH(our_current_private, their_public)");
131
+ const receivingDHOutput = await (0, signal_protocol_1.performSignalDH)(state.sendingDHKeyPair.privateKey, newRemotePublicKey);
132
+ // Derive receiving chain key from the DH output
133
+ const hkdfReceiving = await doubleRatchetHKDF(new Uint8Array(state.rootKey), receivingDHOutput, DOUBLE_RATCHET_INFO_CHAIN_KEY, 64);
134
+ state.rootKey = hkdfReceiving.slice(0, 32);
135
+ state.receivingChainKey = hkdfReceiving.slice(32, 64);
136
+ console.log("🔄 DH ratchet step - receiving chain established");
137
+ }
138
+ // Step 2: Generate NEW DH key pair and derive sending chain
139
+ console.log("🔑 Generating NEW DH key pair for ratchet step");
140
+ state.sendingDHKeyPair = await (0, signal_protocol_1.generateSignalKeyPair)();
141
+ state.sendingMessageNumber = 0;
142
+ // Derive sending chain from our NEW DH key pair
143
+ const sendingDHOutput = await (0, signal_protocol_1.performSignalDH)(state.sendingDHKeyPair.privateKey, newRemotePublicKey);
144
+ const hkdfSending = await doubleRatchetHKDF(new Uint8Array(state.rootKey), sendingDHOutput, DOUBLE_RATCHET_INFO_CHAIN_KEY, 64);
145
+ state.rootKey = hkdfSending.slice(0, 32);
146
+ state.sendingChainKey = hkdfSending.slice(32, 64);
147
+ console.log("🔄 DH ratchet step - sending chain established");
148
+ console.log("✓ DH ratchet step completed");
149
+ };
150
+ // Skip message keys for out-of-order messages
151
+ const skipMessageKeys = async (state, until) => {
152
+ console.log(`⏭️ Skipping message keys from ${state.receivingMessageNumber} to ${until}`);
153
+ if (state.receivingChainKey && state.receivingMessageNumber < until) {
154
+ if (until - state.receivingMessageNumber > MAX_SKIPPED_MESSAGE_KEYS) {
155
+ throw new Error(`Too many skipped message keys: ${until - state.receivingMessageNumber}`);
156
+ }
157
+ const dhPublicKeyHex = state.receivingDHPublicKey
158
+ ? (0, signal_protocol_1.bufferToSignalHex)(await (0, signal_protocol_1.exportSignalPublicKey)(state.receivingDHPublicKey))
159
+ : "null";
160
+ let chainKey = state.receivingChainKey;
161
+ while (state.receivingMessageNumber < until) {
162
+ const messageKey = await deriveMessageKey(chainKey);
163
+ const keyId = `${dhPublicKeyHex}:${state.receivingMessageNumber}`;
164
+ state.skippedMessageKeys.set(keyId, messageKey);
165
+ chainKey = await deriveNextChainKey(chainKey);
166
+ state.receivingMessageNumber++;
167
+ console.log(`📝 Saved skipped message key for ${keyId}`);
168
+ }
169
+ state.receivingChainKey = chainKey;
170
+ }
171
+ };
172
+ // Encrypt message using Double Ratchet
173
+ const doubleRatchetEncrypt = async (state, plaintext) => {
174
+ console.log(`🔒 Encrypting message #${state.sendingMessageNumber} with Double Ratchet`);
175
+ if (!state.sendingChainKey) {
176
+ throw new Error("No sending chain key available - cannot encrypt");
177
+ }
178
+ // Derive message key
179
+ const messageKey = await deriveMessageKey(state.sendingChainKey);
180
+ // Get DH public key for logging
181
+ const dhPublicKeyBuffer = await (0, signal_protocol_1.exportSignalPublicKey)(state.sendingDHKeyPair.publicKey);
182
+ const dhPublicKeyBytes = new Uint8Array(dhPublicKeyBuffer);
183
+ console.log("🔑 Alice encryption - Chain and message keys");
184
+ // Prepare additional authenticated data (AAD)
185
+ const aad = new Uint8Array(dhPublicKeyBytes.length + 8); // DH key + 2 uint32s
186
+ aad.set(dhPublicKeyBytes);
187
+ // Add message number and previous chain length as AAD
188
+ const view = new DataView(aad.buffer, dhPublicKeyBytes.length);
189
+ view.setUint32(0, state.sendingMessageNumber, true);
190
+ view.setUint32(4, state.previousChainLength, true);
191
+ // Encrypt with AES-GCM
192
+ const plaintextBytes = new TextEncoder().encode(plaintext);
193
+ const iv = crypto.getRandomValues(new Uint8Array(12));
194
+ const cryptoKey = await crypto.subtle.importKey("raw", messageKey.buffer, { name: "AES-GCM" }, false, ["encrypt"]);
195
+ const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv, additionalData: aad }, cryptoKey, plaintextBytes);
196
+ // Update sending chain key
197
+ state.sendingChainKey = await deriveNextChainKey(state.sendingChainKey);
198
+ const messageEnvelope = {
199
+ dhPublicKey: dhPublicKeyBytes,
200
+ messageNumber: state.sendingMessageNumber,
201
+ previousChainLength: state.previousChainLength,
202
+ ciphertext: new Uint8Array(ciphertext),
203
+ iv: iv,
204
+ timestamp: Date.now(),
205
+ };
206
+ state.sendingMessageNumber++;
207
+ console.log(`✅ Message encrypted successfully (msg #${state.sendingMessageNumber - 1})`);
208
+ // Securely delete message key
209
+ messageKey.fill(0);
210
+ return messageEnvelope;
211
+ };
212
+ exports.doubleRatchetEncrypt = doubleRatchetEncrypt;
213
+ // Decrypt message using Double Ratchet
214
+ const doubleRatchetDecrypt = async (state, messageEnvelope) => {
215
+ console.log(`🔓 Decrypting message #${messageEnvelope.messageNumber} with Double Ratchet`);
216
+ const { dhPublicKey, messageNumber, previousChainLength, ciphertext, iv } = messageEnvelope;
217
+ const dhPublicKeyHex = (0, signal_protocol_1.bufferToSignalHex)(dhPublicKey.buffer);
218
+ // Check for skipped message key first
219
+ const skippedKeyId = `${dhPublicKeyHex}:${messageNumber}`;
220
+ let messageKey = state.skippedMessageKeys.get(skippedKeyId);
221
+ if (messageKey) {
222
+ console.log(`📋 Using skipped message key for message ${messageNumber}`);
223
+ state.skippedMessageKeys.delete(skippedKeyId);
224
+ }
225
+ else {
226
+ // Check if this is a new DH ratchet step
227
+ const currentDhKeyHex = state.receivingDHPublicKey
228
+ ? (0, signal_protocol_1.bufferToSignalHex)(await (0, signal_protocol_1.exportSignalPublicKey)(state.receivingDHPublicKey))
229
+ : null;
230
+ if (dhPublicKeyHex !== currentDhKeyHex) {
231
+ console.log("🔄 New DH public key detected, performing ratchet step");
232
+ // Skip message keys for current chain if needed
233
+ await skipMessageKeys(state, state.receivingMessageNumber);
234
+ // Perform DH ratchet step
235
+ const remotePublicKey = await (0, signal_protocol_1.importSignalPublicKey)(dhPublicKey.buffer);
236
+ await performDHRatchetStep(state, remotePublicKey);
237
+ }
238
+ // Skip message keys if needed
239
+ await skipMessageKeys(state, messageNumber);
240
+ // Derive message key
241
+ if (!state.receivingChainKey) {
242
+ throw new Error("No receiving chain key available - cannot decrypt");
243
+ }
244
+ // Store original chain key for logging
245
+ const originalChainKey = state.receivingChainKey;
246
+ messageKey = await deriveMessageKey(originalChainKey);
247
+ state.receivingChainKey = await deriveNextChainKey(originalChainKey);
248
+ const currentMessageNumber = state.receivingMessageNumber;
249
+ state.receivingMessageNumber++;
250
+ console.log("🔑 Bob decryption - Chain and message keys");
251
+ }
252
+ // Prepare AAD for verification
253
+ const aad = new Uint8Array(dhPublicKey.length + 8);
254
+ aad.set(dhPublicKey);
255
+ const view = new DataView(aad.buffer, dhPublicKey.length);
256
+ view.setUint32(0, messageNumber, true);
257
+ view.setUint32(4, previousChainLength, true);
258
+ // Decrypt with AES-GCM
259
+ const cryptoKey = await crypto.subtle.importKey("raw", messageKey.buffer, { name: "AES-GCM" }, false, ["decrypt"]);
260
+ try {
261
+ const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv: new Uint8Array(iv), additionalData: aad }, cryptoKey, new Uint8Array(ciphertext));
262
+ const plaintextString = new TextDecoder().decode(plaintext);
263
+ console.log(`✅ Message decrypted successfully: "${plaintextString}"`);
264
+ // Securely delete message key
265
+ messageKey.fill(0);
266
+ return plaintextString;
267
+ }
268
+ catch (error) {
269
+ console.error("❌ Message decryption failed:", error);
270
+ throw new Error("Message decryption failed - authentication failed");
271
+ }
272
+ };
273
+ exports.doubleRatchetDecrypt = doubleRatchetDecrypt;
274
+ // Serialize Double Ratchet state for storage
275
+ const serializeDoubleRatchetState = async (state) => {
276
+ const serialized = {
277
+ rootKey: (0, signal_protocol_1.bufferToSignalHex)(state.rootKey),
278
+ sendingChainKey: state.sendingChainKey
279
+ ? (0, signal_protocol_1.bufferToSignalHex)(state.sendingChainKey)
280
+ : null,
281
+ receivingChainKey: state.receivingChainKey
282
+ ? (0, signal_protocol_1.bufferToSignalHex)(state.receivingChainKey)
283
+ : null,
284
+ sendingDHPublicKey: state.sendingDHKeyPair
285
+ ? (0, signal_protocol_1.bufferToSignalHex)(await (0, signal_protocol_1.exportSignalPublicKey)(state.sendingDHKeyPair.publicKey))
286
+ : null,
287
+ receivingDHPublicKey: state.receivingDHPublicKey
288
+ ? (0, signal_protocol_1.bufferToSignalHex)(await (0, signal_protocol_1.exportSignalPublicKey)(state.receivingDHPublicKey))
289
+ : null,
290
+ sendingMessageNumber: state.sendingMessageNumber,
291
+ receivingMessageNumber: state.receivingMessageNumber,
292
+ previousChainLength: state.previousChainLength,
293
+ isInitiator: state.isInitiator,
294
+ initialized: state.initialized,
295
+ skippedMessageKeysCount: state.skippedMessageKeys.size,
296
+ };
297
+ return JSON.stringify(serialized);
298
+ };
299
+ exports.serializeDoubleRatchetState = serializeDoubleRatchetState;
300
+ // Clean up old skipped message keys to prevent memory bloat
301
+ const cleanupSkippedMessageKeys = (state, maxAge = 7 * 24 * 60 * 60 * 1000) => {
302
+ const now = Date.now();
303
+ const keysToDelete = [];
304
+ // In a real implementation, you'd track the age of each skipped key
305
+ // For now, just limit the total number
306
+ if (state.skippedMessageKeys.size > MAX_SKIPPED_MESSAGE_KEYS * 0.8) {
307
+ const keys = Array.from(state.skippedMessageKeys.keys());
308
+ const deleteCount = state.skippedMessageKeys.size - MAX_SKIPPED_MESSAGE_KEYS / 2;
309
+ for (let i = 0; i < deleteCount; i++) {
310
+ keysToDelete.push(keys[i]);
311
+ }
312
+ }
313
+ keysToDelete.forEach((key) => {
314
+ state.skippedMessageKeys.delete(key);
315
+ });
316
+ if (keysToDelete.length > 0) {
317
+ console.log(`🧹 Cleaned up ${keysToDelete.length} old skipped message keys`);
318
+ }
319
+ };
320
+ exports.cleanupSkippedMessageKeys = cleanupSkippedMessageKeys;
321
+ // Demonstrate Double Ratchet conversation
322
+ const demonstrateDoubleRatchet = async () => {
323
+ try {
324
+ console.log("🚀 Starting Double Ratchet demonstration...");
325
+ // Initialize X3DH for shared secret
326
+ const { initializeSignalUser, getSignalPublicKeyBundle, performSignalX3DHKeyExchange, } = await import("./signal-protocol.js");
327
+ const alice = await initializeSignalUser("Alice");
328
+ const bob = await initializeSignalUser("Bob");
329
+ const bobBundle = await getSignalPublicKeyBundle(bob);
330
+ const exchangeResult = await performSignalX3DHKeyExchange(alice, bobBundle);
331
+ // Initialize Double Ratchet states
332
+ // Alice starts as initiator, Bob as responder - both start fresh
333
+ const aliceState = await (0, exports.initializeDoubleRatchet)(exchangeResult.masterSecret, true);
334
+ const bobState = await (0, exports.initializeDoubleRatchet)(exchangeResult.masterSecret, false);
335
+ console.log("📊 Double Ratchet states initialized");
336
+ // Simulate conversation
337
+ const conversation = [];
338
+ // Alice sends first message
339
+ const msg1 = await (0, exports.doubleRatchetEncrypt)(aliceState, "Hello Bob! This is our first Double Ratchet message! 🔒");
340
+ conversation.push({ from: "Alice", envelope: msg1 });
341
+ const decrypted1 = await (0, exports.doubleRatchetDecrypt)(bobState, msg1);
342
+ console.log(`Bob decrypted: "${decrypted1}"`);
343
+ // Bob replies (he now has sending chain from DH ratchet)
344
+ const msg2 = await (0, exports.doubleRatchetEncrypt)(bobState, "Hi Alice! The Double Ratchet is working perfectly! 🎉");
345
+ conversation.push({ from: "Bob", envelope: msg2 });
346
+ const decrypted2 = await (0, exports.doubleRatchetDecrypt)(aliceState, msg2);
347
+ console.log(`Alice decrypted: "${decrypted2}"`);
348
+ console.log("✅ Double Ratchet basic exchange completed successfully");
349
+ const result = {
350
+ success: true,
351
+ aliceState: await (0, exports.serializeDoubleRatchetState)(aliceState),
352
+ bobState: await (0, exports.serializeDoubleRatchetState)(bobState),
353
+ conversation,
354
+ messagesExchanged: conversation.length,
355
+ demonstration: {
356
+ forwardSecrecy: true,
357
+ outOfOrderHandling: true,
358
+ dhRatcheting: true,
359
+ chainKeyUpdating: true,
360
+ },
361
+ };
362
+ console.log("✅ Double Ratchet demonstration completed successfully");
363
+ return result;
364
+ }
365
+ catch (error) {
366
+ console.error("❌ Double Ratchet demonstration failed:", error);
367
+ throw error;
368
+ }
369
+ };
370
+ exports.demonstrateDoubleRatchet = demonstrateDoubleRatchet;
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decryptUploadedFile = exports.parseEncryptedFilePackage = exports.createSecureFileDownload = exports.decryptBinaryFile = exports.encryptBinaryFile = exports.decryptTextFile = exports.encryptTextFile = exports.decryptFile = exports.encryptFile = void 0;
4
+ const symmetric_1 = require("./symmetric");
5
+ const hashing_1 = require("./hashing");
6
+ const encryptFile = async (fileContent, password, fileName = "") => {
7
+ try {
8
+ const { key, salt } = await (0, symmetric_1.deriveKeyFromPassword)(password);
9
+ // Generate random IV
10
+ const iv = crypto.getRandomValues(new Uint8Array(12));
11
+ // Convert file content to appropriate format
12
+ let dataToEncrypt;
13
+ if (typeof fileContent === "string") {
14
+ dataToEncrypt = new TextEncoder().encode(fileContent).buffer;
15
+ }
16
+ else if (fileContent instanceof ArrayBuffer) {
17
+ dataToEncrypt = fileContent;
18
+ }
19
+ else if (fileContent instanceof File) {
20
+ dataToEncrypt = await fileContent.arrayBuffer();
21
+ }
22
+ else {
23
+ throw new Error("Unsupported file content type");
24
+ }
25
+ // Encrypt the file content
26
+ const encryptedData = await crypto.subtle.encrypt({
27
+ name: "AES-GCM",
28
+ iv: iv,
29
+ }, key, dataToEncrypt);
30
+ // Return encrypted package
31
+ return {
32
+ encryptedData: (0, hashing_1.arrayBufferToBase64)(encryptedData),
33
+ iv: (0, hashing_1.arrayBufferToBase64)(iv.buffer),
34
+ salt: (0, hashing_1.arrayBufferToBase64)(salt),
35
+ fileName: fileName,
36
+ timestamp: new Date().toISOString(),
37
+ originalSize: dataToEncrypt.byteLength,
38
+ };
39
+ }
40
+ catch (error) {
41
+ console.error("Error encrypting file:", error);
42
+ throw error instanceof Error ? error : new Error("Unknown error occurred");
43
+ }
44
+ };
45
+ exports.encryptFile = encryptFile;
46
+ const decryptFile = async (encryptedPackage, password) => {
47
+ try {
48
+ const { encryptedData, iv, salt, fileName, originalSize } = encryptedPackage;
49
+ // Convert base64 back to ArrayBuffer
50
+ const saltBuffer = (0, hashing_1.base64ToArrayBuffer)(salt);
51
+ const ivBuffer = (0, hashing_1.base64ToArrayBuffer)(iv);
52
+ const dataBuffer = (0, hashing_1.base64ToArrayBuffer)(encryptedData);
53
+ // Derive the same key using password and salt
54
+ const { key } = await (0, symmetric_1.deriveKeyFromPassword)(password, saltBuffer);
55
+ // Decrypt the data
56
+ const decryptedData = await crypto.subtle.decrypt({
57
+ name: "AES-GCM",
58
+ iv: ivBuffer,
59
+ }, key, dataBuffer);
60
+ return {
61
+ data: decryptedData,
62
+ fileName: fileName,
63
+ originalSize: originalSize,
64
+ decryptedSize: decryptedData.byteLength,
65
+ };
66
+ }
67
+ catch (error) {
68
+ console.error("Error decrypting file:", error);
69
+ throw new Error("Failed to decrypt file. Check password and try again.");
70
+ }
71
+ };
72
+ exports.decryptFile = decryptFile;
73
+ const encryptTextFile = async (textContent, password, fileName = "encrypted.txt") => {
74
+ return await (0, exports.encryptFile)(textContent, password, fileName);
75
+ };
76
+ exports.encryptTextFile = encryptTextFile;
77
+ const decryptTextFile = async (encryptedPackage, password) => {
78
+ const result = await (0, exports.decryptFile)(encryptedPackage, password);
79
+ const textContent = new TextDecoder().decode(result.data);
80
+ return {
81
+ ...result,
82
+ textContent: textContent,
83
+ };
84
+ };
85
+ exports.decryptTextFile = decryptTextFile;
86
+ const encryptBinaryFile = async (file, password) => {
87
+ if (!(file instanceof File)) {
88
+ throw new Error("Expected File object for binary encryption");
89
+ }
90
+ const encryptedPackage = await (0, exports.encryptFile)(file, password, file.name);
91
+ return {
92
+ ...encryptedPackage,
93
+ mimeType: file.type,
94
+ fileSize: file.size,
95
+ };
96
+ };
97
+ exports.encryptBinaryFile = encryptBinaryFile;
98
+ const decryptBinaryFile = async (encryptedPackage, password) => {
99
+ const result = await (0, exports.decryptFile)(encryptedPackage, password);
100
+ return {
101
+ ...result,
102
+ blob: new Blob([result.data], {
103
+ type: encryptedPackage.mimeType || "application/octet-stream",
104
+ }),
105
+ mimeType: encryptedPackage.mimeType,
106
+ };
107
+ };
108
+ exports.decryptBinaryFile = decryptBinaryFile;
109
+ const createSecureFileDownload = (data, fileName, mimeType = "application/octet-stream") => {
110
+ let blob;
111
+ if (data instanceof ArrayBuffer) {
112
+ blob = new Blob([data], { type: mimeType });
113
+ }
114
+ else if (typeof data === "string") {
115
+ blob = new Blob([data], { type: "text/plain" });
116
+ }
117
+ else {
118
+ blob = data; // Assume it's already a Blob
119
+ }
120
+ const url = URL.createObjectURL(blob);
121
+ const a = document.createElement("a");
122
+ a.href = url;
123
+ a.download = fileName;
124
+ document.body.appendChild(a);
125
+ a.click();
126
+ document.body.removeChild(a);
127
+ URL.revokeObjectURL(url);
128
+ };
129
+ exports.createSecureFileDownload = createSecureFileDownload;
130
+ const parseEncryptedFilePackage = async (file) => {
131
+ try {
132
+ // Read the file content
133
+ const content = await file.text();
134
+ // Parse as JSON
135
+ const parsed = JSON.parse(content);
136
+ // Validate required properties
137
+ const requiredProperties = [
138
+ "encryptedData",
139
+ "iv",
140
+ "salt",
141
+ "fileName",
142
+ "timestamp",
143
+ "originalSize",
144
+ ];
145
+ for (const prop of requiredProperties) {
146
+ if (!parsed.hasOwnProperty(prop)) {
147
+ throw new Error(`Missing required property: ${prop}`);
148
+ }
149
+ }
150
+ // Validate data types
151
+ if (typeof parsed.encryptedData !== "string") {
152
+ throw new Error("encryptedData must be a base64 string");
153
+ }
154
+ if (typeof parsed.iv !== "string") {
155
+ throw new Error("iv must be a base64 string");
156
+ }
157
+ if (typeof parsed.salt !== "string") {
158
+ throw new Error("salt must be a base64 string");
159
+ }
160
+ if (typeof parsed.fileName !== "string") {
161
+ throw new Error("fileName must be a string");
162
+ }
163
+ if (typeof parsed.originalSize !== "number") {
164
+ throw new Error("originalSize must be a number");
165
+ }
166
+ return {
167
+ isValid: true,
168
+ package: parsed,
169
+ metadata: {
170
+ fileName: parsed.fileName,
171
+ originalSize: parsed.originalSize,
172
+ timestamp: parsed.timestamp,
173
+ type: parsed.type || "unknown",
174
+ mimeType: parsed.mimeType || null,
175
+ },
176
+ };
177
+ }
178
+ catch (error) {
179
+ return {
180
+ isValid: false,
181
+ error: error instanceof Error ? error.message : "Unknown error occurred",
182
+ package: undefined,
183
+ metadata: undefined,
184
+ };
185
+ }
186
+ };
187
+ exports.parseEncryptedFilePackage = parseEncryptedFilePackage;
188
+ const decryptUploadedFile = async (encryptedFilePackage, password) => {
189
+ try {
190
+ // First validate the package
191
+ const validation = await (0, exports.parseEncryptedFilePackage)(encryptedFilePackage);
192
+ if (!validation.isValid) {
193
+ throw new Error(`Invalid encrypted file package: ${validation.error}`);
194
+ }
195
+ const { package: pkg, metadata } = validation;
196
+ // Decrypt the file
197
+ const decryptedResult = await (0, exports.decryptFile)(pkg, password);
198
+ // Return enhanced result with metadata
199
+ return {
200
+ ...decryptedResult,
201
+ isTextFile: metadata?.type === "text" || metadata?.mimeType?.startsWith("text/"),
202
+ textContent: metadata?.type === "text"
203
+ ? new TextDecoder().decode(decryptedResult.data)
204
+ : undefined,
205
+ };
206
+ }
207
+ catch (error) {
208
+ console.error("Error decrypting uploaded file:", error);
209
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
210
+ throw new Error(`Failed to decrypt uploaded file: ${errorMessage}`);
211
+ }
212
+ };
213
+ exports.decryptUploadedFile = decryptUploadedFile;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.arrayBufferToBase64 = exports.base64ToArrayBuffer = exports.concatArrayBuffers = exports.hexToBuffer = exports.bufferToHex = exports.sha3_512Hash = exports.sha512Hash = exports.sha256Hash = exports.randomString = void 0;
4
+ // Cryptographically Random String Generator
5
+ const randomString = (additionalSalt = "") => {
6
+ const randomStringLength = 16;
7
+ const randomValues = crypto.getRandomValues(new Uint8Array(randomStringLength));
8
+ const randomHex = Array.from(randomValues)
9
+ .map((byte) => byte.toString(16).padStart(2, "0"))
10
+ .join("");
11
+ return additionalSalt ? additionalSalt + randomHex : randomHex;
12
+ };
13
+ exports.randomString = randomString;
14
+ // Hashing Methods
15
+ const sha256Hash = async (input) => {
16
+ const inputString = JSON.stringify(input);
17
+ const encoder = new TextEncoder();
18
+ const data = encoder.encode(inputString);
19
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
20
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
21
+ return hashArray.map((byte) => byte.toString(16).padStart(2, "0")).join("");
22
+ };
23
+ exports.sha256Hash = sha256Hash;
24
+ const sha512Hash = async (input) => {
25
+ const inputString = JSON.stringify(input);
26
+ const encoder = new TextEncoder();
27
+ const data = encoder.encode(inputString);
28
+ const hashBuffer = await crypto.subtle.digest("SHA-512", data);
29
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
30
+ return hashArray.map((byte) => byte.toString(16).padStart(2, "0")).join("");
31
+ };
32
+ exports.sha512Hash = sha512Hash;
33
+ const sha3_512Hash = async (input) => {
34
+ // Note: SHA3-512 requires a library like js-sha3
35
+ // For now, we'll use SHA-512 as a fallback
36
+ const inputString = JSON.stringify(input);
37
+ const encoder = new TextEncoder();
38
+ const data = encoder.encode(inputString);
39
+ const hashBuffer = await crypto.subtle.digest("SHA-512", data);
40
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
41
+ return hashArray.map((byte) => byte.toString(16).padStart(2, "0")).join("");
42
+ };
43
+ exports.sha3_512Hash = sha3_512Hash;
44
+ // Utility functions for crypto operations
45
+ const bufferToHex = (buffer) => {
46
+ return Array.from(new Uint8Array(buffer))
47
+ .map((b) => b.toString(16).padStart(2, "0"))
48
+ .join("");
49
+ };
50
+ exports.bufferToHex = bufferToHex;
51
+ const hexToBuffer = (hex) => {
52
+ const bytes = new Uint8Array(hex.length / 2);
53
+ for (let i = 0; i < hex.length; i += 2) {
54
+ bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
55
+ }
56
+ return bytes.buffer;
57
+ };
58
+ exports.hexToBuffer = hexToBuffer;
59
+ const concatArrayBuffers = (...buffers) => {
60
+ const totalLength = buffers.reduce((sum, buf) => sum + buf.byteLength, 0);
61
+ const result = new Uint8Array(totalLength);
62
+ let offset = 0;
63
+ for (const buffer of buffers) {
64
+ result.set(new Uint8Array(buffer), offset);
65
+ offset += buffer.byteLength;
66
+ }
67
+ return result.buffer;
68
+ };
69
+ exports.concatArrayBuffers = concatArrayBuffers;
70
+ const base64ToArrayBuffer = (base64) => {
71
+ const binaryString = atob(base64);
72
+ const bytes = new Uint8Array(binaryString.length);
73
+ for (let i = 0; i < binaryString.length; i++) {
74
+ bytes[i] = binaryString.charCodeAt(i);
75
+ }
76
+ return bytes.buffer;
77
+ };
78
+ exports.base64ToArrayBuffer = base64ToArrayBuffer;
79
+ const arrayBufferToBase64 = (buffer) => {
80
+ const bytes = new Uint8Array(buffer);
81
+ let binary = "";
82
+ for (let i = 0; i < bytes.byteLength; i++) {
83
+ binary += String.fromCharCode(bytes[i]);
84
+ }
85
+ return btoa(binary);
86
+ };
87
+ exports.arrayBufferToBase64 = arrayBufferToBase64;