p2party 0.8.0 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +202 -661
- package/README.md +736 -404
- package/THIRD_PARTY_NOTICES.md +458 -0
- package/docs/assets/p2party-cat.svg +1 -0
- package/docs/getting-started.md +203 -0
- package/docs/protocol-v4-security.md +158 -0
- package/docs/references.md +157 -0
- package/docs/session-api.md +457 -0
- package/docs/wire-format.md +103 -0
- package/examples/standalone-e2ee.ts +194 -0
- package/lib/api/signalingServerApi.d.ts +6 -7
- package/lib/api/webrtc/baseQuery.d.ts +1 -2
- package/lib/api/webrtc/disconnectFromAllRoomsQuery.d.ts +1 -2
- package/lib/api/webrtc/disconnectFromChannelLabelQuery.d.ts +1 -2
- package/lib/api/webrtc/disconnectFromPeerChannelLabelQuery.d.ts +1 -2
- package/lib/api/webrtc/disconnectFromPeerQuery.d.ts +3 -3
- package/lib/api/webrtc/disconnectFromRoomQuery.d.ts +1 -2
- package/lib/api/webrtc/disconnectQuery.d.ts +1 -2
- package/lib/api/webrtc/iceGeneration.d.ts +17 -0
- package/lib/api/webrtc/iceRepair.d.ts +10 -0
- package/lib/api/webrtc/index.d.ts +13 -13
- package/lib/api/webrtc/interfaces.d.ts +67 -11
- package/lib/api/webrtc/negotiationLock.d.ts +12 -0
- package/lib/api/webrtc/openChannelQuery.d.ts +1 -2
- package/lib/api/webrtc/pendingIceCandidates.d.ts +14 -0
- package/lib/api/webrtc/roomPeer.d.ts +11 -0
- package/lib/api/webrtc/sendMessageQuery.d.ts +7 -4
- package/lib/api/webrtc/setCandidateQuery.d.ts +1 -2
- package/lib/api/webrtc/setDescriptionQuery.d.ts +1 -2
- package/lib/cryptography/coverCell.d.ts +65 -0
- package/lib/cryptography/cpace.d.ts +41 -0
- package/lib/cryptography/ed25519.d.ts +0 -1
- package/lib/cryptography/hashStream.d.ts +15 -0
- package/lib/cryptography/hkdf.d.ts +3 -0
- package/lib/cryptography/identityCrossSig.d.ts +7 -0
- package/lib/cryptography/interfaces.d.ts +28 -13
- package/lib/cryptography/memory.d.ts +2 -4
- package/lib/cryptography/merkle.d.ts +0 -1
- package/lib/cryptography/mlkem.d.ts +117 -0
- package/lib/cryptography/mnemonic.d.ts +0 -1
- package/lib/cryptography/pqHealing.d.ts +268 -0
- package/lib/cryptography/pqHealingFrame.d.ts +39 -0
- package/lib/cryptography/pqMessageKey.d.ts +30 -0
- package/lib/cryptography/random.d.ts +22 -0
- package/lib/cryptography/ratchet.d.ts +146 -0
- package/lib/cryptography/testModule.d.ts +7 -0
- package/lib/cryptography/utils.d.ts +0 -1
- package/lib/cryptography/wasmLoader.d.ts +6 -1
- package/lib/cryptography/x25519.d.ts +15 -0
- package/lib/cryptography/x3dh.d.ts +9 -0
- package/lib/db/api.d.ts +54 -22
- package/lib/db/db.worker.d.ts +0 -1
- package/lib/db/ratchetWrap.d.ts +36 -0
- package/lib/db/src/getDB.d.ts +17 -4
- package/lib/db/types.d.ts +274 -32
- package/lib/db.worker.js +1 -2
- package/lib/handlers/chunkFrame.d.ts +13 -0
- package/lib/handlers/connectionSignal.d.ts +26 -0
- package/lib/handlers/coverEdge.d.ts +42 -0
- package/lib/handlers/coverRuntime.d.ts +91 -0
- package/lib/handlers/coverScheduler.d.ts +184 -0
- package/lib/handlers/coverTransfer.d.ts +46 -0
- package/lib/handlers/frameType.d.ts +10 -0
- package/lib/handlers/handleChallenge.d.ts +0 -1
- package/lib/handlers/handleConnectToPeer.d.ts +0 -1
- package/lib/handlers/handleHandshake.d.ts +74 -0
- package/lib/handlers/handleMessageQueueing.d.ts +53 -4
- package/lib/handlers/handleOpenChannel.d.ts +4 -2
- package/lib/handlers/handleQueuedIceCandidates.d.ts +0 -1
- package/lib/handlers/handleReadReceipt.d.ts +17 -4
- package/lib/handlers/handleReceiveMessage.d.ts +21 -3
- package/lib/handlers/handleSendMessage.d.ts +110 -2
- package/lib/handlers/handleWebSocketMessage.d.ts +1 -3
- package/lib/handlers/handshakeCore.d.ts +95 -0
- package/lib/handlers/messageChunkCrypto.d.ts +70 -0
- package/lib/handlers/peerRosterDelta.d.ts +12 -0
- package/lib/handlers/pqHealingOrchestrator.d.ts +46 -0
- package/lib/handlers/pqHealingRuntime.d.ts +82 -0
- package/lib/handlers/ratchetGate.d.ts +18 -0
- package/lib/handlers/ratchetPersist.d.ts +53 -0
- package/lib/handlers/receiptFrame.d.ts +18 -0
- package/lib/handlers/receiveMessageKeyLifetime.d.ts +24 -0
- package/lib/handlers/reconcile.d.ts +16 -0
- package/lib/handlers/transferAbort.d.ts +42 -0
- package/lib/index.d.ts +203 -46
- package/lib/index.js +1 -2
- package/lib/index.min.js +1 -2
- package/lib/index.mjs +1 -2
- package/lib/libcrypto.provenance.json +45 -0
- package/lib/libcrypto.wasm +0 -0
- package/lib/middleware/keyPairListenerMiddleware.d.ts +0 -1
- package/lib/middleware/roomListenerMiddleware.d.ts +0 -1
- package/lib/reducers/commonSlice.d.ts +0 -1
- package/lib/reducers/keyPairSlice.d.ts +0 -1
- package/lib/reducers/roomSlice.d.ts +65 -3
- package/lib/reducers/signalingServerSlice.d.ts +25 -4
- package/lib/roomInvite.d.ts +18 -0
- package/lib/roomPinAttempts.d.ts +12 -0
- package/lib/roomPinVault.d.ts +26 -0
- package/lib/roomPolicy.d.ts +60 -0
- package/lib/session.d.ts +110 -0
- package/lib/session.js +1 -0
- package/lib/session.mjs +1 -0
- package/lib/store.d.ts +40 -41
- package/lib/utils/channelLabel.d.ts +0 -1
- package/lib/utils/chunkBounds.d.ts +9 -0
- package/lib/utils/constants.d.ts +50 -6
- package/lib/utils/debug.d.ts +25 -0
- package/lib/utils/drainAndClose.d.ts +5 -0
- package/lib/utils/identityRole.d.ts +19 -0
- package/lib/utils/interfaces.d.ts +31 -43
- package/lib/utils/leafHash.d.ts +13 -0
- package/lib/utils/messageTypes.d.ts +0 -1
- package/lib/utils/metadata.d.ts +6 -1
- package/lib/utils/mutex.d.ts +24 -0
- package/lib/utils/protocolVersion.d.ts +5 -0
- package/lib/utils/receiptToken.d.ts +7 -0
- package/lib/utils/sendQueueKey.d.ts +2 -0
- package/lib/utils/signalingAuth.d.ts +8 -0
- package/lib/utils/signalingBounds.d.ts +9 -0
- package/lib/utils/splitToChunks.d.ts +7 -4
- package/lib/utils/uint8array.d.ts +1 -2
- package/lib/utils/waitForOpen.d.ts +3 -0
- package/lib/utils/zeroFree.d.ts +10 -0
- package/package.json +109 -49
- package/lib/api/signalingServerApi.d.ts.map +0 -1
- package/lib/api/webrtc/baseQuery.d.ts.map +0 -1
- package/lib/api/webrtc/disconnectFromAllRoomsQuery.d.ts.map +0 -1
- package/lib/api/webrtc/disconnectFromChannelLabelQuery.d.ts.map +0 -1
- package/lib/api/webrtc/disconnectFromPeerChannelLabelQuery.d.ts.map +0 -1
- package/lib/api/webrtc/disconnectFromPeerQuery.d.ts.map +0 -1
- package/lib/api/webrtc/disconnectFromRoomQuery.d.ts.map +0 -1
- package/lib/api/webrtc/disconnectQuery.d.ts.map +0 -1
- package/lib/api/webrtc/index.d.ts.map +0 -1
- package/lib/api/webrtc/interfaces.d.ts.map +0 -1
- package/lib/api/webrtc/openChannelQuery.d.ts.map +0 -1
- package/lib/api/webrtc/sendMessageQuery.d.ts.map +0 -1
- package/lib/api/webrtc/setCandidateQuery.d.ts.map +0 -1
- package/lib/api/webrtc/setDescriptionQuery.d.ts.map +0 -1
- package/lib/cryptography/chacha20poly1305.d.ts +0 -116
- package/lib/cryptography/chacha20poly1305.d.ts.map +0 -1
- package/lib/cryptography/ed25519.d.ts.map +0 -1
- package/lib/cryptography/interfaces.d.ts.map +0 -1
- package/lib/cryptography/memory.d.ts.map +0 -1
- package/lib/cryptography/merkle.d.ts.map +0 -1
- package/lib/cryptography/mnemonic.d.ts.map +0 -1
- package/lib/cryptography/utils.d.ts.map +0 -1
- package/lib/cryptography/wasmLoader.d.ts.map +0 -1
- package/lib/db/api.d.ts.map +0 -1
- package/lib/db/db.worker.d.ts.map +0 -1
- package/lib/db/src/getDB.d.ts.map +0 -1
- package/lib/db/types.d.ts.map +0 -1
- package/lib/db.worker.js.map +0 -1
- package/lib/handlers/handleChallenge.d.ts.map +0 -1
- package/lib/handlers/handleConnectToPeer.d.ts.map +0 -1
- package/lib/handlers/handleMessageQueueing.d.ts.map +0 -1
- package/lib/handlers/handleOpenChannel.d.ts.map +0 -1
- package/lib/handlers/handleQueuedIceCandidates.d.ts.map +0 -1
- package/lib/handlers/handleReadReceipt.d.ts.map +0 -1
- package/lib/handlers/handleReceiveMessage.d.ts.map +0 -1
- package/lib/handlers/handleSendMessage.d.ts.map +0 -1
- package/lib/handlers/handleWebSocketMessage.d.ts.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/index.min.js.map +0 -1
- package/lib/index.mjs.map +0 -1
- package/lib/middleware/keyPairListenerMiddleware.d.ts.map +0 -1
- package/lib/middleware/roomListenerMiddleware.d.ts.map +0 -1
- package/lib/reducers/commonSlice.d.ts.map +0 -1
- package/lib/reducers/keyPairSlice.d.ts.map +0 -1
- package/lib/reducers/roomSlice.d.ts.map +0 -1
- package/lib/reducers/signalingServerSlice.d.ts.map +0 -1
- package/lib/store.d.ts.map +0 -1
- package/lib/utils/allocators.d.ts +0 -22
- package/lib/utils/allocators.d.ts.map +0 -1
- package/lib/utils/channelLabel.d.ts.map +0 -1
- package/lib/utils/constants.d.ts.map +0 -1
- package/lib/utils/interfaces.d.ts.map +0 -1
- package/lib/utils/messageTypes.d.ts.map +0 -1
- package/lib/utils/metadata.d.ts.map +0 -1
- package/lib/utils/splitToChunks.d.ts.map +0 -1
- package/lib/utils/uint8array.d.ts.map +0 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { LibCrypto } from "./libcrypto";
|
|
2
|
+
import type { MlKemParameterSet, MlKemSuiteDescriptor } from "./mlkem";
|
|
3
|
+
export declare const PQ_CONTROL_FRAME_HEADER_BYTES: number;
|
|
4
|
+
export declare const PQ_CONTROL_FRAME_PLAINTEXT_BYTES: number;
|
|
5
|
+
export type PqControlDirection = "initiator-to-responder" | "responder-to-initiator";
|
|
6
|
+
export type PqControlFrameErrorCode = "authentication-failed" | "binding-mismatch" | "epoch-mismatch" | "invalid-direction" | "invalid-frame" | "invalid-padding" | "record-mismatch";
|
|
7
|
+
export declare class PqControlFrameError extends Error {
|
|
8
|
+
readonly code: PqControlFrameErrorCode;
|
|
9
|
+
constructor(code: PqControlFrameErrorCode, message: string);
|
|
10
|
+
}
|
|
11
|
+
export interface SealPqControlFrameOptions<P extends MlKemParameterSet = MlKemParameterSet> {
|
|
12
|
+
readonly module: LibCrypto;
|
|
13
|
+
readonly suite: Readonly<MlKemSuiteDescriptor<P>>;
|
|
14
|
+
readonly rootKey: Uint8Array;
|
|
15
|
+
readonly binding: Uint8Array;
|
|
16
|
+
readonly direction: PqControlDirection;
|
|
17
|
+
/** Epoch whose PQ root authenticates this frame. */
|
|
18
|
+
readonly keyEpoch: bigint;
|
|
19
|
+
/** Exact canonical OFFER, ADVANCE, or 64-byte ACK. */
|
|
20
|
+
readonly record: Uint8Array;
|
|
21
|
+
}
|
|
22
|
+
export interface OpenPqControlFrameOptions<P extends MlKemParameterSet = MlKemParameterSet> {
|
|
23
|
+
readonly module: LibCrypto;
|
|
24
|
+
readonly suite: Readonly<MlKemSuiteDescriptor<P>>;
|
|
25
|
+
readonly rootKey: Uint8Array;
|
|
26
|
+
readonly binding: Uint8Array;
|
|
27
|
+
/** Expected inbound direction; the opposite direction derives another key. */
|
|
28
|
+
readonly direction: PqControlDirection;
|
|
29
|
+
/** Exact expected authentication epoch. */
|
|
30
|
+
readonly keyEpoch: bigint;
|
|
31
|
+
readonly frame: Uint8Array;
|
|
32
|
+
}
|
|
33
|
+
/** Seal one exact canonical control record into a fixed-size v4 cell. */
|
|
34
|
+
export declare const sealPqControlFrame: <P extends MlKemParameterSet>(options: SealPqControlFrameOptions<P>) => Uint8Array;
|
|
35
|
+
/**
|
|
36
|
+
* Authenticate a fixed-size cell and return an owned exact canonical record.
|
|
37
|
+
* The caller supplies the expected edge, direction, suite, root, and epoch.
|
|
38
|
+
*/
|
|
39
|
+
export declare const openPqControlFrame: <P extends MlKemParameterSet>(options: OpenPqControlFrameOptions<P>) => Uint8Array;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { LibCrypto } from "./libcrypto";
|
|
2
|
+
import type { RatchetHeader } from "./ratchet";
|
|
3
|
+
import type { RatchetRootSuite } from "../utils/constants";
|
|
4
|
+
/**
|
|
5
|
+
* Context authenticated by the hybrid bootstrap and advanced by sparse PQ
|
|
6
|
+
* healing. `rootKey` is reusable epoch state: this module copies it and never
|
|
7
|
+
* wipes the caller's live buffer.
|
|
8
|
+
*/
|
|
9
|
+
export interface PqMessageKeyContext {
|
|
10
|
+
readonly rootKey: Uint8Array;
|
|
11
|
+
readonly binding: Uint8Array;
|
|
12
|
+
readonly rootSuite: RatchetRootSuite;
|
|
13
|
+
readonly epoch: bigint;
|
|
14
|
+
}
|
|
15
|
+
export declare const PQ_MESSAGE_KEY_BYTES: number;
|
|
16
|
+
export declare const PQ_MESSAGE_KEY_ROOT_BYTES = 32;
|
|
17
|
+
export declare const PQ_MESSAGE_KEY_BINDING_BYTES = 32;
|
|
18
|
+
/**
|
|
19
|
+
* Combine one classical Double-Ratchet message key with the current PQ epoch
|
|
20
|
+
* root:
|
|
21
|
+
*
|
|
22
|
+
* PRK = HKDF-Extract-SHA512(salt = pqRoot, IKM = classicalMessageKey)
|
|
23
|
+
* key = HKDF-Expand-SHA512(PRK, canonicalContext, 32)
|
|
24
|
+
*
|
|
25
|
+
* Ownership is deliberate: `classicalMessageKey` is consumed and zeroed on
|
|
26
|
+
* every success/failure path. The live `context.rootKey` remains reusable; only
|
|
27
|
+
* an owned copy and the transient PRK are wiped. Callers that need the
|
|
28
|
+
* classical key for multiple chunks must pass an owned copy on each call.
|
|
29
|
+
*/
|
|
30
|
+
export declare const combinePqMessageKey: (classicalMessageKey: Uint8Array, context: PqMessageKeyContext, header: RatchetHeader, module: LibCrypto) => Uint8Array;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supply Emscripten/libsodium with one Web Crypto backed unsigned word.
|
|
3
|
+
*
|
|
4
|
+
* Passing this callback explicitly keeps the generated glue independent of
|
|
5
|
+
* CommonJS `require("crypto")`, which is unavailable in Node ESM. Browsers,
|
|
6
|
+
* workers, Node 20+, and Bun all expose the same Web Crypto boundary.
|
|
7
|
+
*/
|
|
8
|
+
export declare const secureRandomUint32: () => number;
|
|
9
|
+
/**
|
|
10
|
+
* Fill any Uint8Array — including a view into WebAssembly.Memory — without
|
|
11
|
+
* handing its backing buffer to WebCrypto.
|
|
12
|
+
*
|
|
13
|
+
* Chromium 147/149 rejects BufferSource inputs backed by resizable
|
|
14
|
+
* WebAssembly memory. A fresh length-constructed Uint8Array always owns an
|
|
15
|
+
* ordinary fixed ArrayBuffer, so WebCrypto fills that temporary and we copy
|
|
16
|
+
* into the caller's destination. Randomness is secret key material at every
|
|
17
|
+
* current call site; wipe the temporary on success and failure.
|
|
18
|
+
*
|
|
19
|
+
* The optional filler exists for deterministic boundary tests and is not a
|
|
20
|
+
* protocol entropy-injection API.
|
|
21
|
+
*/
|
|
22
|
+
export declare const fillRandomBytesInto: (destination: Uint8Array, fill?: (temporary: Uint8Array) => void) => void;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { LibCrypto } from "./libcrypto";
|
|
2
|
+
import type { RatchetRootSuite } from "../utils/constants";
|
|
3
|
+
/**
|
|
4
|
+
* Live Double Ratchet state for one `(roomId, peerPublicKey)` edge. Every key
|
|
5
|
+
* field is a plain-JS `Uint8Array` secret (the WASM heap holds no ratchet state
|
|
6
|
+
* — see design §9): `rootKey`, both chain keys, `dhSelfSec`, and each value in
|
|
7
|
+
* `skipped`. Stage 3 wraps these at rest; here they are live plaintext.
|
|
8
|
+
*
|
|
9
|
+
* The ratchet advances **per logical message**, never per chunk. `Ns`/`Nr` are
|
|
10
|
+
* the send/receive message counters in the current chains; `PN` is the length
|
|
11
|
+
* of the previous sending chain (folded into inbound headers so the peer can
|
|
12
|
+
* skip the tail of a superseded chain).
|
|
13
|
+
*/
|
|
14
|
+
export interface RatchetState {
|
|
15
|
+
/** Authenticated bootstrap-suite provenance; immutable for this edge. */
|
|
16
|
+
rootSuite: RatchetRootSuite;
|
|
17
|
+
rootKey: Uint8Array;
|
|
18
|
+
sendingChainKey: Uint8Array | null;
|
|
19
|
+
receivingChainKey: Uint8Array | null;
|
|
20
|
+
dhSelfPub: Uint8Array;
|
|
21
|
+
dhSelfSec: Uint8Array;
|
|
22
|
+
dhRemotePub: Uint8Array | null;
|
|
23
|
+
Ns: number;
|
|
24
|
+
Nr: number;
|
|
25
|
+
PN: number;
|
|
26
|
+
skipped: Map<string, Uint8Array>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Cleartext per-message ratchet header. Rides the frame so the receiver can
|
|
30
|
+
* derive the message key before decrypting: `dhPub` drives the DH ratchet, `N`
|
|
31
|
+
* is this message's index in the sender's current chain, `PN` is the length of
|
|
32
|
+
* the sender's previous chain (for skip-on-DH-step).
|
|
33
|
+
*/
|
|
34
|
+
export interface RatchetHeader {
|
|
35
|
+
dhPub: Uint8Array;
|
|
36
|
+
N: number;
|
|
37
|
+
PN: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Serializable projection of a `RatchetState` — maps 1:1 onto the wrapped
|
|
41
|
+
* secret fields of the `RatchetSession` IndexedDB row (Stage 3). All buffers
|
|
42
|
+
* are owned copies; the skipped map is flattened to an array keyed by
|
|
43
|
+
* `(dhPub, n)`.
|
|
44
|
+
*/
|
|
45
|
+
export interface RatchetSessionSecrets {
|
|
46
|
+
rootSuite: RatchetRootSuite;
|
|
47
|
+
rootKey: ArrayBuffer;
|
|
48
|
+
sendingChainKey: ArrayBuffer | null;
|
|
49
|
+
receivingChainKey: ArrayBuffer | null;
|
|
50
|
+
dhSelfPub: ArrayBuffer;
|
|
51
|
+
dhSelfSec: ArrayBuffer;
|
|
52
|
+
dhRemotePub: ArrayBuffer | null;
|
|
53
|
+
Ns: number;
|
|
54
|
+
Nr: number;
|
|
55
|
+
PN: number;
|
|
56
|
+
skippedMessageKeys: Array<{
|
|
57
|
+
dhPub: ArrayBuffer;
|
|
58
|
+
n: number;
|
|
59
|
+
messageKey: ArrayBuffer;
|
|
60
|
+
}>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Seed a ratchet from the handshake's 32-byte, transcript-bound hybrid root.
|
|
64
|
+
*
|
|
65
|
+
* Initiator/responder asymmetry (must match so the two sides interoperate):
|
|
66
|
+
* - The **responder** (`amInitiator=false`, `remoteDhPub=null`) generates its
|
|
67
|
+
* DH keypair and stops: both chains stay `null`. After the initiator's
|
|
68
|
+
* initial DH pub is authenticated, `primeResponderRatchet` performs the
|
|
69
|
+
* receive-side DH step without consuming a message key and opens both
|
|
70
|
+
* chains. A lower-level caller that does not prime retains the legacy
|
|
71
|
+
* first-inbound-message behavior.
|
|
72
|
+
* - The **initiator** (`amInitiator=true`) is handed the responder's
|
|
73
|
+
* `dhSelfPub` as `remoteDhPub`, runs one `kdf_rk` over `DH(selfSec, remote)`
|
|
74
|
+
* to advance the root and open its **sending** chain immediately, so it can
|
|
75
|
+
* send message 0 before ever hearing back. Its receiving chain opens on the
|
|
76
|
+
* responder's first reply (a DH step against the responder's fresh pub).
|
|
77
|
+
*/
|
|
78
|
+
export declare const initRatchet: (rootSeed: Uint8Array, amInitiator: boolean, remoteDhPub: Uint8Array | null, module: LibCrypto, rootSuite?: RatchetRootSuite) => RatchetState;
|
|
79
|
+
/**
|
|
80
|
+
* Advance the sending chain by one message. Returns the message key (caller's to
|
|
81
|
+
* consume/wipe) and the cleartext header to put on the wire. The consumed chain
|
|
82
|
+
* key is wiped once its successor is derived.
|
|
83
|
+
*/
|
|
84
|
+
export declare const ratchetEncrypt: (state: RatchetState, module: LibCrypto) => {
|
|
85
|
+
messageKey: Uint8Array;
|
|
86
|
+
header: RatchetHeader;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Complete the responder's handshake-time ratchet bootstrap after the
|
|
90
|
+
* initiator's initial DH public key has been authenticated by key confirmation.
|
|
91
|
+
*
|
|
92
|
+
* This is the receive-side DH step that an unprimed responder would otherwise
|
|
93
|
+
* perform on the initiator's first message, deliberately stopped before
|
|
94
|
+
* `kdfCk`: it opens the receiving chain from
|
|
95
|
+
* `DH(initialResponder, initialInitiator)`, rotates the responder DH keypair,
|
|
96
|
+
* and opens the sending chain from `DH(rotatedResponder, initialInitiator)`.
|
|
97
|
+
* Consequently both peers can send message 0 immediately after the handshake,
|
|
98
|
+
* including simultaneously, without consuming or skipping a message key.
|
|
99
|
+
*/
|
|
100
|
+
export declare const primeResponderRatchet: (state: RatchetState, initiatorDhPub: Uint8Array, module: LibCrypto) => void;
|
|
101
|
+
/**
|
|
102
|
+
* Advance the receiving side for an inbound header and return its message key.
|
|
103
|
+
*
|
|
104
|
+
* CAUTION — this function is UNAUTHENTICATED: it mutates `state` (and may fire
|
|
105
|
+
* a DH-ratchet step) from the cleartext header alone, before the AEAD tag is
|
|
106
|
+
* ever checked (authentication happens later, in Stage 5). A duplicate or
|
|
107
|
+
* replayed message that is NOT a stored skipped key — e.g. a retransmit of a
|
|
108
|
+
* message from a chain the session has since stepped past — is NOT detected
|
|
109
|
+
* here: it falls through to the "new peer DH pub" case below and can fire a
|
|
110
|
+
* spurious backward DH-step, permanently desyncing the session. This is
|
|
111
|
+
* reachable by our own retransmit/reconcile layer, so callers MUST:
|
|
112
|
+
* 1. Dedup already-seen `(header.dhPub, header.N)` pairs BEFORE calling this
|
|
113
|
+
* function — never invoke it a second time for a message already
|
|
114
|
+
* processed.
|
|
115
|
+
* 2. Call `ratchetDecrypt` on a CLONE of `state` (via
|
|
116
|
+
* `deserializeRatchet(serializeRatchet(state))`) and commit that clone as
|
|
117
|
+
* the live state ONLY after the returned message key successfully
|
|
118
|
+
* authenticates the AEAD ciphertext. If authentication fails, discard the
|
|
119
|
+
* clone and leave the live state untouched.
|
|
120
|
+
* `ratchetDecrypt` itself performs neither dedup nor authentication — the safe
|
|
121
|
+
* integration of both rules is wired in Stage 4/5, not here.
|
|
122
|
+
*
|
|
123
|
+
* Against that (untrusted) header, handles three cases: (a) a stored skipped
|
|
124
|
+
* key — out-of-order delivery within an already-seen chain, (b) a new peer DH
|
|
125
|
+
* pub — skip the old chain's tail, then DH-step, (c) a forward message in the
|
|
126
|
+
* current chain — skip any gap, then derive. The consumed chain key is wiped
|
|
127
|
+
* once its successor is derived.
|
|
128
|
+
*/
|
|
129
|
+
export declare const ratchetDecrypt: (state: RatchetState, header: RatchetHeader, module: LibCrypto) => Uint8Array;
|
|
130
|
+
/**
|
|
131
|
+
* Snapshot all live state (including the skipped map + DH keys) to owned
|
|
132
|
+
* buffers for at-rest persistence. Does NOT wipe the source state — the caller
|
|
133
|
+
* keeps using it.
|
|
134
|
+
*/
|
|
135
|
+
export declare const serializeRatchet: (state: RatchetState) => RatchetSessionSecrets;
|
|
136
|
+
/** Rebuild a live `RatchetState` from a persisted snapshot. */
|
|
137
|
+
export declare const deserializeRatchet: (s: RatchetSessionSecrets) => RatchetState;
|
|
138
|
+
/** Deep-clone a live ratchet state into independently owned key buffers. */
|
|
139
|
+
export declare const cloneRatchet: (state: RatchetState) => RatchetState;
|
|
140
|
+
/** Wipe every secret-bearing buffer owned by a ratchet state. */
|
|
141
|
+
export declare const wipeRatchet: (state: RatchetState) => void;
|
|
142
|
+
/**
|
|
143
|
+
* Replace a live state with an independently owned authenticated successor.
|
|
144
|
+
* The superseded live secrets are wiped before ownership moves from `next`.
|
|
145
|
+
*/
|
|
146
|
+
export declare const adoptRatchet: (live: RatchetState, next: RatchetState) => void;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LibCrypto } from "./libcrypto";
|
|
2
|
+
/**
|
|
3
|
+
* Instantiate the locally-built libcrypto.wasm for unit tests, bypassing the
|
|
4
|
+
* CDN + SRI fetch in wasmLoader.ts. 32 pages == 2 MiB == INITIAL_MEMORY; growth
|
|
5
|
+
* is off so every op fits the largest-single-op budget.
|
|
6
|
+
*/
|
|
7
|
+
export declare const loadTestModule: () => Promise<LibCrypto>;
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Point the browser root at a self-hosted copy of this release's exact WASM.
|
|
3
|
+
* The build-pinned SRI is still enforced. Configure once, before connect() or
|
|
4
|
+
* any other operation first loads cryptography.
|
|
5
|
+
*/
|
|
6
|
+
export declare const setWasmSourceUrl: (source: string | URL) => void;
|
|
1
7
|
export declare const wasmLoader: (wasmMemory: WebAssembly.Memory) => Promise<import("./libcrypto").LibCrypto>;
|
|
2
|
-
//# sourceMappingURL=wasmLoader.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LibCrypto } from "./libcrypto";
|
|
2
|
+
export interface X25519KeyPair {
|
|
3
|
+
publicKey: Uint8Array;
|
|
4
|
+
secretKey: Uint8Array;
|
|
5
|
+
}
|
|
6
|
+
export declare const x25519Keypair: (module: LibCrypto) => X25519KeyPair;
|
|
7
|
+
/**
|
|
8
|
+
* Async, optional-module wrapper over `x25519Keypair`, mirroring `ed25519.newKeyPair`:
|
|
9
|
+
* self-loads a LibCrypto module (sizing its own fixed 2 MiB heap) when none is passed,
|
|
10
|
+
* so identity-generation call sites can mint the dedicated X25519 identity keypair the
|
|
11
|
+
* same way they call `newKeyPair()`. The keypair is random (`_x25519_keypair` draws its
|
|
12
|
+
* own entropy); `x25519Keypair` copies the secret out and zero-frees the wasm scratch.
|
|
13
|
+
*/
|
|
14
|
+
export declare const newX25519KeyPair: (module?: LibCrypto) => Promise<X25519KeyPair>;
|
|
15
|
+
export declare const x25519Dh: (secretKey: Uint8Array, publicKey: Uint8Array, module: LibCrypto) => Uint8Array;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { LibCrypto } from "./libcrypto";
|
|
2
|
+
/**
|
|
3
|
+
* No-PIN identity-mixed ephemeral DH. Mixes
|
|
4
|
+
* DH(IK_a, EK_b) || DH(EK_a, IK_b) || DH(EK_a, EK_b)
|
|
5
|
+
* (initiator orientation) then HKDF-SHA512 to a 32-byte root seed. The responder
|
|
6
|
+
* passes amInitiator=false; the three DH calls are re-ordered so both sides
|
|
7
|
+
* concatenate the identical shared values.
|
|
8
|
+
*/
|
|
9
|
+
export declare const deriveInteractive3dhSecret: (idSelfSec: Uint8Array, idPeerPub: Uint8Array, ephSelfSec: Uint8Array, ephPeerPub: Uint8Array, amInitiator: boolean, module: LibCrypto) => Uint8Array;
|
package/lib/db/api.d.ts
CHANGED
|
@@ -1,35 +1,67 @@
|
|
|
1
|
-
import type { Chunk, NewChunk, SendQueue } from "./types";
|
|
1
|
+
import type { Chunk, ReceiveChunk, NewChunk, NewChunkSelector, SendQueue, RatchetSession, IdentityEd25519, IdentityX25519 } from "./types";
|
|
2
2
|
export declare const getDBAddressBookEntry: (peerId?: string, peerPublicKey?: string) => Promise<import("./types").UsernamedPeer | undefined>;
|
|
3
3
|
export declare const getAllDBAddressBookEntries: () => Promise<import("./types").UsernamedPeer[]>;
|
|
4
|
-
export declare const setDBAddressBookEntry: (username: string, peerId: string, peerPublicKey: string) => Promise<
|
|
4
|
+
export declare const setDBAddressBookEntry: (username: string, peerId: string, peerPublicKey: string) => Promise<undefined>;
|
|
5
5
|
export declare const deleteDBAddressBookEntry: (username?: string, peerId?: string, peerPublicKey?: string) => Promise<string>;
|
|
6
|
+
/**
|
|
7
|
+
* Fails CLOSED. This is the sole admission gate on every connection path, and
|
|
8
|
+
* an indeterminate answer must never be reported as "not blacklisted" — that
|
|
9
|
+
* would let a storage fault silently readmit every blocked peer. The denial is
|
|
10
|
+
* scoped to the one peer being checked, so a transient fault cannot lock the
|
|
11
|
+
* whole room out.
|
|
12
|
+
*/
|
|
6
13
|
export declare const getDBPeerIsBlacklisted: (peerId?: string, peerPublicKey?: string) => Promise<boolean>;
|
|
7
14
|
export declare const getAllDBBlacklisted: () => Promise<import("./types").BlacklistedPeer[]>;
|
|
8
|
-
export declare const setDBPeerInBlacklist: (peerId: string, peerPublicKey: string) => Promise<
|
|
9
|
-
export declare const deleteDBPeerFromBlacklist: (peerId?: string, peerPublicKey?: string) => Promise<
|
|
15
|
+
export declare const setDBPeerInBlacklist: (peerId: string, peerPublicKey: string) => Promise<undefined>;
|
|
16
|
+
export declare const deleteDBPeerFromBlacklist: (peerId?: string, peerPublicKey?: string) => Promise<undefined>;
|
|
10
17
|
export declare const getAllDBUniqueRooms: () => Promise<import("./types").UniqueRoom[]>;
|
|
11
|
-
export declare const setDBUniqueRoom: (roomUrl: string, roomId: string) => Promise<
|
|
18
|
+
export declare const setDBUniqueRoom: (roomUrl: string, roomId: string, roomPolicy: ArrayBuffer) => Promise<undefined>;
|
|
12
19
|
export declare const getDBMessageData: (merkleRootHex?: string, hashHex?: string) => Promise<import("./types").MessageData | undefined>;
|
|
13
20
|
export declare const getDBRoomMessageData: (roomId: string) => Promise<import("./types").MessageData[]>;
|
|
21
|
+
export declare const getDBRoomStats: (roomId: string) => Promise<import("./types").RoomStats>;
|
|
14
22
|
export declare const setDBRoomMessageData: (roomId: string, merkleRootHex: string, sha512Hex: string, fromPeerId: string, chunkSize: number, totalSize: number, messageType: number, // MessageType,
|
|
15
|
-
filename: string, channelLabel: string, timestamp: number) => Promise<
|
|
16
|
-
export declare const getDBChunk: (
|
|
17
|
-
export declare const existsDBChunk: (
|
|
18
|
-
export declare const getDBNewChunk: (
|
|
19
|
-
export declare const
|
|
23
|
+
filename: string, channelLabel: string, timestamp: number, transferId?: string) => Promise<undefined>;
|
|
24
|
+
export declare const getDBChunk: (merkleRootHex: string, chunkIndex: number) => Promise<ArrayBuffer | undefined>;
|
|
25
|
+
export declare const existsDBChunk: (merkleRootHex: string, chunkIndex: number) => Promise<boolean>;
|
|
26
|
+
export declare const getDBNewChunk: (transferId: string, chunkIndex: number) => Promise<NewChunk | undefined>;
|
|
27
|
+
export declare const getDBNewChunkByReceipt: (merkleRootHex: string, receiptTokenHex: string) => Promise<NewChunk | undefined>;
|
|
28
|
+
export declare const existsDBNewChunk: (transferId: string, chunkIndex: number) => Promise<boolean>;
|
|
20
29
|
export declare const getDBSendQueue: (label: string, toPeerId: string) => Promise<SendQueue[]>;
|
|
21
30
|
export declare const getDBAllChunks: (merkleRootHex?: string, hashHex?: string) => Promise<Chunk[]>;
|
|
31
|
+
export declare const getDBAllChunkLeafHashes: (merkleRootHex: string) => Promise<import("./types").ChunkLeafHash[]>;
|
|
32
|
+
export declare const assembleToOPFS: (merkleRootHex: string, totalSize: number, filename: string, mimeType: string) => Promise<File | null>;
|
|
22
33
|
export declare const getDBAllChunksCount: (merkleRootHex?: string, hashHex?: string) => Promise<number>;
|
|
23
|
-
export declare const setDBChunk: (chunk: Chunk) => Promise<
|
|
24
|
-
export declare const
|
|
25
|
-
export declare const
|
|
26
|
-
export declare const
|
|
27
|
-
|
|
34
|
+
export declare const setDBChunk: (chunk: Chunk) => Promise<undefined>;
|
|
35
|
+
export declare const storeReceiveChunk: (chunk: ReceiveChunk) => Promise<import("./types").ReceiveChunkStoreResult>;
|
|
36
|
+
export declare const getReceiveFile: (merkleRootHex: string, totalSize: number, filename: string, mimeType: string) => Promise<File | null>;
|
|
37
|
+
export declare const closeReceiveFile: (merkleRootHex: string) => Promise<undefined>;
|
|
38
|
+
/**
|
|
39
|
+
* Atomically retires one inbound transfer behind the same per-Merkle worker
|
|
40
|
+
* lock used by storeReceiveChunk. This is the cancellation primitive: a chunk
|
|
41
|
+
* write that was already in flight completes first and is then removed, while
|
|
42
|
+
* later writes are stopped by the channel's AbortSignal.
|
|
43
|
+
*/
|
|
44
|
+
export declare const deleteReceiveTransfer: (merkleRootHex: string) => Promise<undefined>;
|
|
45
|
+
export declare const getDBAllNewChunks: (selector: NewChunkSelector) => Promise<NewChunk[]>;
|
|
46
|
+
export declare const getDBAllNewChunksCount: (transferId: string) => Promise<number>;
|
|
47
|
+
export declare const setDBNewChunk: (chunk: NewChunk) => Promise<undefined>;
|
|
48
|
+
export declare const setDBSendQueue: (item: SendQueue) => Promise<undefined>;
|
|
28
49
|
export declare const countDBSendQueue: (label: string, toPeerId: string) => Promise<number>;
|
|
29
|
-
export declare const deleteDBChunk: (hashHex: string, chunkIndex?: number) => Promise<
|
|
30
|
-
export declare const deleteDBNewChunk: (
|
|
31
|
-
export declare const deleteDBMessageData: (merkleRootHex: string) => Promise<
|
|
32
|
-
export declare const deleteDBUniqueRoom: (roomId: string) => Promise<
|
|
33
|
-
export declare const deleteDB: () => Promise<
|
|
34
|
-
export declare const deleteDBSendQueue: (label: string, toPeerId: string, position?: number) => Promise<
|
|
35
|
-
|
|
50
|
+
export declare const deleteDBChunk: (hashHex: string, chunkIndex?: number) => Promise<undefined>;
|
|
51
|
+
export declare const deleteDBNewChunk: (selector: NewChunkSelector) => Promise<undefined>;
|
|
52
|
+
export declare const deleteDBMessageData: (merkleRootHex: string) => Promise<undefined>;
|
|
53
|
+
export declare const deleteDBUniqueRoom: (roomId: string) => Promise<undefined>;
|
|
54
|
+
export declare const deleteDB: () => Promise<undefined>;
|
|
55
|
+
export declare const deleteDBSendQueue: (label: string, toPeerId: string, position?: number) => Promise<undefined>;
|
|
56
|
+
export declare const getRatchetSession: (roomId: string, peerPublicKey: string) => Promise<RatchetSession | undefined>;
|
|
57
|
+
export declare const setRatchetSession: (session: RatchetSession) => Promise<undefined>;
|
|
58
|
+
export declare const deleteRatchetSession: (roomId: string, peerPublicKey: string) => Promise<undefined>;
|
|
59
|
+
export declare const getPinAttemptState: (roomId: string, peerIdentityEd25519: string) => Promise<import("./types").PinAttemptState | undefined>;
|
|
60
|
+
export declare const incrementPinAttemptState: (roomId: string, peerIdentityEd25519: string, now: number, maxImmediateAttempts: number, baseMs: number, maxMs: number) => Promise<import("./types").PinAttemptState>;
|
|
61
|
+
export declare const deletePinAttemptState: (roomId: string, peerIdentityEd25519?: string) => Promise<undefined>;
|
|
62
|
+
export declare const getIdentityX25519: () => Promise<IdentityX25519 | undefined>;
|
|
63
|
+
export declare const setIdentityX25519: (identity: IdentityX25519) => Promise<undefined>;
|
|
64
|
+
export declare const deleteIdentityX25519: () => Promise<undefined>;
|
|
65
|
+
export declare const getIdentityEd25519: () => Promise<IdentityEd25519 | undefined>;
|
|
66
|
+
export declare const setIdentityEd25519: (identity: IdentityEd25519) => Promise<undefined>;
|
|
67
|
+
export declare const deleteIdentityEd25519: () => Promise<undefined>;
|
package/lib/db/db.worker.d.ts
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { RatchetSession } from "./types";
|
|
2
|
+
export declare const RATCHET_WRAP_VERSION = 2;
|
|
3
|
+
/** Bounded canonical v4 PQ/outbox/active-key checkpoint (plaintext bytes). */
|
|
4
|
+
export declare const MAX_EDGE_CRYPTO_STATE_BYTES: number;
|
|
5
|
+
/**
|
|
6
|
+
* Best-effort rollback/equivocation detection for one running client context.
|
|
7
|
+
*
|
|
8
|
+
* Callers pass an instance to `unwrapRatchetSession`; a successfully
|
|
9
|
+
* authenticated record may advance `updatedAt`, but may not move backwards or
|
|
10
|
+
* present different ciphertext at the same timestamp. `forget` is required
|
|
11
|
+
* when an edge is intentionally deleted/re-established.
|
|
12
|
+
*
|
|
13
|
+
* This cannot detect replay of an older *complete* IndexedDB snapshot after a
|
|
14
|
+
* full browser restart: WebCrypto/IndexedDB expose no trusted monotonic
|
|
15
|
+
* counter. Durable cross-restart detection needs an independently protected
|
|
16
|
+
* high-water head (or a remote/trusted anchor), atomically advanced with the
|
|
17
|
+
* ratchet row. AES-GCM alone cannot provide that property.
|
|
18
|
+
*/
|
|
19
|
+
export declare class RatchetRollbackGuard {
|
|
20
|
+
#private;
|
|
21
|
+
acceptAuthenticated(s: RatchetSession): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Advance the in-process high-water mark after, and only after, a local
|
|
24
|
+
* wrapped row was durably stored. The record's canonical metadata and every
|
|
25
|
+
* envelope shape/record ID are validated, but the locally produced
|
|
26
|
+
* ciphertext is intentionally not decrypted.
|
|
27
|
+
*/
|
|
28
|
+
rememberTrustedWrite(wrapped: RatchetSession): Promise<void>;
|
|
29
|
+
forget(roomId: string, peerPublicKey: string): void;
|
|
30
|
+
clear(): void;
|
|
31
|
+
}
|
|
32
|
+
export declare function getWrapKey(): Promise<CryptoKey>;
|
|
33
|
+
export declare function wrapSecret(key: CryptoKey, bytes: ArrayBuffer): Promise<ArrayBuffer>;
|
|
34
|
+
export declare function unwrapSecret(key: CryptoKey, blob: ArrayBuffer): Promise<ArrayBuffer>;
|
|
35
|
+
export declare function wrapRatchetSession(s: RatchetSession, key: CryptoKey): Promise<RatchetSession>;
|
|
36
|
+
export declare function unwrapRatchetSession(s: RatchetSession, key: CryptoKey, rollbackGuard?: RatchetRollbackGuard): Promise<RatchetSession>;
|
package/lib/db/src/getDB.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { DBSchema, IDBPDatabase } from "idb";
|
|
2
|
-
import type { MessageData, Chunk, SendQueue, AddressBook, BlacklistedPeer, UniqueRoom, NewChunk } from "../types";
|
|
2
|
+
import type { MessageData, Chunk, SendQueue, AddressBook, BlacklistedPeer, UniqueRoom, NewChunk, RatchetSession, StoredIdentityX25519, StoredIdentityEd25519, PinAttemptState } from "../types";
|
|
3
3
|
export declare const dbName = "p2party";
|
|
4
|
-
export declare const dbVersion =
|
|
4
|
+
export declare const dbVersion = 19;
|
|
5
5
|
export interface RepoSchema extends DBSchema {
|
|
6
6
|
addressBook: {
|
|
7
7
|
value: AddressBook;
|
|
@@ -51,9 +51,10 @@ export interface RepoSchema extends DBSchema {
|
|
|
51
51
|
value: NewChunk;
|
|
52
52
|
key: [string, number];
|
|
53
53
|
indexes: {
|
|
54
|
+
transferId: string;
|
|
54
55
|
hash: string;
|
|
55
56
|
merkleRoot: string;
|
|
56
|
-
|
|
57
|
+
receiptScope: [string, string];
|
|
57
58
|
};
|
|
58
59
|
};
|
|
59
60
|
sendQueue: {
|
|
@@ -63,6 +64,18 @@ export interface RepoSchema extends DBSchema {
|
|
|
63
64
|
labelPeer: string;
|
|
64
65
|
};
|
|
65
66
|
};
|
|
67
|
+
ratchetSessions: {
|
|
68
|
+
value: RatchetSession;
|
|
69
|
+
key: [string, string];
|
|
70
|
+
indexes: {
|
|
71
|
+
peerId: string;
|
|
72
|
+
peerPublicKey: string;
|
|
73
|
+
roomId: string;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
meta: {
|
|
77
|
+
value: CryptoKey | StoredIdentityX25519 | StoredIdentityEd25519 | PinAttemptState;
|
|
78
|
+
key: string;
|
|
79
|
+
};
|
|
66
80
|
}
|
|
67
81
|
export declare function getDB(): Promise<IDBPDatabase<RepoSchema>>;
|
|
68
|
-
//# sourceMappingURL=getDB.d.ts.map
|