shogun-core 5.2.2 → 6.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.
- package/README.md +123 -20
- package/dist/browser/shogun-core.js +1134 -493
- package/dist/browser/shogun-core.js.map +1 -1
- package/dist/config/simplified-config.js +108 -40
- package/dist/crypto/mls.js +34 -15
- package/dist/crypto/sframe.js +13 -11
- package/dist/examples/auth-test.js +263 -59
- package/dist/examples/crypto-identity-example.js +55 -21
- package/dist/examples/mls-3-member-test.js +97 -0
- package/dist/examples/mls-multi-member.js +153 -0
- package/dist/examples/mls-sframe-test.js +14 -11
- package/dist/examples/mls-simple-test.js +58 -0
- package/dist/examples/shogun-core-example.js +90 -0
- package/dist/examples/zkproof-credentials-example.js +9 -5
- package/dist/examples/zkproof-example.js +14 -10
- package/dist/gundb/api.js +17 -16
- package/dist/gundb/db.js +769 -328
- package/dist/index.js +4 -4
- package/dist/managers/CoreInitializer.js +21 -15
- package/dist/managers/CryptoIdentityManager.js +79 -32
- package/dist/plugins/zkproof/zkCredentials.js +4 -1
- package/dist/types/config/simplified-config.d.ts +64 -3
- package/dist/types/crypto/sframe.d.ts +4 -0
- package/dist/types/examples/mls-3-member-test.d.ts +6 -0
- package/dist/types/examples/mls-multi-member.d.ts +6 -0
- package/dist/types/examples/mls-simple-test.d.ts +6 -0
- package/dist/types/examples/shogun-core-example.d.ts +8 -0
- package/dist/types/gundb/api.d.ts +6 -13
- package/dist/types/gundb/db.d.ts +84 -41
- package/dist/types/index.d.ts +4 -2
- package/dist/types/interfaces/shogun.d.ts +1 -2
- package/dist/types/managers/CryptoIdentityManager.d.ts +2 -1
- package/package.json +11 -8
- package/dist/examples/mls-advanced-example.js +0 -294
- package/dist/examples/quick-auth-test.js +0 -61
- package/dist/examples/simple-api-test.js +0 -114
- package/dist/examples/simple-crypto-identity-example.js +0 -84
- package/dist/examples/timeout-test.js +0 -227
- package/dist/types/examples/mls-advanced-example.d.ts +0 -53
- package/dist/types/examples/quick-auth-test.d.ts +0 -8
- package/dist/types/examples/simple-api-test.d.ts +0 -10
- package/dist/types/examples/simple-crypto-identity-example.d.ts +0 -6
- package/dist/types/examples/timeout-test.d.ts +0 -8
|
@@ -2,31 +2,94 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Simplified configuration options to reduce complexity
|
|
4
4
|
* Provides sensible defaults and easy-to-use presets
|
|
5
|
+
*
|
|
6
|
+
* NOTE: This file is deprecated. ShogunCore now requires an existing Gun instance.
|
|
7
|
+
* Use the examples in the examples/ directory for proper usage.
|
|
5
8
|
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
6
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.QuickConfig = exports.ConfigHelpers = exports.ShogunConfigBuilder = exports.ShogunPresets = void 0;
|
|
13
|
+
exports.QuickConfig = exports.ConfigHelpers = exports.ShogunConfigBuilder = exports.ShogunPresets = exports.GunInstanceHelpers = void 0;
|
|
14
|
+
const gun_1 = __importDefault(require("gun/gun"));
|
|
15
|
+
require("gun/lib/then");
|
|
16
|
+
require("gun/lib/radix");
|
|
17
|
+
require("gun/lib/radisk");
|
|
18
|
+
require("gun/lib/store");
|
|
19
|
+
require("gun/lib/rindexed");
|
|
20
|
+
require("gun/lib/webrtc");
|
|
21
|
+
/**
|
|
22
|
+
* Helper functions to create Gun instances with common configurations
|
|
23
|
+
* These functions create Gun instances that can be passed to ShogunCore
|
|
24
|
+
*/
|
|
25
|
+
exports.GunInstanceHelpers = {
|
|
26
|
+
/**
|
|
27
|
+
* Create a minimal Gun instance for simple apps
|
|
28
|
+
*/
|
|
29
|
+
minimal: () => (0, gun_1.default)({
|
|
30
|
+
peers: ["https://shogunnode.scobrudot.dev/gun"],
|
|
31
|
+
localStorage: true,
|
|
32
|
+
}),
|
|
33
|
+
/**
|
|
34
|
+
* Create a development Gun instance with local storage
|
|
35
|
+
*/
|
|
36
|
+
development: () => (0, gun_1.default)({
|
|
37
|
+
peers: ["https://shogunnode.scobrudot.dev/gun"],
|
|
38
|
+
localStorage: true,
|
|
39
|
+
radisk: false,
|
|
40
|
+
}),
|
|
41
|
+
/**
|
|
42
|
+
* Create a production Gun instance with multiple peers
|
|
43
|
+
*/
|
|
44
|
+
production: (customPeers) => (0, gun_1.default)({
|
|
45
|
+
peers: customPeers || [
|
|
46
|
+
"https://shogunnode.scobrudot.dev/gun",
|
|
47
|
+
"https://peer.wallie.io/gun",
|
|
48
|
+
],
|
|
49
|
+
localStorage: true,
|
|
50
|
+
radisk: true,
|
|
51
|
+
}),
|
|
52
|
+
/**
|
|
53
|
+
* Create an offline-first Gun instance
|
|
54
|
+
*/
|
|
55
|
+
offline: () => (0, gun_1.default)({
|
|
56
|
+
peers: [],
|
|
57
|
+
localStorage: true,
|
|
58
|
+
radisk: true,
|
|
59
|
+
}),
|
|
60
|
+
/**
|
|
61
|
+
* Create a Gun instance for Web3-enabled apps
|
|
62
|
+
*/
|
|
63
|
+
web3: () => (0, gun_1.default)({
|
|
64
|
+
peers: ["https://shogunnode.scobrudot.dev/gun"],
|
|
65
|
+
localStorage: true,
|
|
66
|
+
}),
|
|
67
|
+
/**
|
|
68
|
+
* Create a Gun instance for WebAuthn-enabled apps
|
|
69
|
+
*/
|
|
70
|
+
webauthn: () => (0, gun_1.default)({
|
|
71
|
+
peers: ["https://shogunnode.scobrudot.dev/gun"],
|
|
72
|
+
localStorage: true,
|
|
73
|
+
}),
|
|
74
|
+
};
|
|
8
75
|
/**
|
|
9
76
|
* Preset configurations for common use cases
|
|
77
|
+
* @deprecated Use GunInstanceHelpers to create Gun instances and pass them to ShogunCore
|
|
10
78
|
*/
|
|
11
79
|
exports.ShogunPresets = {
|
|
12
80
|
/**
|
|
13
81
|
* Minimal configuration for simple apps
|
|
82
|
+
* @deprecated Use GunInstanceHelpers.minimal() instead
|
|
14
83
|
*/
|
|
15
84
|
minimal: () => ({
|
|
16
|
-
|
|
17
|
-
peers: ["https://relay.shogun-eco.xyz/gun"],
|
|
18
|
-
localStorage: true,
|
|
19
|
-
},
|
|
85
|
+
gunInstance: exports.GunInstanceHelpers.minimal(),
|
|
20
86
|
}),
|
|
21
87
|
/**
|
|
22
88
|
* Development configuration with local storage
|
|
89
|
+
* @deprecated Use GunInstanceHelpers.development() instead
|
|
23
90
|
*/
|
|
24
91
|
development: () => ({
|
|
25
|
-
|
|
26
|
-
peers: ["https://relay.shogun-eco.xyz/gun"],
|
|
27
|
-
localStorage: true,
|
|
28
|
-
radisk: false,
|
|
29
|
-
},
|
|
92
|
+
gunInstance: exports.GunInstanceHelpers.development(),
|
|
30
93
|
timeouts: {
|
|
31
94
|
login: 5000,
|
|
32
95
|
signup: 5000,
|
|
@@ -35,16 +98,10 @@ exports.ShogunPresets = {
|
|
|
35
98
|
}),
|
|
36
99
|
/**
|
|
37
100
|
* Production configuration with multiple peers
|
|
101
|
+
* @deprecated Use GunInstanceHelpers.production() instead
|
|
38
102
|
*/
|
|
39
103
|
production: (customPeers) => ({
|
|
40
|
-
|
|
41
|
-
peers: customPeers || [
|
|
42
|
-
"https://relay.shogun-eco.xyz/gun",
|
|
43
|
-
"https://peer.wallie.io/gun",
|
|
44
|
-
],
|
|
45
|
-
localStorage: true,
|
|
46
|
-
radisk: true,
|
|
47
|
-
},
|
|
104
|
+
gunInstance: exports.GunInstanceHelpers.production(customPeers),
|
|
48
105
|
timeouts: {
|
|
49
106
|
login: 10000,
|
|
50
107
|
signup: 10000,
|
|
@@ -53,62 +110,59 @@ exports.ShogunPresets = {
|
|
|
53
110
|
}),
|
|
54
111
|
/**
|
|
55
112
|
* Offline-first configuration
|
|
113
|
+
* @deprecated Use GunInstanceHelpers.offline() instead
|
|
56
114
|
*/
|
|
57
115
|
offline: () => ({
|
|
58
|
-
|
|
59
|
-
peers: [],
|
|
60
|
-
localStorage: true,
|
|
61
|
-
radisk: true,
|
|
62
|
-
},
|
|
116
|
+
gunInstance: exports.GunInstanceHelpers.offline(),
|
|
63
117
|
}),
|
|
64
118
|
/**
|
|
65
119
|
* Web3-enabled configuration
|
|
120
|
+
* @deprecated Use GunInstanceHelpers.web3() instead
|
|
66
121
|
*/
|
|
67
122
|
web3: () => ({
|
|
68
|
-
|
|
69
|
-
peers: ["https://relay.shogun-eco.xyz/gun"],
|
|
70
|
-
localStorage: true,
|
|
71
|
-
},
|
|
123
|
+
gunInstance: exports.GunInstanceHelpers.web3(),
|
|
72
124
|
web3: {
|
|
73
125
|
enabled: true,
|
|
74
126
|
},
|
|
75
127
|
}),
|
|
76
128
|
/**
|
|
77
129
|
* WebAuthn-enabled configuration
|
|
130
|
+
* @deprecated Use GunInstanceHelpers.webauthn() instead
|
|
78
131
|
*/
|
|
79
132
|
webauthn: () => ({
|
|
80
|
-
|
|
81
|
-
peers: ["https://relay.shogun-eco.xyz/gun"],
|
|
82
|
-
localStorage: true,
|
|
83
|
-
},
|
|
133
|
+
gunInstance: exports.GunInstanceHelpers.webauthn(),
|
|
84
134
|
webauthn: {
|
|
85
135
|
enabled: true,
|
|
86
136
|
rpName: "My Shogun App",
|
|
87
|
-
rpId: window.location.hostname,
|
|
137
|
+
rpId: typeof window !== "undefined" ? window.location.hostname : "localhost",
|
|
88
138
|
},
|
|
89
139
|
}),
|
|
90
140
|
};
|
|
91
141
|
/**
|
|
92
142
|
* Configuration builder for custom setups
|
|
143
|
+
* @deprecated Use GunInstanceHelpers to create Gun instances and pass them to ShogunCore
|
|
93
144
|
*/
|
|
94
145
|
class ShogunConfigBuilder {
|
|
95
146
|
constructor() {
|
|
96
147
|
this.config = {};
|
|
148
|
+
this.gunOptions = {};
|
|
97
149
|
}
|
|
98
150
|
/**
|
|
99
|
-
* Set Gun options
|
|
151
|
+
* Set Gun options (deprecated - use GunInstanceHelpers instead)
|
|
152
|
+
* @deprecated Use GunInstanceHelpers to create Gun instances
|
|
100
153
|
*/
|
|
101
|
-
|
|
102
|
-
this.
|
|
154
|
+
setGunOptions(options) {
|
|
155
|
+
this.gunOptions = { ...this.gunOptions, ...options };
|
|
103
156
|
return this;
|
|
104
157
|
}
|
|
105
158
|
/**
|
|
106
|
-
* Add peers
|
|
159
|
+
* Add peers (deprecated - use GunInstanceHelpers instead)
|
|
160
|
+
* @deprecated Use GunInstanceHelpers to create Gun instances
|
|
107
161
|
*/
|
|
108
162
|
peers(peerList) {
|
|
109
|
-
this.
|
|
110
|
-
...this.
|
|
111
|
-
peers: [...(this.
|
|
163
|
+
this.gunOptions = {
|
|
164
|
+
...this.gunOptions,
|
|
165
|
+
peers: [...(this.gunOptions?.peers || []), ...peerList],
|
|
112
166
|
};
|
|
113
167
|
return this;
|
|
114
168
|
}
|
|
@@ -146,18 +200,24 @@ class ShogunConfigBuilder {
|
|
|
146
200
|
}
|
|
147
201
|
/**
|
|
148
202
|
* Build the final configuration
|
|
203
|
+
* @deprecated Use GunInstanceHelpers to create Gun instances and pass them to ShogunCore
|
|
149
204
|
*/
|
|
150
205
|
build() {
|
|
151
|
-
return
|
|
206
|
+
return {
|
|
207
|
+
...this.config,
|
|
208
|
+
gunInstance: (0, gun_1.default)(this.gunOptions),
|
|
209
|
+
};
|
|
152
210
|
}
|
|
153
211
|
}
|
|
154
212
|
exports.ShogunConfigBuilder = ShogunConfigBuilder;
|
|
155
213
|
/**
|
|
156
214
|
* Helper functions for common configuration patterns
|
|
215
|
+
* @deprecated Use GunInstanceHelpers to create Gun instances and pass them to ShogunCore
|
|
157
216
|
*/
|
|
158
217
|
exports.ConfigHelpers = {
|
|
159
218
|
/**
|
|
160
219
|
* Create a configuration for a specific environment
|
|
220
|
+
* @deprecated Use GunInstanceHelpers instead
|
|
161
221
|
*/
|
|
162
222
|
forEnvironment(env) {
|
|
163
223
|
switch (env) {
|
|
@@ -173,12 +233,14 @@ exports.ConfigHelpers = {
|
|
|
173
233
|
},
|
|
174
234
|
/**
|
|
175
235
|
* Create a configuration with custom peers
|
|
236
|
+
* @deprecated Use GunInstanceHelpers.production(peers) instead
|
|
176
237
|
*/
|
|
177
238
|
withPeers(peers) {
|
|
178
239
|
return exports.ShogunPresets.production(peers);
|
|
179
240
|
},
|
|
180
241
|
/**
|
|
181
242
|
* Create a configuration for a specific use case
|
|
243
|
+
* @deprecated Use GunInstanceHelpers instead
|
|
182
244
|
*/
|
|
183
245
|
forUseCase(useCase) {
|
|
184
246
|
const baseConfig = exports.ShogunPresets.production();
|
|
@@ -211,26 +273,32 @@ exports.ConfigHelpers = {
|
|
|
211
273
|
};
|
|
212
274
|
/**
|
|
213
275
|
* Quick configuration functions
|
|
276
|
+
* @deprecated Use GunInstanceHelpers to create Gun instances and pass them to ShogunCore
|
|
214
277
|
*/
|
|
215
278
|
exports.QuickConfig = {
|
|
216
279
|
/**
|
|
217
280
|
* Minimal setup for quick testing
|
|
281
|
+
* @deprecated Use GunInstanceHelpers.minimal() instead
|
|
218
282
|
*/
|
|
219
283
|
test: () => exports.ShogunPresets.minimal(),
|
|
220
284
|
/**
|
|
221
285
|
* Standard setup for most apps
|
|
286
|
+
* @deprecated Use GunInstanceHelpers.production() instead
|
|
222
287
|
*/
|
|
223
288
|
standard: () => exports.ShogunPresets.production(),
|
|
224
289
|
/**
|
|
225
290
|
* Setup with WebAuthn for secure apps
|
|
291
|
+
* @deprecated Use GunInstanceHelpers.webauthn() instead
|
|
226
292
|
*/
|
|
227
293
|
secure: () => exports.ShogunPresets.webauthn(),
|
|
228
294
|
/**
|
|
229
295
|
* Setup with Web3 for crypto apps
|
|
296
|
+
* @deprecated Use GunInstanceHelpers.web3() instead
|
|
230
297
|
*/
|
|
231
298
|
crypto: () => exports.ShogunPresets.web3(),
|
|
232
299
|
/**
|
|
233
300
|
* Offline setup for local development
|
|
301
|
+
* @deprecated Use GunInstanceHelpers.offline() instead
|
|
234
302
|
*/
|
|
235
303
|
local: () => exports.ShogunPresets.offline(),
|
|
236
304
|
};
|
package/dist/crypto/mls.js
CHANGED
|
@@ -141,7 +141,9 @@ class MLSManager {
|
|
|
141
141
|
console.group("🔍 [MLS Debug] Commit Structure");
|
|
142
142
|
console.log("commitResult keys:", Object.keys(commitResult));
|
|
143
143
|
console.log("commit:", commitResult.commit);
|
|
144
|
-
|
|
144
|
+
if (commitResult.commit?.wireformat === "mls_private_message") {
|
|
145
|
+
console.log("commit.privateMessage:", commitResult.commit.privateMessage);
|
|
146
|
+
}
|
|
145
147
|
console.groupEnd();
|
|
146
148
|
// RFC 9420 Section 11.2: Commit Distribution
|
|
147
149
|
// ⚠️ IMPORTANT: The returned commit MUST be sent to all existing group members
|
|
@@ -205,8 +207,8 @@ class MLSManager {
|
|
|
205
207
|
else {
|
|
206
208
|
console.log(` Node ${i}:`, {
|
|
207
209
|
type: typeof node,
|
|
208
|
-
isObject: typeof node === "object",
|
|
209
|
-
hasNodeType:
|
|
210
|
+
isObject: typeof node === "object" && node !== null,
|
|
211
|
+
hasNodeType: typeof node === "object" && node !== null && "nodeType" in node,
|
|
210
212
|
nodeType: node?.nodeType,
|
|
211
213
|
keys: node && typeof node === "object"
|
|
212
214
|
? Object.keys(node).slice(0, 5)
|
|
@@ -283,10 +285,11 @@ class MLSManager {
|
|
|
283
285
|
}
|
|
284
286
|
// Decode the message
|
|
285
287
|
const decoded = (0, ts_mls_1.decodeMlsMessage)(envelope.ciphertext, 0);
|
|
286
|
-
if (!decoded) {
|
|
288
|
+
if (!decoded || decoded.length === 0) {
|
|
289
|
+
// Changed from 0n to 0 to fix type error
|
|
287
290
|
throw new Error("Failed to decode message");
|
|
288
291
|
}
|
|
289
|
-
const
|
|
292
|
+
const decodedMessage = decoded[0];
|
|
290
293
|
if (decodedMessage.wireformat !== "mls_private_message") {
|
|
291
294
|
throw new Error("Expected private message");
|
|
292
295
|
}
|
|
@@ -392,7 +395,7 @@ class MLSManager {
|
|
|
392
395
|
}
|
|
393
396
|
catch (error) {
|
|
394
397
|
console.error("❌ [MLS] Failed to process commit:", error);
|
|
395
|
-
console.error("❌ [MLS Debug] Error details:", error instanceof Error ? error.stack :
|
|
398
|
+
console.error("❌ [MLS Debug] Error details:", error instanceof Error ? error.stack : String(error));
|
|
396
399
|
console.error("❌ [MLS Debug] Error message:", error instanceof Error ? error.message : String(error));
|
|
397
400
|
throw new Error(`Commit processing failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
398
401
|
}
|
|
@@ -412,7 +415,7 @@ class MLSManager {
|
|
|
412
415
|
const removeProposals = memberIndices.map((index) => ({
|
|
413
416
|
proposalType: "remove",
|
|
414
417
|
remove: {
|
|
415
|
-
removed: index, //
|
|
418
|
+
removed: index, // Changed from BigInt(index) to number
|
|
416
419
|
},
|
|
417
420
|
}));
|
|
418
421
|
// Create commit with remove proposals
|
|
@@ -420,11 +423,21 @@ class MLSManager {
|
|
|
420
423
|
// Update group state
|
|
421
424
|
this.groups.set(groupId, commitResult.newState);
|
|
422
425
|
// Encode the commit
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
wireformat
|
|
426
|
-
|
|
427
|
-
|
|
426
|
+
let encodedCommit;
|
|
427
|
+
if (commitResult.commit &&
|
|
428
|
+
commitResult.commit.wireformat === "mls_public_message") {
|
|
429
|
+
encodedCommit = (0, ts_mls_1.encodeMlsMessage)({
|
|
430
|
+
publicMessage: commitResult.commit
|
|
431
|
+
.publicMessage,
|
|
432
|
+
wireformat: "mls_public_message",
|
|
433
|
+
version: "mls10",
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
// Fallback or error handling if not a public message
|
|
438
|
+
// For now, we'll assume it should be public for remove operation.
|
|
439
|
+
throw new Error("Commit result does not contain a public message for encoding.");
|
|
440
|
+
}
|
|
428
441
|
console.log("✅ [MLS] Members removed");
|
|
429
442
|
return encodedCommit;
|
|
430
443
|
}
|
|
@@ -517,9 +530,15 @@ class MLSManager {
|
|
|
517
530
|
const node = state.ratchetTree[i];
|
|
518
531
|
if (node &&
|
|
519
532
|
node.nodeType === "leaf" &&
|
|
520
|
-
node.leaf
|
|
521
|
-
const
|
|
522
|
-
|
|
533
|
+
node.leaf.credential) {
|
|
534
|
+
const credential = node.leaf.credential;
|
|
535
|
+
if (credential.credentialType === "basic" && credential.identity) {
|
|
536
|
+
const identity = new TextDecoder().decode(credential.identity);
|
|
537
|
+
members.push(identity);
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
console.warn("Skipping credential without basic identity:", credential);
|
|
541
|
+
}
|
|
523
542
|
}
|
|
524
543
|
}
|
|
525
544
|
}
|
package/dist/crypto/sframe.js
CHANGED
|
@@ -110,6 +110,13 @@ class SFrameManager {
|
|
|
110
110
|
throw new Error(`SFrame key derivation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Set a shared SFrame key (e.g., from another member)
|
|
115
|
+
*/
|
|
116
|
+
async setSharedKey(sframeKey) {
|
|
117
|
+
this.keys.set(sframeKey.keyId, sframeKey);
|
|
118
|
+
console.log(`✅ [SFrame] Shared key ${sframeKey.keyId} set.`);
|
|
119
|
+
}
|
|
113
120
|
/**
|
|
114
121
|
* Set the active encryption key
|
|
115
122
|
*/
|
|
@@ -155,11 +162,9 @@ class SFrameManager {
|
|
|
155
162
|
tagLength: 128, // 128-bit authentication tag
|
|
156
163
|
}, sframeKey.key, frameData);
|
|
157
164
|
// RFC 9605: SFrame format = header + ciphertext (IV is derived, not transmitted)
|
|
158
|
-
|
|
159
|
-
const encrypted = new Uint8Array(header.length + iv.length + ciphertext.byteLength);
|
|
165
|
+
const encrypted = new Uint8Array(header.length + ciphertext.byteLength);
|
|
160
166
|
encrypted.set(header, 0);
|
|
161
|
-
encrypted.set(
|
|
162
|
-
encrypted.set(new Uint8Array(ciphertext), header.length + iv.length);
|
|
167
|
+
encrypted.set(new Uint8Array(ciphertext), header.length);
|
|
163
168
|
// Increment frame counter
|
|
164
169
|
this.frameCounter++;
|
|
165
170
|
return encrypted;
|
|
@@ -184,20 +189,17 @@ class SFrameManager {
|
|
|
184
189
|
if (!sframeKey) {
|
|
185
190
|
throw new Error(`SFrame key ${keyId} not found`);
|
|
186
191
|
}
|
|
187
|
-
//
|
|
188
|
-
const iv = encryptedFrame.slice(5, 17);
|
|
189
|
-
// RFC 9605: Verify IV derivation (optional check for debugging)
|
|
190
|
-
// Reconstruct expected IV from frame count and salt
|
|
192
|
+
// RFC 9605: IV is derived from frame count and salt (not transmitted)
|
|
191
193
|
const counterBytes = new Uint8Array(12);
|
|
192
194
|
const counterView = new DataView(counterBytes.buffer);
|
|
193
195
|
counterView.setUint32(4, Math.floor(frameCount / 0x100000000), false);
|
|
194
196
|
counterView.setUint32(8, frameCount & 0xffffffff, false);
|
|
195
|
-
const
|
|
197
|
+
const iv = new Uint8Array(12);
|
|
196
198
|
for (let i = 0; i < 12; i++) {
|
|
197
|
-
|
|
199
|
+
iv[i] = sframeKey.salt[i] ^ counterBytes[i];
|
|
198
200
|
}
|
|
199
201
|
// Extract ciphertext (rest of the data)
|
|
200
|
-
const ciphertext = encryptedFrame.slice(
|
|
202
|
+
const ciphertext = encryptedFrame.slice(header.length);
|
|
201
203
|
// Decrypt the frame with header authentication (RFC 9605 Section 4.3)
|
|
202
204
|
const plaintext = await crypto.subtle.decrypt({
|
|
203
205
|
name: "AES-GCM",
|