@floegence/flowersec-core 3.2.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +57 -200
  2. package/THIRD_PARTY_NOTICES.md +11 -11
  3. package/dist/browser/index.d.ts +0 -5
  4. package/dist/browser/index.js +0 -3
  5. package/dist/cli.js +93 -64
  6. package/dist/facade.d.ts +0 -17
  7. package/dist/facade.js +0 -11
  8. package/dist/generated/unicode151.d.ts +2 -0
  9. package/dist/generated/unicode151.js +83 -0
  10. package/dist/interop/proxyServerPeer.js +34 -20
  11. package/dist/interop/serverParityPeer.js +11 -252
  12. package/dist/node/acceptor.d.ts +1 -57
  13. package/dist/node/acceptor.js +3 -420
  14. package/dist/node/acceptorV3.js +2 -2
  15. package/dist/node/connectSessionV3.js +2 -2
  16. package/dist/node/index.d.ts +2 -17
  17. package/dist/node/index.js +1 -9
  18. package/dist/node/nativeTransportAddon.d.ts +7 -36
  19. package/dist/node/nativeTransportAddon.js +7 -27
  20. package/dist/node/proxyServer.d.ts +2 -2
  21. package/dist/node/proxyServer.js +2 -2
  22. package/dist/node/rawQuicAdapterV3.d.ts +2 -2
  23. package/dist/node/rawQuicAdapterV3.js +1 -1
  24. package/dist/node/rawQuicServerV3.d.ts +2 -2
  25. package/dist/node/rawQuicServerV3.js +1 -1
  26. package/dist/node/rawQuicTls.d.ts +2 -0
  27. package/dist/node/{rawQuicAdapter.js → rawQuicTls.js} +30 -45
  28. package/dist/node/tunnelRuntimeV3.js +2 -2
  29. package/dist/proxy/index.d.ts +1 -1
  30. package/dist/proxy/index.js +1 -1
  31. package/dist/proxy/integration.d.ts +5 -5
  32. package/dist/proxy/integration.js +2 -2
  33. package/dist/proxy/runtime.js +11 -26
  34. package/dist/proxy/serviceWorkerRuntime.d.ts +7 -0
  35. package/dist/proxy/serviceWorkerRuntime.js +75 -0
  36. package/dist/proxy/types.d.ts +0 -1
  37. package/dist/proxy/windowBridge.d.ts +2 -0
  38. package/dist/proxy/windowBridge.js +53 -6
  39. package/dist/public/contract.d.ts +1 -3
  40. package/dist/public/streamHandlers.d.ts +0 -3
  41. package/dist/public/streamHandlers.js +2 -4
  42. package/dist/public/streamMetadata.js +2 -2
  43. package/dist/runtime/errors.d.ts +2 -2
  44. package/dist/testSupport/tlsFixture.d.ts +2 -0
  45. package/dist/testSupport/tlsFixture.js +8 -0
  46. package/dist/utils/errors.d.ts +0 -2
  47. package/dist/utils/errors.js +0 -80
  48. package/dist/v3/artifact.js +17 -3
  49. package/dist/v3/carrier.d.ts +2 -2
  50. package/dist/v3/controller.js +2 -6
  51. package/dist/v3/protocol.js +2 -103
  52. package/dist/v3/retryDisposition.d.ts +2 -2
  53. package/dist/v3/retryDisposition.js +0 -7
  54. package/dist/v3/security.d.ts +1 -10
  55. package/dist/v3/security.js +2 -9
  56. package/dist/vendor/tr46.d.ts +4 -0
  57. package/dist/vendor/tr46.js +37 -32
  58. package/package.json +19 -17
  59. package/sbom/cyclonedx.json +62 -62
  60. package/sbom/spdx.json +65 -65
  61. package/dist/browser/connectSession.d.ts +0 -13
  62. package/dist/browser/connectSession.js +0 -40
  63. package/dist/browser/runtimeCapability.d.ts +0 -13
  64. package/dist/browser/runtimeCapability.js +0 -27
  65. package/dist/browser/sessionRuntime.d.ts +0 -2
  66. package/dist/browser/sessionRuntime.js +0 -21
  67. package/dist/browser/v2.d.ts +0 -3
  68. package/dist/browser/v2.js +0 -2
  69. package/dist/browser/webTransportClient.d.ts +0 -8
  70. package/dist/browser/webTransportClient.js +0 -55
  71. package/dist/connectionController.d.ts +0 -55
  72. package/dist/connectionController.js +0 -441
  73. package/dist/connector/adapters/rawQuicCandidate.d.ts +0 -5
  74. package/dist/connector/adapters/rawQuicCandidate.js +0 -71
  75. package/dist/connector/adapters/webSocketCandidate.d.ts +0 -7
  76. package/dist/connector/adapters/webSocketCandidate.js +0 -116
  77. package/dist/connector/adapters/webTransportCandidate.d.ts +0 -5
  78. package/dist/connector/adapters/webTransportCandidate.js +0 -73
  79. package/dist/connector/admissionCommit.d.ts +0 -31
  80. package/dist/connector/admissionCommit.js +0 -239
  81. package/dist/connector/sessionAcceptor.d.ts +0 -38
  82. package/dist/connector/sessionAcceptor.js +0 -158
  83. package/dist/connector/sessionConfig.d.ts +0 -3
  84. package/dist/connector/sessionConfig.js +0 -30
  85. package/dist/connector/sessionConnector.d.ts +0 -47
  86. package/dist/connector/sessionConnector.js +0 -606
  87. package/dist/node/connectSession.d.ts +0 -23
  88. package/dist/node/connectSession.js +0 -93
  89. package/dist/node/controlplane.d.ts +0 -137
  90. package/dist/node/controlplane.js +0 -443
  91. package/dist/node/rawQuicAdapter.d.ts +0 -9
  92. package/dist/node/rawQuicServer.d.ts +0 -24
  93. package/dist/node/rawQuicServer.js +0 -37
  94. package/dist/node/runtimeCapability.d.ts +0 -14
  95. package/dist/node/runtimeCapability.js +0 -24
  96. package/dist/node/sessionRuntime.d.ts +0 -2
  97. package/dist/node/sessionRuntime.js +0 -13
  98. package/dist/node/tunnelRuntime.d.ts +0 -42
  99. package/dist/node/tunnelRuntime.js +0 -559
  100. package/dist/node/v2.d.ts +0 -11
  101. package/dist/node/v2.js +0 -6
  102. package/dist/node/webSocketServer.d.ts +0 -24
  103. package/dist/node/webSocketServer.js +0 -135
  104. package/dist/public/artifact.d.ts +0 -11
  105. package/dist/public/artifact.js +0 -44
  106. package/dist/public/artifactLease.d.ts +0 -10
  107. package/dist/public/artifactLease.js +0 -41
  108. package/dist/public/connectError.d.ts +0 -6
  109. package/dist/public/connectError.js +0 -9
  110. package/dist/transport/webSocketAdapter.d.ts +0 -31
  111. package/dist/transport/webSocketAdapter.js +0 -370
  112. package/dist/transport/webTransportAdapter.d.ts +0 -27
  113. package/dist/transport/webTransportAdapter.js +0 -534
  114. package/dist/v2/admissionError.d.ts +0 -4
  115. package/dist/v2/admissionError.js +0 -8
  116. package/dist/v2/artifact.d.ts +0 -128
  117. package/dist/v2/artifact.js +0 -1131
  118. package/dist/v2/artifactLease.d.ts +0 -1
  119. package/dist/v2/artifactLease.js +0 -7
  120. package/dist/v2/capability.d.ts +0 -29
  121. package/dist/v2/capability.js +0 -190
  122. package/dist/v2/carrier.d.ts +0 -84
  123. package/dist/v2/carrier.js +0 -411
  124. package/dist/v2/contract.d.ts +0 -46
  125. package/dist/v2/contract.js +0 -1
  126. package/dist/v2/handshake.d.ts +0 -80
  127. package/dist/v2/handshake.js +0 -470
  128. package/dist/v2/index.d.ts +0 -14
  129. package/dist/v2/index.js +0 -7
  130. package/dist/v2/opaqueArtifact.d.ts +0 -1
  131. package/dist/v2/opaqueArtifact.js +0 -2
  132. package/dist/v2/protocol.d.ts +0 -99
  133. package/dist/v2/protocol.js +0 -737
  134. package/dist/v2/publicSession.d.ts +0 -1
  135. package/dist/v2/publicSession.js +0 -281
  136. package/dist/v2/retryDisposition.d.ts +0 -16
  137. package/dist/v2/retryDisposition.js +0 -30
  138. package/dist/v2/session.d.ts +0 -310
  139. package/dist/v2/session.js +0 -2127
  140. package/dist/v2/streamLifetimeLedger.d.ts +0 -31
  141. package/dist/v2/streamLifetimeLedger.js +0 -131
  142. package/dist/v2/streamMetadata.d.ts +0 -1
  143. package/dist/v2/streamMetadata.js +0 -7
  144. package/dist/v2/unreliableMessage.d.ts +0 -43
  145. package/dist/v2/unreliableMessage.js +0 -254
@@ -1,31 +0,0 @@
1
- export declare const MAX_STREAM_LIFETIME_SLOTS_V2 = 1048576;
2
- export declare function maxLogicalStreamIDV2(openerRole: 1 | 2): bigint;
3
- export declare enum StreamLifetimeStateV2 {
4
- Empty = 0,
5
- AbandonedNoFSS2 = 1,
6
- PrefaceSeen = 2,
7
- ActiveOrResolved = 3
8
- }
9
- export type LateFSS2ActionV2 = "accepted" | "reset";
10
- export declare class StreamLifetimeLedgerV2Error extends Error {
11
- readonly code: "capacity" | "duplicate" | "invalid_state";
12
- constructor(code: "capacity" | "duplicate" | "invalid_state", message: string);
13
- }
14
- export declare class StreamLifetimeLedgerV2 {
15
- private readonly openerRole;
16
- private readonly states;
17
- private resolvedFrontier;
18
- constructor(openerRole: 1 | 2);
19
- get frontier(): bigint;
20
- get backingBytes(): number;
21
- state(id: bigint): StreamLifetimeStateV2;
22
- validFSS2(id: bigint): LateFSS2ActionV2;
23
- validOpen(id: bigint): void;
24
- peerReset(id: bigint): void;
25
- localResetCommitted(id: bigint): void;
26
- private requireSlot;
27
- private slotIndex;
28
- private stateAt;
29
- private setStateAt;
30
- private advanceFrontier;
31
- }
@@ -1,131 +0,0 @@
1
- export const MAX_STREAM_LIFETIME_SLOTS_V2 = 1_048_576;
2
- export function maxLogicalStreamIDV2(openerRole) {
3
- return BigInt(MAX_STREAM_LIFETIME_SLOTS_V2) * 2n - (openerRole === 1 ? 1n : 0n);
4
- }
5
- export var StreamLifetimeStateV2;
6
- (function (StreamLifetimeStateV2) {
7
- StreamLifetimeStateV2[StreamLifetimeStateV2["Empty"] = 0] = "Empty";
8
- StreamLifetimeStateV2[StreamLifetimeStateV2["AbandonedNoFSS2"] = 1] = "AbandonedNoFSS2";
9
- StreamLifetimeStateV2[StreamLifetimeStateV2["PrefaceSeen"] = 2] = "PrefaceSeen";
10
- StreamLifetimeStateV2[StreamLifetimeStateV2["ActiveOrResolved"] = 3] = "ActiveOrResolved";
11
- })(StreamLifetimeStateV2 || (StreamLifetimeStateV2 = {}));
12
- export class StreamLifetimeLedgerV2Error extends Error {
13
- code;
14
- constructor(code, message) {
15
- super(message);
16
- this.code = code;
17
- this.name = "StreamLifetimeLedgerV2Error";
18
- }
19
- }
20
- export class StreamLifetimeLedgerV2 {
21
- openerRole;
22
- states = new Uint8Array(MAX_STREAM_LIFETIME_SLOTS_V2 / 4);
23
- resolvedFrontier = 0n;
24
- constructor(openerRole) {
25
- this.openerRole = openerRole;
26
- }
27
- get frontier() {
28
- return this.resolvedFrontier;
29
- }
30
- get backingBytes() {
31
- return this.states.byteLength;
32
- }
33
- state(id) {
34
- const index = this.slotIndex(id, false);
35
- return index === undefined ? StreamLifetimeStateV2.Empty : this.stateAt(index);
36
- }
37
- validFSS2(id) {
38
- const index = this.requireSlot(id);
39
- switch (this.stateAt(index)) {
40
- case StreamLifetimeStateV2.Empty:
41
- this.setStateAt(index, StreamLifetimeStateV2.PrefaceSeen);
42
- return "accepted";
43
- case StreamLifetimeStateV2.AbandonedNoFSS2:
44
- this.setStateAt(index, StreamLifetimeStateV2.ActiveOrResolved);
45
- this.advanceFrontier();
46
- return "reset";
47
- case StreamLifetimeStateV2.PrefaceSeen:
48
- case StreamLifetimeStateV2.ActiveOrResolved:
49
- throw new StreamLifetimeLedgerV2Error("duplicate", "duplicate logical stream identity");
50
- }
51
- }
52
- validOpen(id) {
53
- const index = this.requireSlot(id);
54
- if (this.stateAt(index) !== StreamLifetimeStateV2.PrefaceSeen) {
55
- throw new StreamLifetimeLedgerV2Error("invalid_state", "OPEN without a pending FSS2 identity");
56
- }
57
- this.setStateAt(index, StreamLifetimeStateV2.ActiveOrResolved);
58
- this.advanceFrontier();
59
- }
60
- peerReset(id) {
61
- const index = this.requireSlot(id);
62
- switch (this.stateAt(index)) {
63
- case StreamLifetimeStateV2.Empty:
64
- this.setStateAt(index, StreamLifetimeStateV2.AbandonedNoFSS2);
65
- break;
66
- case StreamLifetimeStateV2.PrefaceSeen:
67
- this.setStateAt(index, StreamLifetimeStateV2.ActiveOrResolved);
68
- break;
69
- case StreamLifetimeStateV2.AbandonedNoFSS2:
70
- case StreamLifetimeStateV2.ActiveOrResolved:
71
- break;
72
- }
73
- this.advanceFrontier();
74
- }
75
- localResetCommitted(id) {
76
- const index = this.requireSlot(id);
77
- const state = this.stateAt(index);
78
- if (state === StreamLifetimeStateV2.ActiveOrResolved)
79
- return;
80
- if (state !== StreamLifetimeStateV2.PrefaceSeen) {
81
- throw new StreamLifetimeLedgerV2Error("invalid_state", "ordered reset without a pending FSS2 identity");
82
- }
83
- this.setStateAt(index, StreamLifetimeStateV2.ActiveOrResolved);
84
- this.advanceFrontier();
85
- }
86
- requireSlot(id) {
87
- const index = this.slotIndex(id, true);
88
- if (index === undefined) {
89
- throw new StreamLifetimeLedgerV2Error("capacity", "logical stream lifetime ledger capacity exceeded");
90
- }
91
- return index;
92
- }
93
- slotIndex(id, validate) {
94
- const validParity = id > 0n && (this.openerRole === 1 ? (id & 1n) === 1n : (id & 1n) === 0n);
95
- if (!validParity) {
96
- if (validate)
97
- throw new StreamLifetimeLedgerV2Error("capacity", "logical stream identity is outside the opener role");
98
- return undefined;
99
- }
100
- const ordinal = this.openerRole === 1 ? (id + 1n) / 2n : id / 2n;
101
- if (ordinal < 1n || ordinal > BigInt(MAX_STREAM_LIFETIME_SLOTS_V2))
102
- return undefined;
103
- return Number(ordinal - 1n);
104
- }
105
- stateAt(index) {
106
- const shift = (index % 4) * 2;
107
- return ((this.states[Math.floor(index / 4)] >>> shift) & 0x03);
108
- }
109
- setStateAt(index, state) {
110
- const byteIndex = Math.floor(index / 4);
111
- const shift = (index % 4) * 2;
112
- const mask = 0x03 << shift;
113
- this.states[byteIndex] = (this.states[byteIndex] & ~mask) | (state << shift);
114
- }
115
- advanceFrontier() {
116
- let next = this.resolvedFrontier === 0n
117
- ? (this.openerRole === 1 ? 1n : 2n)
118
- : this.resolvedFrontier + 2n;
119
- while (true) {
120
- const index = this.slotIndex(next, false);
121
- if (index === undefined)
122
- return;
123
- const state = this.stateAt(index);
124
- if (state !== StreamLifetimeStateV2.AbandonedNoFSS2 && state !== StreamLifetimeStateV2.ActiveOrResolved) {
125
- return;
126
- }
127
- this.resolvedFrontier = next;
128
- next += 2n;
129
- }
130
- }
131
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,7 +0,0 @@
1
- import { createStreamMetadata, streamMetadataValues, } from "../public/streamMetadata.js";
2
- /** @internal */
3
- export { createStreamMetadata, StreamMetadataError } from "../public/streamMetadata.js";
4
- /** @internal */
5
- export const createStreamMetadataV2 = createStreamMetadata;
6
- /** @internal */
7
- export const streamMetadataValuesV2 = streamMetadataValues;
@@ -1,43 +0,0 @@
1
- import type { CarrierUnreliableDatagramsV2 } from "./carrier.js";
2
- import { CipherSuiteV2 } from "./protocol.js";
3
- import type { DirectionV2 } from "./protocol.js";
4
- export declare const UNRELIABLE_MESSAGES_FEATURE_V2 = 1;
5
- export declare const UNRELIABLE_MESSAGE_MAX_PLAINTEXT_BYTES_V2: 976;
6
- export declare const UNRELIABLE_MESSAGE_WIRE_BYTES_V2: 1024;
7
- export type InternalUnreliableMessageChannelV2Options = Readonly<{
8
- transport: CarrierUnreliableDatagramsV2;
9
- suite: CipherSuiteV2;
10
- h3: Uint8Array;
11
- sendDirection: DirectionV2;
12
- receiveDirection: DirectionV2;
13
- currentSendEpoch(): Readonly<{
14
- epoch: number;
15
- epochSecret: Uint8Array;
16
- }>;
17
- receiveEpochSecret(epoch: number): Uint8Array | undefined;
18
- now?: () => number;
19
- }>;
20
- export type UnreliableMessageMaterialV2 = Readonly<{
21
- unreliableRoot: Uint8Array;
22
- materialSecret: Uint8Array;
23
- recordKey: Uint8Array;
24
- noncePrefix: Uint8Array;
25
- }>;
26
- export type SealUnreliableMessageDatagramV2Options = Readonly<{
27
- suite: CipherSuiteV2;
28
- epochSecret: Uint8Array;
29
- h3: Uint8Array;
30
- direction: DirectionV2;
31
- epoch: number;
32
- sequence: bigint;
33
- expiresAtUnixMs: bigint;
34
- plaintext: Uint8Array;
35
- }>;
36
- export type SealedUnreliableMessageDatagramV2 = Readonly<{
37
- material: UnreliableMessageMaterialV2;
38
- nonce: Uint8Array;
39
- header: Uint8Array;
40
- aad: Uint8Array;
41
- ciphertext: Uint8Array;
42
- wire: Uint8Array;
43
- }>;
@@ -1,254 +0,0 @@
1
- import { gcm } from "@noble/ciphers/aes.js";
2
- import { chacha20poly1305 } from "@noble/ciphers/chacha.js";
3
- import { expand } from "@noble/hashes/hkdf.js";
4
- import { sha256 } from "@noble/hashes/sha2.js";
5
- import { CipherSuiteV2 } from "./protocol.js";
6
- import { UnreliableMessageError } from "../public/contract.js";
7
- export const UNRELIABLE_MESSAGES_FEATURE_V2 = 0x00000001;
8
- export const UNRELIABLE_MESSAGE_MAX_PLAINTEXT_BYTES_V2 = 976;
9
- export const UNRELIABLE_MESSAGE_WIRE_BYTES_V2 = 1_024;
10
- const HEADER_BYTES = 32;
11
- const TAG_BYTES = 16;
12
- const MAX_PENDING_SENDS = 64;
13
- const MAX_UINT64 = (1n << 64n) - 1n;
14
- const encoder = new TextEncoder();
15
- /** @internal */ export { UnreliableMessageError } from "../public/contract.js";
16
- /** @internal */
17
- export function createInternalUnreliableMessageChannelV2(options) {
18
- return new InternalUnreliableMessageChannelV2(options);
19
- }
20
- class InternalUnreliableMessageChannelV2 {
21
- options;
22
- maxMessageSize = UNRELIABLE_MESSAGE_MAX_PLAINTEXT_BYTES_V2;
23
- replay = new Map();
24
- now;
25
- nextSequence = 0n;
26
- sendEpoch = -1;
27
- pendingSends = 0;
28
- constructor(options) {
29
- this.options = options;
30
- if (options.transport.maxDatagramSize < UNRELIABLE_MESSAGE_WIRE_BYTES_V2 || options.h3.byteLength !== 32) {
31
- throw new UnreliableMessageError("operation_failed");
32
- }
33
- this.now = options.now ?? Date.now;
34
- }
35
- async send(message, options) {
36
- throwIfAborted(options.signal);
37
- if (!(message instanceof Uint8Array) || message.byteLength < 1) {
38
- throw new UnreliableMessageError("invalid_message");
39
- }
40
- if (message.byteLength > UNRELIABLE_MESSAGE_MAX_PLAINTEXT_BYTES_V2) {
41
- throw new UnreliableMessageError("too_large");
42
- }
43
- const payload = message.slice();
44
- const expiresAt = requireFutureExpiry(options.expiresAtUnixMs, this.now());
45
- if (expiresAt === undefined)
46
- return "dropped_expired";
47
- if (this.pendingSends >= MAX_PENDING_SENDS)
48
- return "dropped_budget";
49
- const roots = this.options.currentSendEpoch();
50
- if (roots.epoch !== this.sendEpoch) {
51
- this.sendEpoch = roots.epoch;
52
- this.nextSequence = 0n;
53
- }
54
- if (this.nextSequence > MAX_UINT64)
55
- throw new UnreliableMessageError("closed");
56
- const sequence = this.nextSequence++;
57
- const sealed = sealUnreliableMessageDatagramV2({
58
- suite: this.options.suite,
59
- epochSecret: roots.epochSecret,
60
- h3: this.options.h3,
61
- direction: this.options.sendDirection,
62
- epoch: roots.epoch,
63
- sequence,
64
- expiresAtUnixMs: BigInt(expiresAt),
65
- plaintext: payload,
66
- });
67
- this.pendingSends++;
68
- try {
69
- return await this.options.transport.send(sealed.wire, {
70
- ...(options.signal === undefined ? {} : { signal: options.signal }),
71
- expiresAt,
72
- });
73
- }
74
- catch (error) {
75
- if (isAbortError(error))
76
- throw new UnreliableMessageError("canceled");
77
- throw new UnreliableMessageError("operation_failed");
78
- }
79
- finally {
80
- this.pendingSends--;
81
- }
82
- }
83
- async receive(options = {}) {
84
- for (;;) {
85
- throwIfAborted(options.signal);
86
- let wire;
87
- try {
88
- wire = await this.options.transport.receive(options);
89
- }
90
- catch (error) {
91
- if (isAbortError(error))
92
- throw new UnreliableMessageError("canceled");
93
- throw new UnreliableMessageError("closed");
94
- }
95
- const decoded = decodeHeader(wire);
96
- if (decoded === undefined || decoded.expiresAtUnixMs <= BigInt(Math.floor(this.now())))
97
- continue;
98
- const epochSecret = this.options.receiveEpochSecret(decoded.epoch);
99
- if (epochSecret === undefined)
100
- continue;
101
- const window = this.replay.get(decoded.epoch) ?? new ReplayWindow();
102
- if (window.seen(decoded.sequence))
103
- continue;
104
- const material = deriveUnreliableMessageMaterialV2(epochSecret, this.options.h3, this.options.receiveDirection, decoded.epoch);
105
- const nonce = concat(material.noncePrefix, u64be(decoded.sequence));
106
- const aad = labelWith("flowersec-v2-unreliable", this.options.h3, byte(this.options.receiveDirection), wire.subarray(0, HEADER_BYTES));
107
- let plaintext;
108
- try {
109
- plaintext = cipher(this.options.suite, material.recordKey, nonce, aad).decrypt(wire.subarray(HEADER_BYTES));
110
- }
111
- catch {
112
- continue;
113
- }
114
- if (plaintext.byteLength < 1 || plaintext.byteLength > UNRELIABLE_MESSAGE_MAX_PLAINTEXT_BYTES_V2)
115
- continue;
116
- window.mark(decoded.sequence);
117
- this.replay.set(decoded.epoch, window);
118
- pruneReplayEpochs(this.replay, decoded.epoch);
119
- return plaintext.slice();
120
- }
121
- }
122
- }
123
- /** @internal */
124
- export function encodeUnreliableMessageHeaderV2(epoch, sequence, expiresAtUnixMs, ciphertextLength) {
125
- const header = new Uint8Array(HEADER_BYTES);
126
- header.set(encoder.encode("FSD2"));
127
- header[4] = 2;
128
- header[5] = 0;
129
- const view = new DataView(header.buffer);
130
- view.setUint16(6, HEADER_BYTES, false);
131
- view.setUint32(8, epoch, false);
132
- view.setBigUint64(12, sequence, false);
133
- view.setBigUint64(20, expiresAtUnixMs, false);
134
- view.setUint32(28, ciphertextLength, false);
135
- return header;
136
- }
137
- function decodeHeader(wire) {
138
- if (!(wire instanceof Uint8Array) || wire.byteLength < HEADER_BYTES + TAG_BYTES ||
139
- wire.byteLength > UNRELIABLE_MESSAGE_WIRE_BYTES_V2 ||
140
- wire[0] !== 0x46 || wire[1] !== 0x53 || wire[2] !== 0x44 || wire[3] !== 0x32 ||
141
- wire[4] !== 2 || wire[5] !== 0)
142
- return undefined;
143
- const view = new DataView(wire.buffer, wire.byteOffset, HEADER_BYTES);
144
- if (view.getUint16(6, false) !== HEADER_BYTES || view.getUint32(28, false) !== wire.byteLength - HEADER_BYTES) {
145
- return undefined;
146
- }
147
- return {
148
- epoch: view.getUint32(8, false),
149
- sequence: view.getBigUint64(12, false),
150
- expiresAtUnixMs: view.getBigUint64(20, false),
151
- };
152
- }
153
- /** @internal */
154
- export function deriveUnreliableMessageMaterialV2(epochSecret, h3, direction, epoch) {
155
- const unreliableRoot = expand(sha256, epochSecret, labelWith("flowersec v2 unreliable root"), 32);
156
- const materialSecret = expand(sha256, unreliableRoot, labelWith("flowersec v2 unreliable", h3, byte(direction), u32be(epoch)), 32);
157
- return {
158
- unreliableRoot,
159
- materialSecret,
160
- recordKey: expand(sha256, materialSecret, labelWith("flowersec v2 unreliable key"), 32),
161
- noncePrefix: expand(sha256, materialSecret, labelWith("flowersec v2 unreliable nonce"), 4),
162
- };
163
- }
164
- /** @internal */
165
- export function sealUnreliableMessageDatagramV2(options) {
166
- const header = encodeUnreliableMessageHeaderV2(options.epoch, options.sequence, options.expiresAtUnixMs, options.plaintext.byteLength + TAG_BYTES);
167
- const material = deriveUnreliableMessageMaterialV2(options.epochSecret, options.h3, options.direction, options.epoch);
168
- const nonce = concat(material.noncePrefix, u64be(options.sequence));
169
- const aad = labelWith("flowersec-v2-unreliable", options.h3, byte(options.direction), header);
170
- const ciphertext = cipher(options.suite, material.recordKey, nonce, aad).encrypt(options.plaintext);
171
- return {
172
- material,
173
- nonce,
174
- header,
175
- aad,
176
- ciphertext,
177
- wire: concat(header, ciphertext),
178
- };
179
- }
180
- function cipher(suite, key, nonce, aad) {
181
- if (suite === CipherSuiteV2.ChaCha20Poly1305)
182
- return chacha20poly1305(key, nonce, aad);
183
- if (suite === CipherSuiteV2.AES256GCM)
184
- return gcm(key, nonce, aad);
185
- throw new UnreliableMessageError("operation_failed");
186
- }
187
- class ReplayWindow {
188
- highest = -1n;
189
- bitmap = 0n;
190
- seen(sequence) {
191
- if (this.highest < 0n || sequence > this.highest)
192
- return false;
193
- const distance = this.highest - sequence;
194
- return distance >= 64n || (this.bitmap & (1n << distance)) !== 0n;
195
- }
196
- mark(sequence) {
197
- if (this.highest < 0n) {
198
- this.highest = sequence;
199
- this.bitmap = 1n;
200
- return;
201
- }
202
- if (sequence > this.highest) {
203
- const shift = sequence - this.highest;
204
- this.bitmap = shift >= 64n ? 1n : ((this.bitmap << shift) | 1n) & ((1n << 64n) - 1n);
205
- this.highest = sequence;
206
- return;
207
- }
208
- this.bitmap |= 1n << (this.highest - sequence);
209
- }
210
- }
211
- function pruneReplayEpochs(windows, current) {
212
- for (const epoch of windows.keys()) {
213
- if (epoch + 1 < current || epoch > current + 1)
214
- windows.delete(epoch);
215
- }
216
- }
217
- function requireFutureExpiry(value, now) {
218
- if (!Number.isSafeInteger(value) || value < 0)
219
- throw new UnreliableMessageError("invalid_message");
220
- return value <= now ? undefined : value;
221
- }
222
- function throwIfAborted(signal) {
223
- if (signal?.aborted === true)
224
- throw new UnreliableMessageError("canceled");
225
- }
226
- function isAbortError(error) {
227
- return error instanceof DOMException && error.name === "AbortError" ||
228
- error instanceof Error && error.name === "AbortError";
229
- }
230
- function labelWith(label, ...parts) {
231
- return concat(encoder.encode(label), byte(0), ...parts);
232
- }
233
- function concat(...parts) {
234
- const out = new Uint8Array(parts.reduce((size, part) => size + part.byteLength, 0));
235
- let offset = 0;
236
- for (const part of parts) {
237
- out.set(part, offset);
238
- offset += part.byteLength;
239
- }
240
- return out;
241
- }
242
- function byte(value) {
243
- return Uint8Array.of(value);
244
- }
245
- function u32be(value) {
246
- const out = new Uint8Array(4);
247
- new DataView(out.buffer).setUint32(0, value, false);
248
- return out;
249
- }
250
- function u64be(value) {
251
- const out = new Uint8Array(8);
252
- new DataView(out.buffer).setBigUint64(0, value, false);
253
- return out;
254
- }