dignity.js 0.6.0 → 0.7.1
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 +97 -1
- package/dist/dignity.cjs.js +802 -16
- package/dist/dignity.cjs.js.map +4 -4
- package/dist/dignity.esm.js +795 -9
- package/dist/dignity.esm.js.map +3 -3
- package/dist/dignity.min.js +9 -9
- package/docs/assets/dignity.esm.js +928 -30
- package/docs/assets/playground-demos.js +342 -0
- package/docs/assets/playground.css +277 -0
- package/docs/assets/playground.js +248 -0
- package/docs/assets/styles.css +18 -2
- package/docs/index.html +23 -3
- package/docs/openapi-like.json +1 -1
- package/package.json +5 -3
- package/src/core/dignity-p2p.js +229 -4
- package/src/index.js +25 -1
- package/src/security/derive-key-pair.js +147 -0
- package/src/security/identity-rotation.js +427 -0
- package/src/security/message-security-service.js +94 -4
|
@@ -2444,19 +2444,511 @@ var require_vdf = __commonJS({
|
|
|
2444
2444
|
}
|
|
2445
2445
|
});
|
|
2446
2446
|
|
|
2447
|
+
// src/security/derive-key-pair.js
|
|
2448
|
+
var require_derive_key_pair = __commonJS({
|
|
2449
|
+
"src/security/derive-key-pair.js"(exports, module) {
|
|
2450
|
+
var nacl = require_nacl_fast();
|
|
2451
|
+
var naclUtil = require_nacl_util();
|
|
2452
|
+
var { deriveBroadcastKey, DEFAULT_SECURITY_OPTIONS } = require_message_security_service();
|
|
2453
|
+
var SIGNING_INFO = "dignity-signing-v1";
|
|
2454
|
+
var ENCRYPTION_INFO = "dignity-encryption-v1";
|
|
2455
|
+
var COLD_RECOVERY_INFO = "dignity-cold-recovery-v1";
|
|
2456
|
+
function utf8ToBytes(value) {
|
|
2457
|
+
return naclUtil.decodeUTF8(value);
|
|
2458
|
+
}
|
|
2459
|
+
function concatBytes(...parts) {
|
|
2460
|
+
const total = parts.reduce((sum, part) => sum + part.length, 0);
|
|
2461
|
+
const result = new Uint8Array(total);
|
|
2462
|
+
let offset = 0;
|
|
2463
|
+
for (const part of parts) {
|
|
2464
|
+
result.set(part, offset);
|
|
2465
|
+
offset += part.length;
|
|
2466
|
+
}
|
|
2467
|
+
return result;
|
|
2468
|
+
}
|
|
2469
|
+
function buildColdRecoverySalt(username, pepper = "") {
|
|
2470
|
+
if (!username || typeof username !== "string") {
|
|
2471
|
+
throw new Error("deriveColdRecoverySigningKey requires username");
|
|
2472
|
+
}
|
|
2473
|
+
const segments = ["dignity-cold-recovery-v1"];
|
|
2474
|
+
if (pepper) {
|
|
2475
|
+
segments.push(pepper);
|
|
2476
|
+
}
|
|
2477
|
+
segments.push(username, COLD_RECOVERY_INFO);
|
|
2478
|
+
return utf8ToBytes(segments.join("\0"));
|
|
2479
|
+
}
|
|
2480
|
+
async function deriveColdRecoverySigningKey({
|
|
2481
|
+
username,
|
|
2482
|
+
coldPassword,
|
|
2483
|
+
pepper = "",
|
|
2484
|
+
kdfIterations
|
|
2485
|
+
} = {}) {
|
|
2486
|
+
if (!coldPassword || typeof coldPassword !== "string") {
|
|
2487
|
+
throw new Error("deriveColdRecoverySigningKey requires coldPassword");
|
|
2488
|
+
}
|
|
2489
|
+
const salt = buildColdRecoverySalt(username, pepper);
|
|
2490
|
+
const iterations = typeof kdfIterations === "number" ? kdfIterations : DEFAULT_SECURITY_OPTIONS.kdfIterations;
|
|
2491
|
+
const seed = await deriveBroadcastKey(coldPassword, salt, iterations);
|
|
2492
|
+
const signing = nacl.sign.keyPair.fromSeed(seed);
|
|
2493
|
+
return {
|
|
2494
|
+
signing,
|
|
2495
|
+
recoveryPublicKey: naclUtil.encodeBase64(signing.publicKey)
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
function buildIdentitySalt(username, info, pepper = "", generation = 1) {
|
|
2499
|
+
if (!username || typeof username !== "string") {
|
|
2500
|
+
throw new Error("deriveKeyPairFromCredentials requires username");
|
|
2501
|
+
}
|
|
2502
|
+
if (!info || typeof info !== "string") {
|
|
2503
|
+
throw new Error("deriveKeyPairFromCredentials requires info label");
|
|
2504
|
+
}
|
|
2505
|
+
const normalizedGeneration = Number(generation);
|
|
2506
|
+
if (!Number.isInteger(normalizedGeneration) || normalizedGeneration < 1) {
|
|
2507
|
+
throw new Error("deriveKeyPairFromCredentials requires generation >= 1");
|
|
2508
|
+
}
|
|
2509
|
+
const segments = ["dignity-identity-v1"];
|
|
2510
|
+
if (pepper) {
|
|
2511
|
+
segments.push(pepper);
|
|
2512
|
+
}
|
|
2513
|
+
segments.push(username, `gen:${normalizedGeneration}`, info);
|
|
2514
|
+
return utf8ToBytes(segments.join("\0"));
|
|
2515
|
+
}
|
|
2516
|
+
async function deriveIdentitySeed({ password, username, info, pepper, generation, kdfIterations }) {
|
|
2517
|
+
if (!password || typeof password !== "string") {
|
|
2518
|
+
throw new Error("deriveKeyPairFromCredentials requires password");
|
|
2519
|
+
}
|
|
2520
|
+
const salt = buildIdentitySalt(username, info, pepper, generation);
|
|
2521
|
+
const iterations = typeof kdfIterations === "number" ? kdfIterations : DEFAULT_SECURITY_OPTIONS.kdfIterations;
|
|
2522
|
+
return deriveBroadcastKey(password, salt, iterations);
|
|
2523
|
+
}
|
|
2524
|
+
async function deriveKeyPairFromCredentials({
|
|
2525
|
+
username,
|
|
2526
|
+
password,
|
|
2527
|
+
pepper = "",
|
|
2528
|
+
generation = 1,
|
|
2529
|
+
kdfIterations
|
|
2530
|
+
} = {}) {
|
|
2531
|
+
const signingSeed = await deriveIdentitySeed({
|
|
2532
|
+
password,
|
|
2533
|
+
username,
|
|
2534
|
+
info: SIGNING_INFO,
|
|
2535
|
+
pepper,
|
|
2536
|
+
generation,
|
|
2537
|
+
kdfIterations
|
|
2538
|
+
});
|
|
2539
|
+
const encryptionSecret = await deriveIdentitySeed({
|
|
2540
|
+
password,
|
|
2541
|
+
username,
|
|
2542
|
+
info: ENCRYPTION_INFO,
|
|
2543
|
+
pepper,
|
|
2544
|
+
generation,
|
|
2545
|
+
kdfIterations
|
|
2546
|
+
});
|
|
2547
|
+
return {
|
|
2548
|
+
signing: nacl.sign.keyPair.fromSeed(signingSeed),
|
|
2549
|
+
encryption: nacl.box.keyPair.fromSecretKey(encryptionSecret),
|
|
2550
|
+
generation
|
|
2551
|
+
};
|
|
2552
|
+
}
|
|
2553
|
+
function keyPairToPublicBundle(keyPair) {
|
|
2554
|
+
return {
|
|
2555
|
+
signingPublicKey: naclUtil.encodeBase64(keyPair.signing.publicKey),
|
|
2556
|
+
encryptionPublicKey: naclUtil.encodeBase64(keyPair.encryption.publicKey)
|
|
2557
|
+
};
|
|
2558
|
+
}
|
|
2559
|
+
module.exports = {
|
|
2560
|
+
deriveKeyPairFromCredentials,
|
|
2561
|
+
deriveColdRecoverySigningKey,
|
|
2562
|
+
keyPairToPublicBundle,
|
|
2563
|
+
buildIdentitySalt,
|
|
2564
|
+
buildColdRecoverySalt,
|
|
2565
|
+
SIGNING_INFO,
|
|
2566
|
+
ENCRYPTION_INFO,
|
|
2567
|
+
COLD_RECOVERY_INFO,
|
|
2568
|
+
concatBytes
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2571
|
+
});
|
|
2572
|
+
|
|
2573
|
+
// src/security/identity-rotation.js
|
|
2574
|
+
var require_identity_rotation = __commonJS({
|
|
2575
|
+
"src/security/identity-rotation.js"(exports, module) {
|
|
2576
|
+
var nacl = require_nacl_fast();
|
|
2577
|
+
var naclUtil = require_nacl_util();
|
|
2578
|
+
var { stableStringify } = require_message_security_service();
|
|
2579
|
+
var {
|
|
2580
|
+
deriveKeyPairFromCredentials,
|
|
2581
|
+
deriveColdRecoverySigningKey,
|
|
2582
|
+
keyPairToPublicBundle
|
|
2583
|
+
} = require_derive_key_pair();
|
|
2584
|
+
var ROTATION_TYPES = /* @__PURE__ */ new Set(["compromise-recovery", "password-change"]);
|
|
2585
|
+
function utf8ToBytes(value) {
|
|
2586
|
+
return naclUtil.decodeUTF8(value);
|
|
2587
|
+
}
|
|
2588
|
+
function normalizePublicKeyBundle(publicKey) {
|
|
2589
|
+
if (!publicKey || !publicKey.signingPublicKey || !publicKey.encryptionPublicKey) {
|
|
2590
|
+
throw new Error("Public key bundle requires signingPublicKey and encryptionPublicKey");
|
|
2591
|
+
}
|
|
2592
|
+
return {
|
|
2593
|
+
signingPublicKey: publicKey.signingPublicKey,
|
|
2594
|
+
encryptionPublicKey: publicKey.encryptionPublicKey
|
|
2595
|
+
};
|
|
2596
|
+
}
|
|
2597
|
+
function buildIdentityRotationPayload({
|
|
2598
|
+
username,
|
|
2599
|
+
fromGeneration,
|
|
2600
|
+
toGeneration,
|
|
2601
|
+
previousPublicKey,
|
|
2602
|
+
nextPublicKey,
|
|
2603
|
+
rotationKind,
|
|
2604
|
+
reason,
|
|
2605
|
+
timestamp
|
|
2606
|
+
}) {
|
|
2607
|
+
if (!username) {
|
|
2608
|
+
throw new Error("Identity rotation requires username");
|
|
2609
|
+
}
|
|
2610
|
+
if (!ROTATION_TYPES.has(rotationKind)) {
|
|
2611
|
+
throw new Error(`Identity rotation kind must be one of: ${[...ROTATION_TYPES].join(", ")}`);
|
|
2612
|
+
}
|
|
2613
|
+
if (toGeneration !== fromGeneration + 1) {
|
|
2614
|
+
throw new Error("Identity rotation must advance generation by exactly 1");
|
|
2615
|
+
}
|
|
2616
|
+
return {
|
|
2617
|
+
version: 1,
|
|
2618
|
+
type: "identity:rotate",
|
|
2619
|
+
username,
|
|
2620
|
+
fromGeneration,
|
|
2621
|
+
toGeneration,
|
|
2622
|
+
previousPublicKey: normalizePublicKeyBundle(previousPublicKey),
|
|
2623
|
+
nextPublicKey: normalizePublicKeyBundle(nextPublicKey),
|
|
2624
|
+
rotationKind,
|
|
2625
|
+
reason: reason || "",
|
|
2626
|
+
timestamp: typeof timestamp === "number" ? timestamp : Date.now()
|
|
2627
|
+
};
|
|
2628
|
+
}
|
|
2629
|
+
function signIdentityRotationPayload(payload, signingSecretKey) {
|
|
2630
|
+
const message = utf8ToBytes(stableStringify(payload));
|
|
2631
|
+
const signature = nacl.sign.detached(message, signingSecretKey);
|
|
2632
|
+
return naclUtil.encodeBase64(signature);
|
|
2633
|
+
}
|
|
2634
|
+
function verifyDetachedSignature(payload, signatureBase64, signingPublicKeyBase64) {
|
|
2635
|
+
const message = utf8ToBytes(stableStringify(payload));
|
|
2636
|
+
const signatureBytes = naclUtil.decodeBase64(signatureBase64);
|
|
2637
|
+
const signingPublicKey = naclUtil.decodeBase64(signingPublicKeyBase64);
|
|
2638
|
+
return nacl.sign.detached.verify(message, signatureBytes, signingPublicKey);
|
|
2639
|
+
}
|
|
2640
|
+
function createIdentityRotation({
|
|
2641
|
+
username,
|
|
2642
|
+
fromGeneration,
|
|
2643
|
+
toGeneration,
|
|
2644
|
+
previousPublicKey,
|
|
2645
|
+
nextKeyPair,
|
|
2646
|
+
rotationKind,
|
|
2647
|
+
reason,
|
|
2648
|
+
timestamp,
|
|
2649
|
+
coldRecoverySigningSecretKey
|
|
2650
|
+
}) {
|
|
2651
|
+
if (!nextKeyPair || !nextKeyPair.signing || !nextKeyPair.signing.secretKey) {
|
|
2652
|
+
throw new Error("Identity rotation requires nextKeyPair with signing secret");
|
|
2653
|
+
}
|
|
2654
|
+
const payload = buildIdentityRotationPayload({
|
|
2655
|
+
username,
|
|
2656
|
+
fromGeneration,
|
|
2657
|
+
toGeneration,
|
|
2658
|
+
previousPublicKey,
|
|
2659
|
+
nextPublicKey: keyPairToPublicBundle(nextKeyPair),
|
|
2660
|
+
rotationKind,
|
|
2661
|
+
reason,
|
|
2662
|
+
timestamp
|
|
2663
|
+
});
|
|
2664
|
+
const rotation = {
|
|
2665
|
+
...payload,
|
|
2666
|
+
signature: signIdentityRotationPayload(payload, nextKeyPair.signing.secretKey)
|
|
2667
|
+
};
|
|
2668
|
+
if (coldRecoverySigningSecretKey) {
|
|
2669
|
+
rotation.recoverySignature = signIdentityRotationPayload(
|
|
2670
|
+
payload,
|
|
2671
|
+
coldRecoverySigningSecretKey
|
|
2672
|
+
);
|
|
2673
|
+
}
|
|
2674
|
+
return rotation;
|
|
2675
|
+
}
|
|
2676
|
+
function buildColdRecoveryEnrollmentPayload({ username, recoveryPublicKey, timestamp }) {
|
|
2677
|
+
if (!username) {
|
|
2678
|
+
throw new Error("Cold recovery enrollment requires username");
|
|
2679
|
+
}
|
|
2680
|
+
if (!recoveryPublicKey) {
|
|
2681
|
+
throw new Error("Cold recovery enrollment requires recoveryPublicKey");
|
|
2682
|
+
}
|
|
2683
|
+
return {
|
|
2684
|
+
version: 1,
|
|
2685
|
+
type: "identity:cold-enroll",
|
|
2686
|
+
username,
|
|
2687
|
+
recoveryPublicKey,
|
|
2688
|
+
timestamp: typeof timestamp === "number" ? timestamp : Date.now()
|
|
2689
|
+
};
|
|
2690
|
+
}
|
|
2691
|
+
function createColdRecoveryEnrollment({
|
|
2692
|
+
username,
|
|
2693
|
+
coldRecoverySigningSecretKey,
|
|
2694
|
+
recoveryPublicKey,
|
|
2695
|
+
timestamp
|
|
2696
|
+
}) {
|
|
2697
|
+
if (!coldRecoverySigningSecretKey) {
|
|
2698
|
+
throw new Error("Cold recovery enrollment requires cold recovery signing secret");
|
|
2699
|
+
}
|
|
2700
|
+
if (!recoveryPublicKey) {
|
|
2701
|
+
throw new Error("Cold recovery enrollment requires recoveryPublicKey");
|
|
2702
|
+
}
|
|
2703
|
+
const payload = buildColdRecoveryEnrollmentPayload({
|
|
2704
|
+
username,
|
|
2705
|
+
recoveryPublicKey,
|
|
2706
|
+
timestamp
|
|
2707
|
+
});
|
|
2708
|
+
return {
|
|
2709
|
+
...payload,
|
|
2710
|
+
signature: signIdentityRotationPayload(payload, coldRecoverySigningSecretKey)
|
|
2711
|
+
};
|
|
2712
|
+
}
|
|
2713
|
+
function verifyColdRecoveryEnrollment(enrollment) {
|
|
2714
|
+
if (!enrollment || enrollment.type !== "identity:cold-enroll" || enrollment.version !== 1) {
|
|
2715
|
+
return { ok: false, error: "invalid-enrollment-shape" };
|
|
2716
|
+
}
|
|
2717
|
+
if (!enrollment.signature || !enrollment.recoveryPublicKey) {
|
|
2718
|
+
return { ok: false, error: "missing-enrollment-fields" };
|
|
2719
|
+
}
|
|
2720
|
+
const { signature, ...payload } = enrollment;
|
|
2721
|
+
const verified = verifyDetachedSignature(payload, signature, enrollment.recoveryPublicKey);
|
|
2722
|
+
if (!verified) {
|
|
2723
|
+
return { ok: false, error: "invalid-enrollment-signature" };
|
|
2724
|
+
}
|
|
2725
|
+
return { ok: true, enrollment };
|
|
2726
|
+
}
|
|
2727
|
+
function verifyIdentityRotation(rotation, options = {}) {
|
|
2728
|
+
if (!rotation || rotation.type !== "identity:rotate" || rotation.version !== 1) {
|
|
2729
|
+
return { ok: false, error: "invalid-rotation-shape" };
|
|
2730
|
+
}
|
|
2731
|
+
if (!rotation.signature || !rotation.nextPublicKey || !rotation.previousPublicKey) {
|
|
2732
|
+
return { ok: false, error: "missing-rotation-fields" };
|
|
2733
|
+
}
|
|
2734
|
+
if (rotation.toGeneration !== rotation.fromGeneration + 1) {
|
|
2735
|
+
return { ok: false, error: "invalid-generation-step" };
|
|
2736
|
+
}
|
|
2737
|
+
if (!ROTATION_TYPES.has(rotation.rotationKind)) {
|
|
2738
|
+
return { ok: false, error: "invalid-rotation-kind" };
|
|
2739
|
+
}
|
|
2740
|
+
const { signature, recoverySignature, ...payload } = rotation;
|
|
2741
|
+
const verified = verifyDetachedSignature(payload, signature, rotation.nextPublicKey.signingPublicKey);
|
|
2742
|
+
if (!verified) {
|
|
2743
|
+
return { ok: false, error: "invalid-signature" };
|
|
2744
|
+
}
|
|
2745
|
+
const requiredRecoveryPublicKey = options.requiredRecoveryPublicKey || null;
|
|
2746
|
+
if (requiredRecoveryPublicKey) {
|
|
2747
|
+
if (!recoverySignature) {
|
|
2748
|
+
return { ok: false, error: "missing-recovery-signature" };
|
|
2749
|
+
}
|
|
2750
|
+
const recoveryVerified = verifyDetachedSignature(
|
|
2751
|
+
payload,
|
|
2752
|
+
recoverySignature,
|
|
2753
|
+
requiredRecoveryPublicKey
|
|
2754
|
+
);
|
|
2755
|
+
if (!recoveryVerified) {
|
|
2756
|
+
return { ok: false, error: "invalid-recovery-signature" };
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
if (rotation.previousPublicKey.signingPublicKey === rotation.nextPublicKey.signingPublicKey) {
|
|
2760
|
+
return { ok: false, error: "unchanged-signing-key" };
|
|
2761
|
+
}
|
|
2762
|
+
return { ok: true, rotation };
|
|
2763
|
+
}
|
|
2764
|
+
async function resolveColdRecoverySigningSecretKey({
|
|
2765
|
+
username,
|
|
2766
|
+
coldPassword,
|
|
2767
|
+
pepper,
|
|
2768
|
+
kdfIterations
|
|
2769
|
+
}) {
|
|
2770
|
+
if (!coldPassword) {
|
|
2771
|
+
return null;
|
|
2772
|
+
}
|
|
2773
|
+
const coldRecovery = await deriveColdRecoverySigningKey({
|
|
2774
|
+
username,
|
|
2775
|
+
coldPassword,
|
|
2776
|
+
pepper,
|
|
2777
|
+
kdfIterations
|
|
2778
|
+
});
|
|
2779
|
+
return coldRecovery.signing.secretKey;
|
|
2780
|
+
}
|
|
2781
|
+
async function revokeAndRotateIdentity({
|
|
2782
|
+
username,
|
|
2783
|
+
password,
|
|
2784
|
+
coldPassword,
|
|
2785
|
+
currentGeneration = 1,
|
|
2786
|
+
reason = "compromise-recovery",
|
|
2787
|
+
pepper = "",
|
|
2788
|
+
kdfIterations,
|
|
2789
|
+
timestamp
|
|
2790
|
+
} = {}) {
|
|
2791
|
+
const currentKeyPair = await deriveKeyPairFromCredentials({
|
|
2792
|
+
username,
|
|
2793
|
+
password,
|
|
2794
|
+
generation: currentGeneration,
|
|
2795
|
+
pepper,
|
|
2796
|
+
kdfIterations
|
|
2797
|
+
});
|
|
2798
|
+
const nextGeneration = currentGeneration + 1;
|
|
2799
|
+
const nextKeyPair = await deriveKeyPairFromCredentials({
|
|
2800
|
+
username,
|
|
2801
|
+
password,
|
|
2802
|
+
generation: nextGeneration,
|
|
2803
|
+
pepper,
|
|
2804
|
+
kdfIterations
|
|
2805
|
+
});
|
|
2806
|
+
const coldRecoverySigningSecretKey = await resolveColdRecoverySigningSecretKey({
|
|
2807
|
+
username,
|
|
2808
|
+
coldPassword,
|
|
2809
|
+
pepper,
|
|
2810
|
+
kdfIterations
|
|
2811
|
+
});
|
|
2812
|
+
const rotation = createIdentityRotation({
|
|
2813
|
+
username,
|
|
2814
|
+
fromGeneration: currentGeneration,
|
|
2815
|
+
toGeneration: nextGeneration,
|
|
2816
|
+
previousPublicKey: keyPairToPublicBundle(currentKeyPair),
|
|
2817
|
+
nextKeyPair,
|
|
2818
|
+
rotationKind: "compromise-recovery",
|
|
2819
|
+
reason,
|
|
2820
|
+
timestamp,
|
|
2821
|
+
coldRecoverySigningSecretKey
|
|
2822
|
+
});
|
|
2823
|
+
return {
|
|
2824
|
+
rotation,
|
|
2825
|
+
currentKeyPair,
|
|
2826
|
+
nextKeyPair,
|
|
2827
|
+
nextGeneration,
|
|
2828
|
+
coldRecoveryUsed: Boolean(coldRecoverySigningSecretKey)
|
|
2829
|
+
};
|
|
2830
|
+
}
|
|
2831
|
+
async function rotateIdentityPassword({
|
|
2832
|
+
username,
|
|
2833
|
+
currentPassword,
|
|
2834
|
+
newPassword,
|
|
2835
|
+
coldPassword,
|
|
2836
|
+
currentGeneration = 1,
|
|
2837
|
+
reason = "password-change",
|
|
2838
|
+
pepper = "",
|
|
2839
|
+
kdfIterations,
|
|
2840
|
+
timestamp
|
|
2841
|
+
} = {}) {
|
|
2842
|
+
const currentKeyPair = await deriveKeyPairFromCredentials({
|
|
2843
|
+
username,
|
|
2844
|
+
password: currentPassword,
|
|
2845
|
+
generation: currentGeneration,
|
|
2846
|
+
pepper,
|
|
2847
|
+
kdfIterations
|
|
2848
|
+
});
|
|
2849
|
+
const nextGeneration = currentGeneration + 1;
|
|
2850
|
+
const nextKeyPair = await deriveKeyPairFromCredentials({
|
|
2851
|
+
username,
|
|
2852
|
+
password: newPassword,
|
|
2853
|
+
generation: nextGeneration,
|
|
2854
|
+
pepper,
|
|
2855
|
+
kdfIterations
|
|
2856
|
+
});
|
|
2857
|
+
const coldRecoverySigningSecretKey = await resolveColdRecoverySigningSecretKey({
|
|
2858
|
+
username,
|
|
2859
|
+
coldPassword,
|
|
2860
|
+
pepper,
|
|
2861
|
+
kdfIterations
|
|
2862
|
+
});
|
|
2863
|
+
const rotation = createIdentityRotation({
|
|
2864
|
+
username,
|
|
2865
|
+
fromGeneration: currentGeneration,
|
|
2866
|
+
toGeneration: nextGeneration,
|
|
2867
|
+
previousPublicKey: keyPairToPublicBundle(currentKeyPair),
|
|
2868
|
+
nextKeyPair,
|
|
2869
|
+
rotationKind: "password-change",
|
|
2870
|
+
reason,
|
|
2871
|
+
timestamp,
|
|
2872
|
+
coldRecoverySigningSecretKey
|
|
2873
|
+
});
|
|
2874
|
+
return {
|
|
2875
|
+
rotation,
|
|
2876
|
+
currentKeyPair,
|
|
2877
|
+
nextKeyPair,
|
|
2878
|
+
nextGeneration,
|
|
2879
|
+
coldRecoveryUsed: Boolean(coldRecoverySigningSecretKey)
|
|
2880
|
+
};
|
|
2881
|
+
}
|
|
2882
|
+
async function enrollColdRecoveryPassword({
|
|
2883
|
+
username,
|
|
2884
|
+
coldPassword,
|
|
2885
|
+
pepper = "",
|
|
2886
|
+
kdfIterations,
|
|
2887
|
+
timestamp
|
|
2888
|
+
} = {}) {
|
|
2889
|
+
const coldRecovery = await deriveColdRecoverySigningKey({
|
|
2890
|
+
username,
|
|
2891
|
+
coldPassword,
|
|
2892
|
+
pepper,
|
|
2893
|
+
kdfIterations
|
|
2894
|
+
});
|
|
2895
|
+
const enrollment = createColdRecoveryEnrollment({
|
|
2896
|
+
username,
|
|
2897
|
+
coldRecoverySigningSecretKey: coldRecovery.signing.secretKey,
|
|
2898
|
+
recoveryPublicKey: coldRecovery.recoveryPublicKey,
|
|
2899
|
+
timestamp
|
|
2900
|
+
});
|
|
2901
|
+
return {
|
|
2902
|
+
enrollment,
|
|
2903
|
+
recoveryPublicKey: coldRecovery.recoveryPublicKey,
|
|
2904
|
+
coldRecovery
|
|
2905
|
+
};
|
|
2906
|
+
}
|
|
2907
|
+
function shouldApplyIdentityRotation(currentState, rotation, options = {}) {
|
|
2908
|
+
const requiredRecoveryPublicKey = options.enrolledRecoveryPublicKey || currentState && currentState.recoveryPublicKey || null;
|
|
2909
|
+
const verified = verifyIdentityRotation(rotation, { requiredRecoveryPublicKey });
|
|
2910
|
+
if (!verified.ok) {
|
|
2911
|
+
return { apply: false, reason: verified.error };
|
|
2912
|
+
}
|
|
2913
|
+
if (currentState && rotation.toGeneration <= currentState.generation) {
|
|
2914
|
+
return { apply: false, reason: "stale-generation" };
|
|
2915
|
+
}
|
|
2916
|
+
if (currentState && currentState.publicKey && currentState.publicKey.signingPublicKey !== rotation.previousPublicKey.signingPublicKey) {
|
|
2917
|
+
return { apply: false, reason: "previous-key-mismatch" };
|
|
2918
|
+
}
|
|
2919
|
+
return { apply: true, rotation: verified.rotation };
|
|
2920
|
+
}
|
|
2921
|
+
module.exports = {
|
|
2922
|
+
createIdentityRotation,
|
|
2923
|
+
createColdRecoveryEnrollment,
|
|
2924
|
+
verifyIdentityRotation,
|
|
2925
|
+
verifyColdRecoveryEnrollment,
|
|
2926
|
+
revokeAndRotateIdentity,
|
|
2927
|
+
rotateIdentityPassword,
|
|
2928
|
+
enrollColdRecoveryPassword,
|
|
2929
|
+
shouldApplyIdentityRotation,
|
|
2930
|
+
keyPairToPublicBundle,
|
|
2931
|
+
buildIdentityRotationPayload,
|
|
2932
|
+
buildColdRecoveryEnrollmentPayload,
|
|
2933
|
+
signIdentityRotationPayload
|
|
2934
|
+
};
|
|
2935
|
+
}
|
|
2936
|
+
});
|
|
2937
|
+
|
|
2447
2938
|
// src/security/message-security-service.js
|
|
2448
2939
|
var require_message_security_service = __commonJS({
|
|
2449
2940
|
"src/security/message-security-service.js"(exports, module) {
|
|
2450
2941
|
var nacl = require_nacl_fast();
|
|
2451
2942
|
var naclUtil = require_nacl_util();
|
|
2452
2943
|
var VDF = require_vdf();
|
|
2944
|
+
var DEFAULT_APP_PASSWORD = "change-this-app-password";
|
|
2453
2945
|
var DEFAULT_SECURITY_OPTIONS = {
|
|
2454
2946
|
enabled: true,
|
|
2455
2947
|
signingEnabled: true,
|
|
2456
2948
|
encryptionEnabled: true,
|
|
2457
2949
|
powEnabled: true,
|
|
2458
2950
|
powTargetMs: 1e3,
|
|
2459
|
-
appPassword:
|
|
2951
|
+
appPassword: DEFAULT_APP_PASSWORD,
|
|
2460
2952
|
broadcastPasswords: {},
|
|
2461
2953
|
resolveBroadcastPassword: null,
|
|
2462
2954
|
powSteps: 22,
|
|
@@ -2551,16 +3043,85 @@ var require_message_security_service = __commonJS({
|
|
|
2551
3043
|
encryptionPublicKey: naclUtil.encodeBase64(this.encryptionPublicKey)
|
|
2552
3044
|
};
|
|
2553
3045
|
this.peerPublicKeys = /* @__PURE__ */ new Map();
|
|
3046
|
+
this.peerIdentityGenerations = /* @__PURE__ */ new Map();
|
|
3047
|
+
this.peerRecoveryPublicKeys = /* @__PURE__ */ new Map();
|
|
2554
3048
|
for (const [peerId, peerKey] of Object.entries(this.options.trustedPeerKeys || {})) {
|
|
2555
3049
|
this.peerPublicKeys.set(peerId, normalizePeerPublicKey(peerKey));
|
|
3050
|
+
this.peerIdentityGenerations.set(peerId, 1);
|
|
2556
3051
|
}
|
|
2557
3052
|
this.calibratedPowSteps = this.options.powSteps;
|
|
2558
3053
|
}
|
|
2559
3054
|
getPublicKey() {
|
|
2560
3055
|
return { ...this.publicKeyBundle };
|
|
2561
3056
|
}
|
|
2562
|
-
registerPeerPublicKey(peerId, publicKey) {
|
|
2563
|
-
|
|
3057
|
+
registerPeerPublicKey(peerId, publicKey, options = {}) {
|
|
3058
|
+
const normalized = normalizePeerPublicKey(publicKey);
|
|
3059
|
+
const generation = typeof options.generation === "number" ? options.generation : 1;
|
|
3060
|
+
const currentGeneration = this.peerIdentityGenerations.get(peerId) || 0;
|
|
3061
|
+
if (generation < currentGeneration && options.allowDowngrade !== true) {
|
|
3062
|
+
throw new Error(`Refusing older identity generation for peer ${peerId}`);
|
|
3063
|
+
}
|
|
3064
|
+
this.peerPublicKeys.set(peerId, normalized);
|
|
3065
|
+
this.peerIdentityGenerations.set(peerId, Math.max(currentGeneration, generation));
|
|
3066
|
+
}
|
|
3067
|
+
getPeerIdentityGeneration(peerId) {
|
|
3068
|
+
return this.peerIdentityGenerations.get(peerId) || 0;
|
|
3069
|
+
}
|
|
3070
|
+
getPeerIdentityState(peerId) {
|
|
3071
|
+
const publicKey = this.peerPublicKeys.get(peerId);
|
|
3072
|
+
const recoveryPublicKey = this.peerRecoveryPublicKeys.get(peerId) || null;
|
|
3073
|
+
if (!publicKey && !recoveryPublicKey) {
|
|
3074
|
+
return null;
|
|
3075
|
+
}
|
|
3076
|
+
return {
|
|
3077
|
+
publicKey: publicKey ? { ...publicKey } : null,
|
|
3078
|
+
generation: this.getPeerIdentityGeneration(peerId),
|
|
3079
|
+
recoveryPublicKey
|
|
3080
|
+
};
|
|
3081
|
+
}
|
|
3082
|
+
registerPeerRecoveryPublicKey(peerId, recoveryPublicKey) {
|
|
3083
|
+
if (!peerId || !recoveryPublicKey) {
|
|
3084
|
+
throw new Error("registerPeerRecoveryPublicKey requires peerId and recoveryPublicKey");
|
|
3085
|
+
}
|
|
3086
|
+
this.peerRecoveryPublicKeys.set(peerId, recoveryPublicKey);
|
|
3087
|
+
}
|
|
3088
|
+
getPeerRecoveryPublicKey(peerId) {
|
|
3089
|
+
return this.peerRecoveryPublicKeys.get(peerId) || null;
|
|
3090
|
+
}
|
|
3091
|
+
applyColdRecoveryEnrollment(peerId, enrollment) {
|
|
3092
|
+
const { verifyColdRecoveryEnrollment } = require_identity_rotation();
|
|
3093
|
+
const verified = verifyColdRecoveryEnrollment(enrollment);
|
|
3094
|
+
if (!verified.ok) {
|
|
3095
|
+
const error = new Error(`Invalid cold recovery enrollment: ${verified.error}`);
|
|
3096
|
+
error.code = "INVALID_COLD_RECOVERY_ENROLLMENT";
|
|
3097
|
+
throw error;
|
|
3098
|
+
}
|
|
3099
|
+
this.registerPeerRecoveryPublicKey(peerId, enrollment.recoveryPublicKey);
|
|
3100
|
+
return { applied: true, recoveryPublicKey: enrollment.recoveryPublicKey };
|
|
3101
|
+
}
|
|
3102
|
+
applyIdentityRotation(peerId, rotation) {
|
|
3103
|
+
const { shouldApplyIdentityRotation } = require_identity_rotation();
|
|
3104
|
+
const currentState = this.getPeerIdentityState(peerId);
|
|
3105
|
+
const decision = shouldApplyIdentityRotation(currentState, rotation, {
|
|
3106
|
+
enrolledRecoveryPublicKey: this.getPeerRecoveryPublicKey(peerId)
|
|
3107
|
+
});
|
|
3108
|
+
if (!decision.apply) {
|
|
3109
|
+
if (decision.reason && decision.reason !== "stale-generation" && decision.reason !== "previous-key-mismatch") {
|
|
3110
|
+
const error = new Error(`Invalid identity rotation: ${decision.reason}`);
|
|
3111
|
+
error.code = "INVALID_IDENTITY_ROTATION";
|
|
3112
|
+
throw error;
|
|
3113
|
+
}
|
|
3114
|
+
return { applied: false, reason: decision.reason };
|
|
3115
|
+
}
|
|
3116
|
+
this.registerPeerPublicKey(peerId, rotation.nextPublicKey, {
|
|
3117
|
+
generation: rotation.toGeneration
|
|
3118
|
+
});
|
|
3119
|
+
return {
|
|
3120
|
+
applied: true,
|
|
3121
|
+
fromGeneration: rotation.fromGeneration,
|
|
3122
|
+
toGeneration: rotation.toGeneration,
|
|
3123
|
+
rotationKind: rotation.rotationKind
|
|
3124
|
+
};
|
|
2564
3125
|
}
|
|
2565
3126
|
resolvePeerPublicKey(peerId, fallbackPublicKey) {
|
|
2566
3127
|
const trusted = this.peerPublicKeys.get(peerId);
|
|
@@ -2769,8 +3330,14 @@ var require_message_security_service = __commonJS({
|
|
|
2769
3330
|
const nonce = naclUtil.decodeBase64(encryption.nonce);
|
|
2770
3331
|
let key;
|
|
2771
3332
|
if (encryption.kdf === "pbkdf2") {
|
|
2772
|
-
const
|
|
2773
|
-
|
|
3333
|
+
const configuredIterations = this.options.kdfIterations || DEFAULT_SECURITY_OPTIONS.kdfIterations;
|
|
3334
|
+
const requestedIterations = encryption.kdfIterations || configuredIterations;
|
|
3335
|
+
const minIterations = Math.max(1e3, Math.floor(configuredIterations * 0.1));
|
|
3336
|
+
const maxIterations = configuredIterations * 2;
|
|
3337
|
+
if (requestedIterations < minIterations || requestedIterations > maxIterations) {
|
|
3338
|
+
throw new Error(`Invalid kdfIterations: ${requestedIterations}`);
|
|
3339
|
+
}
|
|
3340
|
+
key = await deriveBroadcastKey(password, salt, requestedIterations);
|
|
2774
3341
|
} else {
|
|
2775
3342
|
key = legacyBroadcastKey(password, salt);
|
|
2776
3343
|
}
|
|
@@ -2869,7 +3436,8 @@ var require_message_security_service = __commonJS({
|
|
|
2869
3436
|
stableStringify,
|
|
2870
3437
|
deriveBroadcastKey,
|
|
2871
3438
|
legacyBroadcastKey,
|
|
2872
|
-
DEFAULT_SECURITY_OPTIONS
|
|
3439
|
+
DEFAULT_SECURITY_OPTIONS,
|
|
3440
|
+
DEFAULT_APP_PASSWORD
|
|
2873
3441
|
};
|
|
2874
3442
|
}
|
|
2875
3443
|
});
|
|
@@ -2939,7 +3507,17 @@ var require_dignity_p2p = __commonJS({
|
|
|
2939
3507
|
var nacl = require_nacl_fast();
|
|
2940
3508
|
var naclUtil = require_nacl_util();
|
|
2941
3509
|
var EventEmitter = require_event_emitter();
|
|
2942
|
-
var {
|
|
3510
|
+
var {
|
|
3511
|
+
MessageSecurityService,
|
|
3512
|
+
stableStringify,
|
|
3513
|
+
DEFAULT_APP_PASSWORD
|
|
3514
|
+
} = require_message_security_service();
|
|
3515
|
+
var {
|
|
3516
|
+
revokeAndRotateIdentity,
|
|
3517
|
+
rotateIdentityPassword,
|
|
3518
|
+
enrollColdRecoveryPassword
|
|
3519
|
+
} = require_identity_rotation();
|
|
3520
|
+
var { deriveKeyPairFromCredentials } = require_derive_key_pair();
|
|
2943
3521
|
var {
|
|
2944
3522
|
DEFAULT_PEER_GROUP_OPTIONS,
|
|
2945
3523
|
peerGroupScope,
|
|
@@ -2984,13 +3562,24 @@ var require_dignity_p2p = __commonJS({
|
|
|
2984
3562
|
this.defaultGossipMaxHops = security && typeof security.gossipMaxHops === "number" ? security.gossipMaxHops : DEFAULT_PEER_GROUP_OPTIONS.maxHops;
|
|
2985
3563
|
this.globalMaxOpenConnections = security && typeof security.globalMaxOpenConnections === "number" ? security.globalMaxOpenConnections : 32;
|
|
2986
3564
|
this.gossipIdTtlMs = security && typeof security.gossipIdTtlMs === "number" ? security.gossipIdTtlMs : 5 * 60 * 1e3;
|
|
3565
|
+
this.maxSeenGossipIds = security && typeof security.maxSeenGossipIds === "number" ? security.maxSeenGossipIds : 1e5;
|
|
3566
|
+
this.gossipPublishMinIntervalMs = security && typeof security.gossipPublishMinIntervalMs === "number" ? security.gossipPublishMinIntervalMs : 0;
|
|
3567
|
+
this.lastGossipPublishAt = /* @__PURE__ */ new Map();
|
|
3568
|
+
this.maxAppliedOperations = security && typeof security.maxAppliedOperations === "number" ? security.maxAppliedOperations : 5e4;
|
|
2987
3569
|
this.state = /* @__PURE__ */ new Map();
|
|
2988
|
-
this.appliedOperations = /* @__PURE__ */ new
|
|
3570
|
+
this.appliedOperations = /* @__PURE__ */ new Map();
|
|
2989
3571
|
this.boundMessageHandler = this.handleIncomingMessage.bind(this);
|
|
2990
3572
|
}
|
|
2991
3573
|
async start() {
|
|
2992
3574
|
this.networkAdapter.onMessage(this.boundMessageHandler);
|
|
2993
3575
|
await this.networkAdapter.start(this.nodeId);
|
|
3576
|
+
const appPassword = this.securityService.options.appPassword;
|
|
3577
|
+
if (!appPassword || appPassword === DEFAULT_APP_PASSWORD) {
|
|
3578
|
+
this.emit("warning", {
|
|
3579
|
+
type: "default-app-password",
|
|
3580
|
+
message: "Using the default appPassword is insecure; set a strong shared secret in production."
|
|
3581
|
+
});
|
|
3582
|
+
}
|
|
2994
3583
|
}
|
|
2995
3584
|
async stop() {
|
|
2996
3585
|
const joinedGroups = Array.from(this.peerGroups.keys());
|
|
@@ -3286,8 +3875,138 @@ var require_dignity_p2p = __commonJS({
|
|
|
3286
3875
|
connectToPeers: this.resolveReplicationPeers(collectionName, id, options, { fromRecord: existing })
|
|
3287
3876
|
});
|
|
3288
3877
|
}
|
|
3289
|
-
registerPeerPublicKey(peerId, publicKey) {
|
|
3290
|
-
this.securityService.registerPeerPublicKey(peerId, publicKey);
|
|
3878
|
+
registerPeerPublicKey(peerId, publicKey, options = {}) {
|
|
3879
|
+
this.securityService.registerPeerPublicKey(peerId, publicKey, options);
|
|
3880
|
+
}
|
|
3881
|
+
getPeerIdentityGeneration(peerId) {
|
|
3882
|
+
return this.securityService.getPeerIdentityGeneration(peerId);
|
|
3883
|
+
}
|
|
3884
|
+
getPeerIdentityState(peerId) {
|
|
3885
|
+
return this.securityService.getPeerIdentityState(peerId);
|
|
3886
|
+
}
|
|
3887
|
+
applyPeerIdentityRotation(peerId, rotation) {
|
|
3888
|
+
const result = this.securityService.applyIdentityRotation(peerId, rotation);
|
|
3889
|
+
if (result.applied) {
|
|
3890
|
+
this.emit("identityrotated", {
|
|
3891
|
+
peerId,
|
|
3892
|
+
username: rotation.username,
|
|
3893
|
+
fromGeneration: result.fromGeneration,
|
|
3894
|
+
toGeneration: result.toGeneration,
|
|
3895
|
+
rotationKind: result.rotationKind
|
|
3896
|
+
});
|
|
3897
|
+
}
|
|
3898
|
+
return result;
|
|
3899
|
+
}
|
|
3900
|
+
async broadcastIdentityRotation(rotation, options = {}) {
|
|
3901
|
+
return this.broadcastMessage("identity:rotate", rotation, options);
|
|
3902
|
+
}
|
|
3903
|
+
async broadcastColdRecoveryEnrollment(enrollment, options = {}) {
|
|
3904
|
+
return this.broadcastMessage("identity:cold-enroll", enrollment, options);
|
|
3905
|
+
}
|
|
3906
|
+
applyPeerColdRecoveryEnrollment(peerId, enrollment) {
|
|
3907
|
+
const result = this.securityService.applyColdRecoveryEnrollment(peerId, enrollment);
|
|
3908
|
+
if (result.applied) {
|
|
3909
|
+
this.emit("coldrecoveryenrolled", {
|
|
3910
|
+
peerId,
|
|
3911
|
+
username: enrollment.username,
|
|
3912
|
+
recoveryPublicKey: enrollment.recoveryPublicKey
|
|
3913
|
+
});
|
|
3914
|
+
}
|
|
3915
|
+
return result;
|
|
3916
|
+
}
|
|
3917
|
+
async enrollAndBroadcastColdRecovery({
|
|
3918
|
+
username,
|
|
3919
|
+
coldPassword,
|
|
3920
|
+
pepper = "",
|
|
3921
|
+
kdfIterations,
|
|
3922
|
+
broadcastOptions = {}
|
|
3923
|
+
} = {}) {
|
|
3924
|
+
const result = await enrollColdRecoveryPassword({
|
|
3925
|
+
username,
|
|
3926
|
+
coldPassword,
|
|
3927
|
+
pepper,
|
|
3928
|
+
kdfIterations
|
|
3929
|
+
});
|
|
3930
|
+
await this.broadcastColdRecoveryEnrollment(result.enrollment, broadcastOptions);
|
|
3931
|
+
return result;
|
|
3932
|
+
}
|
|
3933
|
+
async revokeAndRotateDerivedIdentity({
|
|
3934
|
+
username,
|
|
3935
|
+
password,
|
|
3936
|
+
coldPassword,
|
|
3937
|
+
currentGeneration = 1,
|
|
3938
|
+
reason = "compromise-recovery",
|
|
3939
|
+
pepper = "",
|
|
3940
|
+
kdfIterations,
|
|
3941
|
+
broadcast = false,
|
|
3942
|
+
broadcastOptions = {}
|
|
3943
|
+
} = {}) {
|
|
3944
|
+
const result = await revokeAndRotateIdentity({
|
|
3945
|
+
username,
|
|
3946
|
+
password,
|
|
3947
|
+
coldPassword,
|
|
3948
|
+
currentGeneration,
|
|
3949
|
+
reason,
|
|
3950
|
+
pepper,
|
|
3951
|
+
kdfIterations
|
|
3952
|
+
});
|
|
3953
|
+
if (broadcast) {
|
|
3954
|
+
await this.broadcastIdentityRotation(result.rotation, broadcastOptions);
|
|
3955
|
+
}
|
|
3956
|
+
return result;
|
|
3957
|
+
}
|
|
3958
|
+
async rotateDerivedIdentityPassword({
|
|
3959
|
+
username,
|
|
3960
|
+
currentPassword,
|
|
3961
|
+
newPassword,
|
|
3962
|
+
coldPassword,
|
|
3963
|
+
currentGeneration = 1,
|
|
3964
|
+
reason = "password-change",
|
|
3965
|
+
pepper = "",
|
|
3966
|
+
kdfIterations,
|
|
3967
|
+
broadcast = false,
|
|
3968
|
+
broadcastOptions = {}
|
|
3969
|
+
} = {}) {
|
|
3970
|
+
const result = await rotateIdentityPassword({
|
|
3971
|
+
username,
|
|
3972
|
+
currentPassword,
|
|
3973
|
+
newPassword,
|
|
3974
|
+
coldPassword,
|
|
3975
|
+
currentGeneration,
|
|
3976
|
+
reason,
|
|
3977
|
+
pepper,
|
|
3978
|
+
kdfIterations
|
|
3979
|
+
});
|
|
3980
|
+
if (broadcast) {
|
|
3981
|
+
await this.broadcastIdentityRotation(result.rotation, broadcastOptions);
|
|
3982
|
+
}
|
|
3983
|
+
return result;
|
|
3984
|
+
}
|
|
3985
|
+
async adoptDerivedIdentityKeyPair(keyPair, { generation = 1 } = {}) {
|
|
3986
|
+
if (!keyPair || !keyPair.signing || !keyPair.encryption) {
|
|
3987
|
+
throw new Error("adoptDerivedIdentityKeyPair requires a derived keyPair");
|
|
3988
|
+
}
|
|
3989
|
+
this.securityService.signingSecretKey = keyPair.signing.secretKey;
|
|
3990
|
+
this.securityService.signingPublicKey = keyPair.signing.publicKey;
|
|
3991
|
+
this.securityService.encryptionSecretKey = keyPair.encryption.secretKey;
|
|
3992
|
+
this.securityService.encryptionPublicKey = keyPair.encryption.publicKey;
|
|
3993
|
+
this.securityService.publicKeyBundle = {
|
|
3994
|
+
signingPublicKey: naclUtil.encodeBase64(keyPair.signing.publicKey),
|
|
3995
|
+
encryptionPublicKey: naclUtil.encodeBase64(keyPair.encryption.publicKey)
|
|
3996
|
+
};
|
|
3997
|
+
this.securityService.options.keyPair = keyPair;
|
|
3998
|
+
this.securityService.options.identityGeneration = generation;
|
|
3999
|
+
}
|
|
4000
|
+
async deriveAndAdoptIdentity({ username, password, generation = 1, pepper = "", kdfIterations } = {}) {
|
|
4001
|
+
const keyPair = await deriveKeyPairFromCredentials({
|
|
4002
|
+
username,
|
|
4003
|
+
password,
|
|
4004
|
+
generation,
|
|
4005
|
+
pepper,
|
|
4006
|
+
kdfIterations
|
|
4007
|
+
});
|
|
4008
|
+
await this.adoptDerivedIdentityKeyPair(keyPair, { generation });
|
|
4009
|
+
return keyPair;
|
|
3291
4010
|
}
|
|
3292
4011
|
trustPeerPublicKey(peerId, publicKey) {
|
|
3293
4012
|
if (!peerId || !publicKey) {
|
|
@@ -3409,6 +4128,13 @@ var require_dignity_p2p = __commonJS({
|
|
|
3409
4128
|
this.seenGossipIds.delete(gossipId);
|
|
3410
4129
|
}
|
|
3411
4130
|
}
|
|
4131
|
+
while (this.seenGossipIds.size > this.maxSeenGossipIds) {
|
|
4132
|
+
const oldestGossipId = this.seenGossipIds.keys().next().value;
|
|
4133
|
+
if (!oldestGossipId) {
|
|
4134
|
+
break;
|
|
4135
|
+
}
|
|
4136
|
+
this.seenGossipIds.delete(oldestGossipId);
|
|
4137
|
+
}
|
|
3412
4138
|
}
|
|
3413
4139
|
hasSeenGossip(gossipId) {
|
|
3414
4140
|
if (!gossipId) {
|
|
@@ -3422,6 +4148,7 @@ var require_dignity_p2p = __commonJS({
|
|
|
3422
4148
|
return;
|
|
3423
4149
|
}
|
|
3424
4150
|
this.seenGossipIds.set(gossipId, this.now() + this.gossipIdTtlMs);
|
|
4151
|
+
this.pruneSeenGossip();
|
|
3425
4152
|
}
|
|
3426
4153
|
listConnectedPeerIds() {
|
|
3427
4154
|
if (typeof this.networkAdapter.listOpenPeerIds === "function") {
|
|
@@ -3499,6 +4226,15 @@ var require_dignity_p2p = __commonJS({
|
|
|
3499
4226
|
if (!group && options.allowUnjoined !== true) {
|
|
3500
4227
|
throw new Error(`PeerGroup ${groupId} has not been joined`);
|
|
3501
4228
|
}
|
|
4229
|
+
if (this.gossipPublishMinIntervalMs > 0) {
|
|
4230
|
+
const lastPublishAt = this.lastGossipPublishAt.get(groupId) || 0;
|
|
4231
|
+
const elapsed = this.now() - lastPublishAt;
|
|
4232
|
+
if (elapsed < this.gossipPublishMinIntervalMs) {
|
|
4233
|
+
const error = new Error(`Gossip publish rate limit exceeded for group ${groupId}`);
|
|
4234
|
+
error.code = "GOSSIP_RATE_LIMIT";
|
|
4235
|
+
throw error;
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
3502
4238
|
const fanout = typeof options.fanout === "number" ? options.fanout : group ? group.fanout : this.defaultPeerGroupFanout;
|
|
3503
4239
|
const maxActivePeers = group ? group.maxActivePeers : this.defaultPeerGroupMaxActivePeers;
|
|
3504
4240
|
const maxHop = typeof options.maxHops === "number" ? options.maxHops : group ? group.maxHops : this.defaultGossipMaxHops;
|
|
@@ -3509,9 +4245,11 @@ var require_dignity_p2p = __commonJS({
|
|
|
3509
4245
|
}
|
|
3510
4246
|
const gossipId = options.gossipId || this.idGenerator();
|
|
3511
4247
|
this.markSeenGossip(gossipId);
|
|
4248
|
+
this.lastGossipPublishAt.set(groupId, this.now());
|
|
3512
4249
|
await this.broadcastMessage("peer-group:gossip", {
|
|
3513
4250
|
groupId,
|
|
3514
4251
|
gossipId,
|
|
4252
|
+
publisherId: this.nodeId,
|
|
3515
4253
|
hop: 0,
|
|
3516
4254
|
maxHop,
|
|
3517
4255
|
innerMessageType,
|
|
@@ -3539,12 +4277,16 @@ var require_dignity_p2p = __commonJS({
|
|
|
3539
4277
|
const {
|
|
3540
4278
|
groupId,
|
|
3541
4279
|
gossipId,
|
|
4280
|
+
publisherId = decrypted.senderId,
|
|
3542
4281
|
hop = 0,
|
|
3543
|
-
maxHop
|
|
4282
|
+
maxHop: payloadMaxHop,
|
|
3544
4283
|
innerMessageType,
|
|
3545
4284
|
innerPayload
|
|
3546
4285
|
} = payload;
|
|
3547
|
-
if (!groupId || !innerMessageType) {
|
|
4286
|
+
if (!groupId || !innerMessageType || !gossipId) {
|
|
4287
|
+
return;
|
|
4288
|
+
}
|
|
4289
|
+
if (!this.peerGroups.has(groupId)) {
|
|
3548
4290
|
return;
|
|
3549
4291
|
}
|
|
3550
4292
|
if (this.hasSeenGossip(gossipId)) {
|
|
@@ -3553,9 +4295,12 @@ var require_dignity_p2p = __commonJS({
|
|
|
3553
4295
|
this.markSeenGossip(gossipId);
|
|
3554
4296
|
await this.dispatchPeerGroupInnerMessage(innerMessageType, innerPayload, {
|
|
3555
4297
|
groupId,
|
|
3556
|
-
senderId: decrypted.senderId
|
|
4298
|
+
senderId: decrypted.senderId,
|
|
4299
|
+
publisherId
|
|
3557
4300
|
});
|
|
3558
4301
|
const group = this.peerGroups.get(groupId);
|
|
4302
|
+
const configuredMaxHop = group ? group.maxHops : this.defaultGossipMaxHops;
|
|
4303
|
+
const maxHop = typeof payloadMaxHop === "number" ? Math.min(payloadMaxHop, configuredMaxHop) : configuredMaxHop;
|
|
3559
4304
|
if (!group || group.relayEnabled === false || hop >= maxHop) {
|
|
3560
4305
|
return;
|
|
3561
4306
|
}
|
|
@@ -3571,6 +4316,7 @@ var require_dignity_p2p = __commonJS({
|
|
|
3571
4316
|
await this.broadcastMessage("peer-group:gossip", {
|
|
3572
4317
|
groupId,
|
|
3573
4318
|
gossipId,
|
|
4319
|
+
publisherId,
|
|
3574
4320
|
hop: hop + 1,
|
|
3575
4321
|
maxHop,
|
|
3576
4322
|
innerMessageType,
|
|
@@ -3580,15 +4326,49 @@ var require_dignity_p2p = __commonJS({
|
|
|
3580
4326
|
fanoutPeerIds: relayPeers
|
|
3581
4327
|
});
|
|
3582
4328
|
}
|
|
4329
|
+
normalizeGossipOperation(operation, publisherId) {
|
|
4330
|
+
if (!operation || !publisherId) {
|
|
4331
|
+
return null;
|
|
4332
|
+
}
|
|
4333
|
+
if (operation.actorId && operation.actorId !== publisherId) {
|
|
4334
|
+
this.emit("warning", {
|
|
4335
|
+
type: "gossip-operation-actor-mismatch",
|
|
4336
|
+
publisherId,
|
|
4337
|
+
actorId: operation.actorId,
|
|
4338
|
+
kind: operation.kind,
|
|
4339
|
+
collection: operation.collectionName,
|
|
4340
|
+
id: operation.id
|
|
4341
|
+
});
|
|
4342
|
+
return null;
|
|
4343
|
+
}
|
|
4344
|
+
const normalized = {
|
|
4345
|
+
...operation,
|
|
4346
|
+
actorId: publisherId
|
|
4347
|
+
};
|
|
4348
|
+
if (normalized.kind === "create") {
|
|
4349
|
+
normalized.ownerId = publisherId;
|
|
4350
|
+
}
|
|
4351
|
+
return normalized;
|
|
4352
|
+
}
|
|
3583
4353
|
async dispatchPeerGroupInnerMessage(innerMessageType, innerPayload, context = {}) {
|
|
3584
4354
|
if (innerMessageType === "operation") {
|
|
3585
|
-
this.
|
|
4355
|
+
const operation = this.normalizeGossipOperation(
|
|
4356
|
+
innerPayload,
|
|
4357
|
+
context.publisherId || context.senderId
|
|
4358
|
+
);
|
|
4359
|
+
if (operation) {
|
|
4360
|
+
this.applyOperation(operation);
|
|
4361
|
+
}
|
|
3586
4362
|
return;
|
|
3587
4363
|
}
|
|
3588
4364
|
if (innerMessageType === "record:snapshot") {
|
|
3589
4365
|
const { collectionName, record } = innerPayload || {};
|
|
3590
4366
|
if (collectionName && record) {
|
|
3591
|
-
const applied = this.restoreRecord(collectionName, record
|
|
4367
|
+
const applied = this.restoreRecord(collectionName, record, {
|
|
4368
|
+
rejectOnHashMismatch: true,
|
|
4369
|
+
rejectOnOwnershipMismatch: true,
|
|
4370
|
+
via: "peer-group"
|
|
4371
|
+
});
|
|
3592
4372
|
if (applied) {
|
|
3593
4373
|
this.emit("change", {
|
|
3594
4374
|
kind: "snapshot",
|
|
@@ -3738,6 +4518,13 @@ var require_dignity_p2p = __commonJS({
|
|
|
3738
4518
|
}
|
|
3739
4519
|
async handleIncomingMessage(message) {
|
|
3740
4520
|
if (message && message.opId && message.kind) {
|
|
4521
|
+
if (this.securityService.options.enabled) {
|
|
4522
|
+
this.emit("messageignored", {
|
|
4523
|
+
reason: "raw-operation-rejected",
|
|
4524
|
+
hint: "Unsigned raw operations are disabled when security is enabled"
|
|
4525
|
+
});
|
|
4526
|
+
return;
|
|
4527
|
+
}
|
|
3741
4528
|
this.applyOperation(message);
|
|
3742
4529
|
return;
|
|
3743
4530
|
}
|
|
@@ -3748,9 +4535,6 @@ var require_dignity_p2p = __commonJS({
|
|
|
3748
4535
|
});
|
|
3749
4536
|
return;
|
|
3750
4537
|
}
|
|
3751
|
-
if (message && message.senderId && message.senderPublicKey) {
|
|
3752
|
-
this.trustPeerPublicKey(message.senderId, message.senderPublicKey);
|
|
3753
|
-
}
|
|
3754
4538
|
let decrypted;
|
|
3755
4539
|
try {
|
|
3756
4540
|
decrypted = await this.securityService.decryptIncomingMessage(message);
|
|
@@ -3768,6 +4552,38 @@ var require_dignity_p2p = __commonJS({
|
|
|
3768
4552
|
if (!decrypted || decrypted.ignored) {
|
|
3769
4553
|
return;
|
|
3770
4554
|
}
|
|
4555
|
+
if (decrypted.messageType === "identity:rotate") {
|
|
4556
|
+
const peerId = decrypted.senderId || decrypted.payload?.username;
|
|
4557
|
+
if (peerId && decrypted.payload) {
|
|
4558
|
+
const result = this.applyPeerIdentityRotation(peerId, decrypted.payload);
|
|
4559
|
+
if (!result.applied) {
|
|
4560
|
+
this.emit("warning", {
|
|
4561
|
+
type: "identity-rotation-ignored",
|
|
4562
|
+
peerId,
|
|
4563
|
+
reason: result.reason
|
|
4564
|
+
});
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
return;
|
|
4568
|
+
}
|
|
4569
|
+
if (decrypted.messageType === "identity:cold-enroll") {
|
|
4570
|
+
const peerId = decrypted.senderId || decrypted.payload?.username;
|
|
4571
|
+
if (peerId && decrypted.payload) {
|
|
4572
|
+
try {
|
|
4573
|
+
this.applyPeerColdRecoveryEnrollment(peerId, decrypted.payload);
|
|
4574
|
+
} catch (error) {
|
|
4575
|
+
this.emit("warning", {
|
|
4576
|
+
type: "cold-recovery-enrollment-rejected",
|
|
4577
|
+
peerId,
|
|
4578
|
+
error
|
|
4579
|
+
});
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
return;
|
|
4583
|
+
}
|
|
4584
|
+
if (message && message.senderId && message.senderPublicKey) {
|
|
4585
|
+
this.trustPeerPublicKey(message.senderId, message.senderPublicKey);
|
|
4586
|
+
}
|
|
3771
4587
|
if (decrypted.messageType === "operation") {
|
|
3772
4588
|
this.applyOperation(decrypted.payload);
|
|
3773
4589
|
return;
|
|
@@ -3776,7 +4592,10 @@ var require_dignity_p2p = __commonJS({
|
|
|
3776
4592
|
const payload = decrypted.payload || {};
|
|
3777
4593
|
const { collectionName, record } = payload;
|
|
3778
4594
|
if (collectionName && record) {
|
|
3779
|
-
const applied = this.restoreRecord(collectionName, record
|
|
4595
|
+
const applied = this.restoreRecord(collectionName, record, {
|
|
4596
|
+
rejectOnHashMismatch: true,
|
|
4597
|
+
via: "direct-mesh"
|
|
4598
|
+
});
|
|
3780
4599
|
if (applied) {
|
|
3781
4600
|
this.emit("change", {
|
|
3782
4601
|
kind: "snapshot",
|
|
@@ -3791,18 +4610,27 @@ var require_dignity_p2p = __commonJS({
|
|
|
3791
4610
|
const payload = decrypted.payload || {};
|
|
3792
4611
|
const scope = payload.scope || "main";
|
|
3793
4612
|
const peerId = payload.peerId || decrypted.senderId;
|
|
3794
|
-
if (!peerId) {
|
|
4613
|
+
if (!peerId || peerId !== decrypted.senderId) {
|
|
4614
|
+
return;
|
|
4615
|
+
}
|
|
4616
|
+
if (!this.discoveryRooms.has(scope)) {
|
|
3795
4617
|
return;
|
|
3796
4618
|
}
|
|
4619
|
+
const room = this.discoveryRooms.get(scope);
|
|
3797
4620
|
const presenceMap = this.getPresenceMap(scope);
|
|
3798
4621
|
const isNewPeerInScope = !presenceMap.has(peerId);
|
|
4622
|
+
const requestedTtl = typeof payload.ttlMs === "number" ? payload.ttlMs : room.ttlMs;
|
|
4623
|
+
const ttlMs = Math.min(requestedTtl, room.ttlMs);
|
|
3799
4624
|
this.upsertPresence(
|
|
3800
4625
|
scope,
|
|
3801
4626
|
peerId,
|
|
3802
4627
|
payload.metadata || {},
|
|
3803
|
-
|
|
3804
|
-
|
|
4628
|
+
ttlMs,
|
|
4629
|
+
this.now()
|
|
3805
4630
|
);
|
|
4631
|
+
if (payload.metadata && payload.metadata.publicKey) {
|
|
4632
|
+
this.trustPeerPublicKey(peerId, payload.metadata.publicKey);
|
|
4633
|
+
}
|
|
3806
4634
|
if (isNewPeerInScope && peerId !== this.nodeId && this.discoveryRooms.has(scope)) {
|
|
3807
4635
|
if (typeof this.networkAdapter.connectToPeer === "function") {
|
|
3808
4636
|
Promise.resolve(this.connectToPeer(peerId)).catch((error) => {
|
|
@@ -3819,6 +4647,9 @@ var require_dignity_p2p = __commonJS({
|
|
|
3819
4647
|
const payload = decrypted.payload || {};
|
|
3820
4648
|
const scope = payload.scope || "main";
|
|
3821
4649
|
const peerId = payload.peerId || decrypted.senderId;
|
|
4650
|
+
if (!peerId || peerId !== decrypted.senderId) {
|
|
4651
|
+
return;
|
|
4652
|
+
}
|
|
3822
4653
|
const map = this.presenceByScope.get(scope);
|
|
3823
4654
|
if (map && peerId && map.has(peerId)) {
|
|
3824
4655
|
map.delete(peerId);
|
|
@@ -3875,7 +4706,7 @@ var require_dignity_p2p = __commonJS({
|
|
|
3875
4706
|
emitConflict(details) {
|
|
3876
4707
|
this.emit("conflict", details);
|
|
3877
4708
|
}
|
|
3878
|
-
restoreRecord(collectionName, record) {
|
|
4709
|
+
restoreRecord(collectionName, record, options = {}) {
|
|
3879
4710
|
if (!record || !record.id) {
|
|
3880
4711
|
return false;
|
|
3881
4712
|
}
|
|
@@ -3886,14 +4717,42 @@ var require_dignity_p2p = __commonJS({
|
|
|
3886
4717
|
}
|
|
3887
4718
|
const restoredData = { ...record.data || {} };
|
|
3888
4719
|
const computedHash = computeContentHash(restoredData);
|
|
3889
|
-
|
|
4720
|
+
const rejectOnHashMismatch = options.rejectOnHashMismatch === true;
|
|
4721
|
+
const rejectOnOwnershipMismatch = options.rejectOnOwnershipMismatch === true;
|
|
4722
|
+
if (rejectOnOwnershipMismatch && current && record.ownerId && current.ownerId !== record.ownerId) {
|
|
4723
|
+
this.emit("warning", {
|
|
4724
|
+
type: "ownership-mismatch",
|
|
4725
|
+
collection: collectionName,
|
|
4726
|
+
id: record.id,
|
|
4727
|
+
currentOwnerId: current.ownerId,
|
|
4728
|
+
advertisedOwnerId: record.ownerId,
|
|
4729
|
+
via: options.via || null
|
|
4730
|
+
});
|
|
4731
|
+
return false;
|
|
4732
|
+
}
|
|
4733
|
+
if (!record.hash) {
|
|
4734
|
+
const warning = {
|
|
4735
|
+
type: "content-hash-missing",
|
|
4736
|
+
collection: collectionName,
|
|
4737
|
+
id: record.id,
|
|
4738
|
+
via: options.via || null
|
|
4739
|
+
};
|
|
4740
|
+
this.emit("warning", warning);
|
|
4741
|
+
if (rejectOnHashMismatch) {
|
|
4742
|
+
return false;
|
|
4743
|
+
}
|
|
4744
|
+
} else if (record.hash !== computedHash) {
|
|
3890
4745
|
this.emit("warning", {
|
|
3891
4746
|
type: "content-hash-mismatch",
|
|
3892
4747
|
collection: collectionName,
|
|
3893
4748
|
id: record.id,
|
|
3894
4749
|
advertisedHash: record.hash,
|
|
3895
|
-
computedHash
|
|
4750
|
+
computedHash,
|
|
4751
|
+
via: options.via || null
|
|
3896
4752
|
});
|
|
4753
|
+
if (rejectOnHashMismatch) {
|
|
4754
|
+
return false;
|
|
4755
|
+
}
|
|
3897
4756
|
}
|
|
3898
4757
|
collection.set(record.id, {
|
|
3899
4758
|
id: record.id,
|
|
@@ -3935,6 +4794,15 @@ var require_dignity_p2p = __commonJS({
|
|
|
3935
4794
|
});
|
|
3936
4795
|
return record;
|
|
3937
4796
|
}
|
|
4797
|
+
pruneAppliedOperations() {
|
|
4798
|
+
while (this.appliedOperations.size > this.maxAppliedOperations) {
|
|
4799
|
+
const oldestOpId = this.appliedOperations.keys().next().value;
|
|
4800
|
+
if (!oldestOpId) {
|
|
4801
|
+
break;
|
|
4802
|
+
}
|
|
4803
|
+
this.appliedOperations.delete(oldestOpId);
|
|
4804
|
+
}
|
|
4805
|
+
}
|
|
3938
4806
|
applyOperation(operation) {
|
|
3939
4807
|
if (!operation || !operation.opId || this.appliedOperations.has(operation.opId)) {
|
|
3940
4808
|
return false;
|
|
@@ -3956,7 +4824,8 @@ var require_dignity_p2p = __commonJS({
|
|
|
3956
4824
|
deletedAt: null,
|
|
3957
4825
|
version: 1
|
|
3958
4826
|
});
|
|
3959
|
-
this.appliedOperations.
|
|
4827
|
+
this.appliedOperations.set(operation.opId, this.now());
|
|
4828
|
+
this.pruneAppliedOperations();
|
|
3960
4829
|
this.emit("change", { kind: "create", collection: operation.collectionName, id: operation.id });
|
|
3961
4830
|
return true;
|
|
3962
4831
|
}
|
|
@@ -4000,7 +4869,8 @@ var require_dignity_p2p = __commonJS({
|
|
|
4000
4869
|
}
|
|
4001
4870
|
current.updatedAt = operation.timestamp;
|
|
4002
4871
|
current.version += 1;
|
|
4003
|
-
this.appliedOperations.
|
|
4872
|
+
this.appliedOperations.set(operation.opId, this.now());
|
|
4873
|
+
this.pruneAppliedOperations();
|
|
4004
4874
|
this.emit("change", {
|
|
4005
4875
|
kind: "transfer-ownership",
|
|
4006
4876
|
collection: operation.collectionName,
|
|
@@ -4029,7 +4899,8 @@ var require_dignity_p2p = __commonJS({
|
|
|
4029
4899
|
current.deletedAt = operation.timestamp;
|
|
4030
4900
|
current.updatedAt = operation.timestamp;
|
|
4031
4901
|
current.version += 1;
|
|
4032
|
-
this.appliedOperations.
|
|
4902
|
+
this.appliedOperations.set(operation.opId, this.now());
|
|
4903
|
+
this.pruneAppliedOperations();
|
|
4033
4904
|
this.emit("change", { kind: "delete", collection: operation.collectionName, id: operation.id });
|
|
4034
4905
|
return true;
|
|
4035
4906
|
}
|
|
@@ -4059,7 +4930,8 @@ var require_dignity_p2p = __commonJS({
|
|
|
4059
4930
|
}
|
|
4060
4931
|
current.updatedAt = operation.timestamp;
|
|
4061
4932
|
current.version += 1;
|
|
4062
|
-
this.appliedOperations.
|
|
4933
|
+
this.appliedOperations.set(operation.opId, this.now());
|
|
4934
|
+
this.pruneAppliedOperations();
|
|
4063
4935
|
this.emit("change", { kind: "update", collection: operation.collectionName, id: operation.id });
|
|
4064
4936
|
return true;
|
|
4065
4937
|
}
|
|
@@ -11595,12 +12467,25 @@ var require_index = __commonJS({
|
|
|
11595
12467
|
var SlothPermutation = require_sloth_vdf();
|
|
11596
12468
|
var {
|
|
11597
12469
|
MessageSecurityService,
|
|
11598
|
-
DEFAULT_SECURITY_OPTIONS
|
|
12470
|
+
DEFAULT_SECURITY_OPTIONS,
|
|
12471
|
+
DEFAULT_APP_PASSWORD
|
|
11599
12472
|
} = require_message_security_service();
|
|
12473
|
+
var { deriveKeyPairFromCredentials, keyPairToPublicBundle, deriveColdRecoverySigningKey } = require_derive_key_pair();
|
|
12474
|
+
var {
|
|
12475
|
+
createIdentityRotation,
|
|
12476
|
+
verifyIdentityRotation,
|
|
12477
|
+
revokeAndRotateIdentity,
|
|
12478
|
+
rotateIdentityPassword,
|
|
12479
|
+
enrollColdRecoveryPassword,
|
|
12480
|
+
verifyColdRecoveryEnrollment,
|
|
12481
|
+
shouldApplyIdentityRotation
|
|
12482
|
+
} = require_identity_rotation();
|
|
12483
|
+
var parsePeerJsServerUrl = require_parse_peerjs_url();
|
|
11600
12484
|
var {
|
|
11601
12485
|
PEER_GROUP_SCOPE_PREFIX,
|
|
11602
12486
|
DEFAULT_PEER_GROUP_OPTIONS,
|
|
11603
12487
|
peerGroupScope,
|
|
12488
|
+
parsePeerGroupScope,
|
|
11604
12489
|
selectFanoutPeers
|
|
11605
12490
|
} = require_peer_group();
|
|
11606
12491
|
module.exports = {
|
|
@@ -11620,9 +12505,22 @@ var require_index = __commonJS({
|
|
|
11620
12505
|
SlothPermutation,
|
|
11621
12506
|
MessageSecurityService,
|
|
11622
12507
|
DEFAULT_SECURITY_OPTIONS,
|
|
12508
|
+
DEFAULT_APP_PASSWORD,
|
|
12509
|
+
deriveKeyPairFromCredentials,
|
|
12510
|
+
deriveColdRecoverySigningKey,
|
|
12511
|
+
keyPairToPublicBundle,
|
|
12512
|
+
createIdentityRotation,
|
|
12513
|
+
verifyIdentityRotation,
|
|
12514
|
+
revokeAndRotateIdentity,
|
|
12515
|
+
rotateIdentityPassword,
|
|
12516
|
+
enrollColdRecoveryPassword,
|
|
12517
|
+
verifyColdRecoveryEnrollment,
|
|
12518
|
+
shouldApplyIdentityRotation,
|
|
12519
|
+
parsePeerJsServerUrl,
|
|
11623
12520
|
PEER_GROUP_SCOPE_PREFIX,
|
|
11624
12521
|
DEFAULT_PEER_GROUP_OPTIONS,
|
|
11625
12522
|
peerGroupScope,
|
|
12523
|
+
parsePeerGroupScope,
|
|
11626
12524
|
selectFanoutPeers
|
|
11627
12525
|
};
|
|
11628
12526
|
}
|