@stvor/sdk 2.2.1 → 2.3.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/dist/facade/app.d.ts +75 -2
- package/dist/facade/app.js +152 -5
- package/dist/facade/crypto-session.d.ts +76 -0
- package/dist/facade/crypto-session.js +175 -0
- package/dist/facade/errors.d.ts +2 -0
- package/dist/facade/errors.js +3 -0
- package/dist/facade/index.d.ts +1 -1
- package/dist/facade/index.js +1 -1
- package/dist/facade/replay-manager.d.ts +58 -0
- package/dist/facade/replay-manager.js +117 -0
- package/dist/facade/sodium-singleton.d.ts +20 -0
- package/dist/facade/sodium-singleton.js +44 -0
- package/dist/facade/tofu-manager.d.ts +80 -0
- package/dist/facade/tofu-manager.js +134 -0
- package/dist/ratchet/index.d.ts +88 -0
- package/dist/ratchet/index.js +318 -0
- package/dist/ratchet/key-recovery.d.ts +45 -0
- package/dist/ratchet/key-recovery.js +148 -0
- package/dist/ratchet/replay-protection.d.ts +21 -0
- package/dist/ratchet/replay-protection.js +50 -0
- package/dist/ratchet/tests/ratchet.test.d.ts +1 -0
- package/dist/ratchet/tests/ratchet.test.js +160 -0
- package/dist/ratchet/tofu.d.ts +27 -0
- package/dist/ratchet/tofu.js +62 -0
- package/package.json +1 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STVOR Replay Protection Manager
|
|
3
|
+
* Integrates nonce-based replay protection with in-memory fallback
|
|
4
|
+
*
|
|
5
|
+
* ⚠️ CRITICAL LIMITATIONS (v2.1):
|
|
6
|
+
*
|
|
7
|
+
* 1. IN-MEMORY ONLY - DEMO-LEVEL PROTECTION
|
|
8
|
+
* - Process restart → cache cleared → replay window reopens
|
|
9
|
+
* - Clustered deployment → each instance has separate cache
|
|
10
|
+
* - Mobile background → iOS/Android may kill process
|
|
11
|
+
*
|
|
12
|
+
* 2. ATTACK WINDOW: 5 minutes after restart/cache clear
|
|
13
|
+
*
|
|
14
|
+
* 3. PRODUCTION REQUIREMENTS:
|
|
15
|
+
* - Redis or distributed cache (Memcached, DynamoDB)
|
|
16
|
+
* - Persistent storage survives restarts
|
|
17
|
+
* - Shared state across instances
|
|
18
|
+
*
|
|
19
|
+
* 4. ACCEPTABLE FOR:
|
|
20
|
+
* ✓ Single-instance development
|
|
21
|
+
* ✓ Proof-of-concept deployments
|
|
22
|
+
* ✓ Low-security use cases
|
|
23
|
+
*
|
|
24
|
+
* 5. NOT ACCEPTABLE FOR:
|
|
25
|
+
* ✗ Multi-instance production
|
|
26
|
+
* ✗ High-security environments
|
|
27
|
+
* ✗ Mobile apps (background kills)
|
|
28
|
+
*
|
|
29
|
+
* STATUS: Transitional implementation - Redis integration planned for v2.2
|
|
30
|
+
*/
|
|
31
|
+
import sodium from 'libsodium-wrappers';
|
|
32
|
+
// In-memory nonce cache (fallback when Redis unavailable)
|
|
33
|
+
// ⚠️ LOST ON RESTART - see limitations above
|
|
34
|
+
const nonceCache = new Map();
|
|
35
|
+
const NONCE_EXPIRY_MS = 5 * 60 * 1000; // 5 minutes
|
|
36
|
+
const MAX_CACHE_SIZE = 10000; // Prevent memory exhaustion
|
|
37
|
+
/**
|
|
38
|
+
* Check if message is a replay attack
|
|
39
|
+
* @param userId - Sender's user ID
|
|
40
|
+
* @param nonce - Message nonce (base64 or hex)
|
|
41
|
+
* @param timestamp - Message timestamp (Unix seconds)
|
|
42
|
+
* @returns true if replay detected
|
|
43
|
+
*/
|
|
44
|
+
export async function isReplay(userId, nonce, timestamp) {
|
|
45
|
+
const key = `${userId}:${nonce}`;
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
// Check if nonce already seen
|
|
48
|
+
const cached = nonceCache.get(key);
|
|
49
|
+
if (cached) {
|
|
50
|
+
// Replay detected
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
// Check if message is too old
|
|
54
|
+
const messageAge = now - timestamp * 1000;
|
|
55
|
+
if (messageAge > NONCE_EXPIRY_MS) {
|
|
56
|
+
throw new Error('Message rejected: too old');
|
|
57
|
+
}
|
|
58
|
+
// Store nonce with timestamp
|
|
59
|
+
nonceCache.set(key, { timestamp: now });
|
|
60
|
+
// Cleanup old entries if cache is too large
|
|
61
|
+
if (nonceCache.size > MAX_CACHE_SIZE) {
|
|
62
|
+
await cleanupExpiredNonces();
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Validate message for replay protection
|
|
68
|
+
* Throws error if replay detected or message too old
|
|
69
|
+
*/
|
|
70
|
+
export async function validateMessage(userId, nonce, timestamp) {
|
|
71
|
+
const replay = await isReplay(userId, nonce, timestamp);
|
|
72
|
+
if (replay) {
|
|
73
|
+
throw new Error(`Replay attack detected from user ${userId}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Validate message with Uint8Array nonce
|
|
78
|
+
*/
|
|
79
|
+
export async function validateMessageWithNonce(userId, nonce, timestamp) {
|
|
80
|
+
const nonceHex = sodium.to_hex(nonce);
|
|
81
|
+
await validateMessage(userId, nonceHex, timestamp);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Cleanup expired nonces from cache
|
|
85
|
+
*/
|
|
86
|
+
async function cleanupExpiredNonces() {
|
|
87
|
+
const now = Date.now();
|
|
88
|
+
let cleaned = 0;
|
|
89
|
+
for (const [key, value] of nonceCache.entries()) {
|
|
90
|
+
if (now - value.timestamp > NONCE_EXPIRY_MS) {
|
|
91
|
+
nonceCache.delete(key);
|
|
92
|
+
cleaned++;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (cleaned > 0) {
|
|
96
|
+
console.log(`[ReplayProtection] Cleaned ${cleaned} expired nonces`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get cache statistics (for monitoring)
|
|
101
|
+
*/
|
|
102
|
+
export function getCacheStats() {
|
|
103
|
+
return {
|
|
104
|
+
size: nonceCache.size,
|
|
105
|
+
maxSize: MAX_CACHE_SIZE,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Clear all cached nonces (for testing)
|
|
110
|
+
*/
|
|
111
|
+
export function clearNonceCache() {
|
|
112
|
+
nonceCache.clear();
|
|
113
|
+
}
|
|
114
|
+
// Periodic cleanup (every 5 minutes)
|
|
115
|
+
if (typeof setInterval !== 'undefined') {
|
|
116
|
+
setInterval(cleanupExpiredNonces, 5 * 60 * 1000);
|
|
117
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STVOR libsodium Singleton
|
|
3
|
+
* Ensures sodium.ready is called only ONCE globally
|
|
4
|
+
* Prevents race conditions during concurrent initialization
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Initialize libsodium ONCE globally
|
|
8
|
+
* Safe to call multiple times - returns same promise
|
|
9
|
+
*
|
|
10
|
+
* @throws Never throws - libsodium.ready is infallible
|
|
11
|
+
*/
|
|
12
|
+
export declare function ensureSodiumReady(): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Check if libsodium is ready (synchronous)
|
|
15
|
+
*/
|
|
16
|
+
export declare function isSodiumReady(): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Reset state (ONLY for testing)
|
|
19
|
+
*/
|
|
20
|
+
export declare function _resetSodiumState(): void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STVOR libsodium Singleton
|
|
3
|
+
* Ensures sodium.ready is called only ONCE globally
|
|
4
|
+
* Prevents race conditions during concurrent initialization
|
|
5
|
+
*/
|
|
6
|
+
import sodium from 'libsodium-wrappers';
|
|
7
|
+
let sodiumInitialized = false;
|
|
8
|
+
let sodiumInitPromise = null;
|
|
9
|
+
/**
|
|
10
|
+
* Initialize libsodium ONCE globally
|
|
11
|
+
* Safe to call multiple times - returns same promise
|
|
12
|
+
*
|
|
13
|
+
* @throws Never throws - libsodium.ready is infallible
|
|
14
|
+
*/
|
|
15
|
+
export async function ensureSodiumReady() {
|
|
16
|
+
// Already initialized - return immediately
|
|
17
|
+
if (sodiumInitialized) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
// Initialization in progress - return existing promise
|
|
21
|
+
if (sodiumInitPromise) {
|
|
22
|
+
return sodiumInitPromise;
|
|
23
|
+
}
|
|
24
|
+
// Start initialization
|
|
25
|
+
sodiumInitPromise = (async () => {
|
|
26
|
+
await sodium.ready;
|
|
27
|
+
sodiumInitialized = true;
|
|
28
|
+
console.log('[Crypto] libsodium initialized');
|
|
29
|
+
})();
|
|
30
|
+
return sodiumInitPromise;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check if libsodium is ready (synchronous)
|
|
34
|
+
*/
|
|
35
|
+
export function isSodiumReady() {
|
|
36
|
+
return sodiumInitialized;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Reset state (ONLY for testing)
|
|
40
|
+
*/
|
|
41
|
+
export function _resetSodiumState() {
|
|
42
|
+
sodiumInitialized = false;
|
|
43
|
+
sodiumInitPromise = null;
|
|
44
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STVOR TOFU (Trust On First Use) Manager
|
|
3
|
+
* Integrates fingerprint verification with in-memory fallback
|
|
4
|
+
*
|
|
5
|
+
* SEMANTICS:
|
|
6
|
+
* - Fingerprint = BLAKE2b(identity_public_key)
|
|
7
|
+
* - Binding: identity key ONLY (not bundle, not SPK)
|
|
8
|
+
* - Key rotation: requires manual re-trust via trustNewFingerprint()
|
|
9
|
+
* - Multi-device: NOT supported (each device = new identity)
|
|
10
|
+
* - Reinstall: fingerprint lost (in-memory only)
|
|
11
|
+
*
|
|
12
|
+
* LIMITATIONS:
|
|
13
|
+
* - First-use MITM vulnerability (standard TOFU)
|
|
14
|
+
* - No persistence (keys lost on restart)
|
|
15
|
+
* - No out-of-band verification UX
|
|
16
|
+
*
|
|
17
|
+
* TODO:
|
|
18
|
+
* - Add persistent storage (IndexedDB/localStorage)
|
|
19
|
+
* - Add manual verification UI (compare fingerprints)
|
|
20
|
+
* - Add key rotation notification system
|
|
21
|
+
*/
|
|
22
|
+
interface FingerprintRecord {
|
|
23
|
+
fingerprint: string;
|
|
24
|
+
firstSeen: Date;
|
|
25
|
+
version: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generate BLAKE2b-256 fingerprint from identity public key
|
|
29
|
+
*
|
|
30
|
+
* BINDING: Identity key ONLY
|
|
31
|
+
* - SPK rotation does NOT change fingerprint
|
|
32
|
+
* - OPK exhaustion does NOT change fingerprint
|
|
33
|
+
* - Only identity key rotation changes fingerprint
|
|
34
|
+
*/
|
|
35
|
+
export declare function generateFingerprint(identityPublicKey: Uint8Array): string;
|
|
36
|
+
/**
|
|
37
|
+
* Store fingerprint for user (in-memory fallback)
|
|
38
|
+
*/
|
|
39
|
+
export declare function storeFingerprint(userId: string, fingerprint: string): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Verify fingerprint against stored value
|
|
42
|
+
*
|
|
43
|
+
* BEHAVIOR:
|
|
44
|
+
* - First use: stores fingerprint, returns true
|
|
45
|
+
* - Match: returns true
|
|
46
|
+
* - Mismatch: throws error (HARD FAILURE)
|
|
47
|
+
*
|
|
48
|
+
* KEY ROTATION:
|
|
49
|
+
* - Automatic rotation NOT supported
|
|
50
|
+
* - Requires manual trustNewFingerprint() call
|
|
51
|
+
* - Otherwise connection fails on mismatch
|
|
52
|
+
*
|
|
53
|
+
* @throws Error on fingerprint mismatch (possible MITM or key rotation)
|
|
54
|
+
*/
|
|
55
|
+
export declare function verifyFingerprint(userId: string, identityPublicKey: Uint8Array): Promise<boolean>;
|
|
56
|
+
/**
|
|
57
|
+
* Manually trust a new fingerprint (key rotation)
|
|
58
|
+
*
|
|
59
|
+
* USE CASES:
|
|
60
|
+
* - User reinstalled app and lost keys
|
|
61
|
+
* - Legitimate key rotation after compromise
|
|
62
|
+
* - Migration from old device
|
|
63
|
+
*
|
|
64
|
+
* SECURITY: Should be called ONLY after out-of-band verification
|
|
65
|
+
*/
|
|
66
|
+
export declare function trustNewFingerprint(userId: string, identityPublicKey: Uint8Array): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Get stored fingerprint record for user
|
|
69
|
+
*/
|
|
70
|
+
export declare function getStoredFingerprint(userId: string): FingerprintRecord | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Format fingerprint for display (groups of 4 hex chars)
|
|
73
|
+
* Example: "a3f2 d8c1 5e90 7b4a ..."
|
|
74
|
+
*/
|
|
75
|
+
export declare function formatFingerprint(fingerprint: string): string;
|
|
76
|
+
/**
|
|
77
|
+
* Clear all stored fingerprints (TESTING ONLY)
|
|
78
|
+
*/
|
|
79
|
+
export declare function clearFingerprints(): void;
|
|
80
|
+
export {};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STVOR TOFU (Trust On First Use) Manager
|
|
3
|
+
* Integrates fingerprint verification with in-memory fallback
|
|
4
|
+
*
|
|
5
|
+
* SEMANTICS:
|
|
6
|
+
* - Fingerprint = BLAKE2b(identity_public_key)
|
|
7
|
+
* - Binding: identity key ONLY (not bundle, not SPK)
|
|
8
|
+
* - Key rotation: requires manual re-trust via trustNewFingerprint()
|
|
9
|
+
* - Multi-device: NOT supported (each device = new identity)
|
|
10
|
+
* - Reinstall: fingerprint lost (in-memory only)
|
|
11
|
+
*
|
|
12
|
+
* LIMITATIONS:
|
|
13
|
+
* - First-use MITM vulnerability (standard TOFU)
|
|
14
|
+
* - No persistence (keys lost on restart)
|
|
15
|
+
* - No out-of-band verification UX
|
|
16
|
+
*
|
|
17
|
+
* TODO:
|
|
18
|
+
* - Add persistent storage (IndexedDB/localStorage)
|
|
19
|
+
* - Add manual verification UI (compare fingerprints)
|
|
20
|
+
* - Add key rotation notification system
|
|
21
|
+
*/
|
|
22
|
+
import sodium from 'libsodium-wrappers';
|
|
23
|
+
// In-memory fingerprint cache (fallback when PostgreSQL unavailable)
|
|
24
|
+
const fingerprintCache = new Map();
|
|
25
|
+
const FINGERPRINT_VERSION = 1; // Increment on breaking changes
|
|
26
|
+
/**
|
|
27
|
+
* Generate BLAKE2b-256 fingerprint from identity public key
|
|
28
|
+
*
|
|
29
|
+
* BINDING: Identity key ONLY
|
|
30
|
+
* - SPK rotation does NOT change fingerprint
|
|
31
|
+
* - OPK exhaustion does NOT change fingerprint
|
|
32
|
+
* - Only identity key rotation changes fingerprint
|
|
33
|
+
*/
|
|
34
|
+
export function generateFingerprint(identityPublicKey) {
|
|
35
|
+
const hash = sodium.crypto_generichash(32, identityPublicKey);
|
|
36
|
+
return sodium.to_hex(hash);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Store fingerprint for user (in-memory fallback)
|
|
40
|
+
*/
|
|
41
|
+
export async function storeFingerprint(userId, fingerprint) {
|
|
42
|
+
const record = {
|
|
43
|
+
fingerprint,
|
|
44
|
+
firstSeen: new Date(),
|
|
45
|
+
version: FINGERPRINT_VERSION,
|
|
46
|
+
};
|
|
47
|
+
fingerprintCache.set(userId, record);
|
|
48
|
+
// TODO: Add PostgreSQL persistence when available
|
|
49
|
+
// try {
|
|
50
|
+
// await pool.query(
|
|
51
|
+
// 'INSERT INTO fingerprints (user_id, fingerprint, first_seen, version) VALUES ($1, $2, $3, $4) ON CONFLICT (user_id) DO UPDATE SET fingerprint = $2',
|
|
52
|
+
// [userId, fingerprint, record.firstSeen, record.version]
|
|
53
|
+
// );
|
|
54
|
+
// } catch (error) {
|
|
55
|
+
// // Fallback to in-memory storage
|
|
56
|
+
// fingerprintCache.set(userId, record);
|
|
57
|
+
// }
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Verify fingerprint against stored value
|
|
61
|
+
*
|
|
62
|
+
* BEHAVIOR:
|
|
63
|
+
* - First use: stores fingerprint, returns true
|
|
64
|
+
* - Match: returns true
|
|
65
|
+
* - Mismatch: throws error (HARD FAILURE)
|
|
66
|
+
*
|
|
67
|
+
* KEY ROTATION:
|
|
68
|
+
* - Automatic rotation NOT supported
|
|
69
|
+
* - Requires manual trustNewFingerprint() call
|
|
70
|
+
* - Otherwise connection fails on mismatch
|
|
71
|
+
*
|
|
72
|
+
* @throws Error on fingerprint mismatch (possible MITM or key rotation)
|
|
73
|
+
*/
|
|
74
|
+
export async function verifyFingerprint(userId, identityPublicKey) {
|
|
75
|
+
const fingerprint = generateFingerprint(identityPublicKey);
|
|
76
|
+
// Check in-memory cache first
|
|
77
|
+
const storedRecord = fingerprintCache.get(userId);
|
|
78
|
+
if (!storedRecord) {
|
|
79
|
+
// First use - store fingerprint
|
|
80
|
+
await storeFingerprint(userId, fingerprint);
|
|
81
|
+
console.log(`[TOFU] ✓ First contact: ${userId} (${fingerprint.slice(0, 16)}...)`);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
// Verify fingerprint matches
|
|
85
|
+
if (storedRecord.fingerprint !== fingerprint) {
|
|
86
|
+
throw new Error(`[TOFU] ✗ SECURITY ALERT: Identity key mismatch for ${userId}\n` +
|
|
87
|
+
` Expected: ${storedRecord.fingerprint.slice(0, 16)}...\n` +
|
|
88
|
+
` Received: ${fingerprint.slice(0, 16)}...\n` +
|
|
89
|
+
` First seen: ${storedRecord.firstSeen.toISOString()}\n\n` +
|
|
90
|
+
`POSSIBLE CAUSES:\n` +
|
|
91
|
+
` 1. MITM attack (key substitution)\n` +
|
|
92
|
+
` 2. User reinstalled app (legitimate key rotation)\n` +
|
|
93
|
+
` 3. Multi-device not supported (different keys)\n\n` +
|
|
94
|
+
`ACTION: Verify out-of-band or call trustNewFingerprint()`);
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Manually trust a new fingerprint (key rotation)
|
|
100
|
+
*
|
|
101
|
+
* USE CASES:
|
|
102
|
+
* - User reinstalled app and lost keys
|
|
103
|
+
* - Legitimate key rotation after compromise
|
|
104
|
+
* - Migration from old device
|
|
105
|
+
*
|
|
106
|
+
* SECURITY: Should be called ONLY after out-of-band verification
|
|
107
|
+
*/
|
|
108
|
+
export async function trustNewFingerprint(userId, identityPublicKey) {
|
|
109
|
+
const fingerprint = generateFingerprint(identityPublicKey);
|
|
110
|
+
const oldRecord = fingerprintCache.get(userId);
|
|
111
|
+
await storeFingerprint(userId, fingerprint);
|
|
112
|
+
console.log(`[TOFU] ⚠️ Manually trusted new identity for ${userId}\n` +
|
|
113
|
+
` Old: ${oldRecord?.fingerprint.slice(0, 16) || 'none'}...\n` +
|
|
114
|
+
` New: ${fingerprint.slice(0, 16)}...`);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get stored fingerprint record for user
|
|
118
|
+
*/
|
|
119
|
+
export function getStoredFingerprint(userId) {
|
|
120
|
+
return fingerprintCache.get(userId);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Format fingerprint for display (groups of 4 hex chars)
|
|
124
|
+
* Example: "a3f2 d8c1 5e90 7b4a ..."
|
|
125
|
+
*/
|
|
126
|
+
export function formatFingerprint(fingerprint) {
|
|
127
|
+
return fingerprint.match(/.{1,4}/g)?.join(' ') || fingerprint;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Clear all stored fingerprints (TESTING ONLY)
|
|
131
|
+
*/
|
|
132
|
+
export function clearFingerprints() {
|
|
133
|
+
fingerprintCache.clear();
|
|
134
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* X3DH + Double Ratchet Implementation
|
|
3
|
+
* This module handles session establishment and message encryption/decryption.
|
|
4
|
+
*/
|
|
5
|
+
export interface SessionState {
|
|
6
|
+
identityKey: Uint8Array;
|
|
7
|
+
signedPreKey: Uint8Array;
|
|
8
|
+
oneTimePreKey: Uint8Array;
|
|
9
|
+
rootKey: Uint8Array;
|
|
10
|
+
sendingChainKey: Uint8Array;
|
|
11
|
+
receivingChainKey: Uint8Array;
|
|
12
|
+
skippedMessageKeys: Map<string, Uint8Array>;
|
|
13
|
+
isPostCompromise: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function initializeCrypto(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* X3DH Session Establishment
|
|
18
|
+
* @param identityKeyPair - The user's identity key pair
|
|
19
|
+
* @param signedPreKeyPair - The user's signed pre-key pair
|
|
20
|
+
* @param oneTimePreKey - A one-time pre-key
|
|
21
|
+
* @param recipientIdentityKey - The recipient's identity key
|
|
22
|
+
* @param recipientSignedPreKey - The recipient's signed pre-key
|
|
23
|
+
* @param recipientOneTimePreKey - The recipient's one-time pre-key
|
|
24
|
+
* @param recipientSPKSignature - Signature of SPK by recipient's identity key
|
|
25
|
+
* @param protocolVersion - The protocol version
|
|
26
|
+
* @param cipherSuite - The cipher suite
|
|
27
|
+
* @returns SessionState
|
|
28
|
+
*/
|
|
29
|
+
export declare function establishSession(identityKeyPair: {
|
|
30
|
+
publicKey: Uint8Array;
|
|
31
|
+
privateKey: Uint8Array;
|
|
32
|
+
}, signedPreKeyPair: {
|
|
33
|
+
publicKey: Uint8Array;
|
|
34
|
+
privateKey: Uint8Array;
|
|
35
|
+
}, oneTimePreKey: Uint8Array, recipientIdentityKey: Uint8Array, recipientSignedPreKey: Uint8Array, recipientOneTimePreKey: Uint8Array, recipientSPKSignature: Uint8Array, protocolVersion: string, cipherSuite: string): SessionState;
|
|
36
|
+
/**
|
|
37
|
+
* Double Ratchet Encryption
|
|
38
|
+
* @param plaintext - The message to encrypt
|
|
39
|
+
* @param session - The current session state
|
|
40
|
+
* @returns { ciphertext: Uint8Array; header: { publicKey: Uint8Array; nonce: Uint8Array } }
|
|
41
|
+
*/
|
|
42
|
+
export declare function encryptMessage(plaintext: string, session: SessionState): {
|
|
43
|
+
ciphertext: any;
|
|
44
|
+
header: {
|
|
45
|
+
publicKey: any;
|
|
46
|
+
nonce: any;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Double Ratchet Decryption
|
|
51
|
+
* @param ciphertext - The encrypted message
|
|
52
|
+
* @param header - The message header containing the sender's public key and nonce
|
|
53
|
+
* @param session - The current session state
|
|
54
|
+
* @returns The decrypted plaintext
|
|
55
|
+
*/
|
|
56
|
+
export declare function decryptMessage(ciphertext: Uint8Array, header: {
|
|
57
|
+
publicKey: Uint8Array;
|
|
58
|
+
nonce: Uint8Array;
|
|
59
|
+
}, session: SessionState): string;
|
|
60
|
+
export declare function addSkippedKey(session: SessionState, header: {
|
|
61
|
+
publicKey: Uint8Array;
|
|
62
|
+
nonce: Uint8Array;
|
|
63
|
+
}, messageKey: Uint8Array): void;
|
|
64
|
+
export declare function removeSkippedKey(session: SessionState, header: {
|
|
65
|
+
publicKey: Uint8Array;
|
|
66
|
+
nonce: Uint8Array;
|
|
67
|
+
}): void;
|
|
68
|
+
export declare function processSkippedKeys(session: SessionState): void;
|
|
69
|
+
export declare function handleSimultaneousSend(session: SessionState, isInitiator: boolean): void;
|
|
70
|
+
export declare function generateOPKPool(): void;
|
|
71
|
+
export declare function consumeOPKAtomically(userId: string): Uint8Array;
|
|
72
|
+
export declare function enforceDHRatchetPolicy(session: SessionState, remotePublicKey: Uint8Array, suspectedCompromise?: boolean): void;
|
|
73
|
+
/**
|
|
74
|
+
* Increment message counter and enforce policy.
|
|
75
|
+
*/
|
|
76
|
+
export declare function incrementMessageCounter(session: SessionState, remotePublicKey: Uint8Array): void;
|
|
77
|
+
/**
|
|
78
|
+
* Force a DH ratchet step to enable PCS.
|
|
79
|
+
* @param session - The current session state.
|
|
80
|
+
* @param remotePublicKey - The remote party's ephemeral public key.
|
|
81
|
+
*/
|
|
82
|
+
export declare function forceDHRatchet(session: SessionState, remotePublicKey: Uint8Array): void;
|
|
83
|
+
/**
|
|
84
|
+
* Trigger PCS recovery only after receiving a new DH public key.
|
|
85
|
+
* @param session - The current session state.
|
|
86
|
+
* @param remotePublicKey - The new DH public key from the remote party.
|
|
87
|
+
*/
|
|
88
|
+
export declare function receiveNewDHPublicKey(session: SessionState, remotePublicKey: Uint8Array): void;
|