@floegence/flowersec-core 2.5.3 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -18
- package/dist/browser/connectSessionV3.d.ts +13 -0
- package/dist/browser/connectSessionV3.js +75 -0
- package/dist/browser/index.d.ts +3 -2
- package/dist/browser/index.js +2 -1
- package/dist/browser/v2.d.ts +3 -0
- package/dist/browser/v2.js +2 -0
- package/dist/facade.d.ts +15 -8
- package/dist/facade.js +5 -4
- package/dist/framing/jsonframe.d.ts +1 -1
- package/dist/framing/jsonframe.js +3 -1
- package/dist/interop/proxyServerPeer.js +1 -1
- package/dist/interop/serverParityPeer.js +8 -1
- package/dist/node/acceptor.d.ts +10 -0
- package/dist/node/acceptor.js +53 -7
- package/dist/node/acceptorV3.d.ts +57 -0
- package/dist/node/acceptorV3.js +227 -0
- package/dist/node/connectSessionV3.d.ts +21 -0
- package/dist/node/connectSessionV3.js +73 -0
- package/dist/node/index.d.ts +15 -10
- package/dist/node/index.js +6 -5
- package/dist/node/nativeTransportAddon.d.ts +39 -1
- package/dist/node/nativeTransportAddon.js +29 -5
- package/dist/node/rawQuicAdapterV3.d.ts +10 -0
- package/dist/node/rawQuicAdapterV3.js +76 -0
- package/dist/node/rawQuicServerV3.d.ts +24 -0
- package/dist/node/rawQuicServerV3.js +34 -0
- package/dist/node/runtimeAuthorizationV3.d.ts +29 -0
- package/dist/node/runtimeAuthorizationV3.js +126 -0
- package/dist/node/tunnelRuntimeV3.d.ts +57 -0
- package/dist/node/tunnelRuntimeV3.js +857 -0
- package/dist/node/v2.d.ts +11 -0
- package/dist/node/v2.js +6 -0
- package/dist/node/webSocketServerV3.d.ts +27 -0
- package/dist/node/webSocketServerV3.js +219 -0
- package/dist/proxy/runtime.js +39 -6
- package/dist/proxy/serviceWorker.d.ts +2 -0
- package/dist/proxy/serviceWorker.js +63 -7
- package/dist/proxy/windowBridge.d.ts +1 -0
- package/dist/proxy/windowBridge.js +93 -33
- package/dist/proxy/wsPatch.d.ts +1 -0
- package/dist/proxy/wsPatch.js +39 -6
- package/dist/public/streamHandlers.d.ts +3 -0
- package/dist/public/streamHandlers.js +6 -1
- package/dist/rpc/client.js +9 -7
- package/dist/rpc/server.js +44 -20
- package/dist/rpc/validate.d.ts +1 -0
- package/dist/rpc/validate.js +18 -6
- package/dist/v2/index.d.ts +14 -0
- package/dist/v2/index.js +7 -0
- package/dist/v2/protocol.js +23 -32
- package/dist/v2/publicSession.js +4 -0
- package/dist/v2/session.d.ts +5 -0
- package/dist/v2/session.js +94 -35
- package/dist/v3/admissionError.d.ts +4 -0
- package/dist/v3/admissionError.js +8 -0
- package/dist/v3/artifact.d.ts +145 -0
- package/dist/v3/artifact.js +1162 -0
- package/dist/v3/artifactLease.d.ts +17 -0
- package/dist/v3/artifactLease.js +109 -0
- package/dist/v3/browserRuntime.d.ts +37 -0
- package/dist/v3/browserRuntime.js +197 -0
- package/dist/v3/browserSessionRuntime.d.ts +2 -0
- package/dist/v3/browserSessionRuntime.js +21 -0
- package/dist/v3/capability.d.ts +32 -0
- package/dist/v3/capability.js +345 -0
- package/dist/v3/carrier.d.ts +84 -0
- package/dist/v3/carrier.js +411 -0
- package/dist/v3/connectionController.d.ts +87 -0
- package/dist/v3/connectionController.js +805 -0
- package/dist/v3/contract.d.ts +65 -0
- package/dist/v3/contract.js +3 -0
- package/dist/v3/controller.d.ts +43 -0
- package/dist/v3/controller.js +303 -0
- package/dist/v3/handshake.d.ts +81 -0
- package/dist/v3/handshake.js +471 -0
- package/dist/v3/jcs.d.ts +6 -0
- package/dist/v3/jcs.js +69 -0
- package/dist/v3/jsonPreflight.d.ts +1 -0
- package/dist/v3/jsonPreflight.js +251 -0
- package/dist/v3/nodeRuntime.d.ts +40 -0
- package/dist/v3/nodeRuntime.js +315 -0
- package/dist/v3/nodeSessionRuntime.d.ts +2 -0
- package/dist/v3/nodeSessionRuntime.js +13 -0
- package/dist/v3/protocol.d.ts +99 -0
- package/dist/v3/protocol.js +773 -0
- package/dist/v3/publicApi.d.ts +12 -0
- package/dist/v3/publicApi.js +46 -0
- package/dist/v3/publicSession.d.ts +1 -0
- package/dist/v3/publicSession.js +281 -0
- package/dist/v3/retryDisposition.d.ts +16 -0
- package/dist/v3/retryDisposition.js +30 -0
- package/dist/v3/runtimeAdapters.d.ts +9 -0
- package/dist/v3/runtimeAdapters.js +109 -0
- package/dist/v3/security.d.ts +34 -0
- package/dist/v3/security.js +100 -0
- package/dist/v3/serverAdmission.d.ts +38 -0
- package/dist/v3/serverAdmission.js +223 -0
- package/dist/v3/session.d.ts +318 -0
- package/dist/v3/session.js +2289 -0
- package/dist/v3/sessionConnector.d.ts +37 -0
- package/dist/v3/sessionConnector.js +440 -0
- package/dist/v3/streamLifetimeLedger.d.ts +31 -0
- package/dist/v3/streamLifetimeLedger.js +131 -0
- package/dist/v3/streamMetadata.d.ts +1 -0
- package/dist/v3/streamMetadata.js +7 -0
- package/dist/v3/transportConstants.d.ts +56 -0
- package/dist/v3/transportConstants.js +59 -0
- package/dist/v3/unreliableMessage.d.ts +44 -0
- package/dist/v3/unreliableMessage.js +284 -0
- package/dist/v3/webSocketCarrier.d.ts +31 -0
- package/dist/v3/webSocketCarrier.js +370 -0
- package/dist/v3/webTransportCarrier.d.ts +27 -0
- package/dist/v3/webTransportCarrier.js +534 -0
- package/dist/yamux/stream.d.ts +3 -0
- package/dist/yamux/stream.js +17 -4
- package/package.json +4 -4
- package/sbom/cyclonedx.json +6 -6
- package/sbom/spdx.json +11 -11
|
@@ -0,0 +1,773 @@
|
|
|
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 { hmac } from "@noble/hashes/hmac.js";
|
|
5
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
6
|
+
import { FLOWERSEC_V3_CRYPTO_LABELS } from "./transportConstants.js";
|
|
7
|
+
const encoder = new TextEncoder();
|
|
8
|
+
const MAX_UINT32 = 0xffffffff;
|
|
9
|
+
const MAX_UINT64 = (1n << 64n) - 1n;
|
|
10
|
+
const SETUP_PREFACE_BYTES = 56;
|
|
11
|
+
const RECORD_HEADER_BYTES = 24;
|
|
12
|
+
const INNER_HEADER_BYTES = 8;
|
|
13
|
+
const MAX_DATA_BYTES = 16_384;
|
|
14
|
+
const MAX_CIPHERTEXT_BYTES = INNER_HEADER_BYTES + MAX_DATA_BYTES + 16;
|
|
15
|
+
const OPEN_FIXED_PAYLOAD_BYTES = 46;
|
|
16
|
+
const MAX_OPEN_BYTES = 8_192;
|
|
17
|
+
const MAX_OPEN_KIND_BYTES = 128;
|
|
18
|
+
const MAX_OPEN_METADATA_BYTES = 4_096;
|
|
19
|
+
const MAX_OPEN_METADATA_DEPTH = 4;
|
|
20
|
+
const MAX_OPEN_METADATA_NODES = 64;
|
|
21
|
+
const MAX_OPEN_METADATA_KEYS = 64;
|
|
22
|
+
const MAX_OPEN_METADATA_ARRAY = 32;
|
|
23
|
+
const MAX_OPEN_METADATA_KEY_BYTES = 64;
|
|
24
|
+
const MAX_OPEN_METADATA_STRING_BYTES = 512;
|
|
25
|
+
const UNICODE_15_1_ASSIGNED_RANGES_HEX = [
|
|
26
|
+
"00000000037700037A00037F00038400038A00038C00038C00038E0003A10003A300052F00053100055600055900058A00058D00058F0005910005C7",
|
|
27
|
+
"0005D00005EA0005EF0005F400060000070D00070F00074A00074D0007B10007C00007FA0007FD00082D00083000083E00084000085B00085E00085E",
|
|
28
|
+
"00086000086A00087000088E00089000089100089800098300098500098C00098F0009900009930009A80009AA0009B00009B20009B20009B60009B9",
|
|
29
|
+
"0009BC0009C40009C70009C80009CB0009CE0009D70009D70009DC0009DD0009DF0009E30009E60009FE000A01000A03000A05000A0A000A0F000A10",
|
|
30
|
+
"000A13000A28000A2A000A30000A32000A33000A35000A36000A38000A39000A3C000A3C000A3E000A42000A47000A48000A4B000A4D000A51000A51",
|
|
31
|
+
"000A59000A5C000A5E000A5E000A66000A76000A81000A83000A85000A8D000A8F000A91000A93000AA8000AAA000AB0000AB2000AB3000AB5000AB9",
|
|
32
|
+
"000ABC000AC5000AC7000AC9000ACB000ACD000AD0000AD0000AE0000AE3000AE6000AF1000AF9000AFF000B01000B03000B05000B0C000B0F000B10",
|
|
33
|
+
"000B13000B28000B2A000B30000B32000B33000B35000B39000B3C000B44000B47000B48000B4B000B4D000B55000B57000B5C000B5D000B5F000B63",
|
|
34
|
+
"000B66000B77000B82000B83000B85000B8A000B8E000B90000B92000B95000B99000B9A000B9C000B9C000B9E000B9F000BA3000BA4000BA8000BAA",
|
|
35
|
+
"000BAE000BB9000BBE000BC2000BC6000BC8000BCA000BCD000BD0000BD0000BD7000BD7000BE6000BFA000C00000C0C000C0E000C10000C12000C28",
|
|
36
|
+
"000C2A000C39000C3C000C44000C46000C48000C4A000C4D000C55000C56000C58000C5A000C5D000C5D000C60000C63000C66000C6F000C77000C8C",
|
|
37
|
+
"000C8E000C90000C92000CA8000CAA000CB3000CB5000CB9000CBC000CC4000CC6000CC8000CCA000CCD000CD5000CD6000CDD000CDE000CE0000CE3",
|
|
38
|
+
"000CE6000CEF000CF1000CF3000D00000D0C000D0E000D10000D12000D44000D46000D48000D4A000D4F000D54000D63000D66000D7F000D81000D83",
|
|
39
|
+
"000D85000D96000D9A000DB1000DB3000DBB000DBD000DBD000DC0000DC6000DCA000DCA000DCF000DD4000DD6000DD6000DD8000DDF000DE6000DEF",
|
|
40
|
+
"000DF2000DF4000E01000E3A000E3F000E5B000E81000E82000E84000E84000E86000E8A000E8C000EA3000EA5000EA5000EA7000EBD000EC0000EC4",
|
|
41
|
+
"000EC6000EC6000EC8000ECE000ED0000ED9000EDC000EDF000F00000F47000F49000F6C000F71000F97000F99000FBC000FBE000FCC000FCE000FDA",
|
|
42
|
+
"0010000010C50010C70010C70010CD0010CD0010D000124800124A00124D00125000125600125800125800125A00125D00126000128800128A00128D",
|
|
43
|
+
"0012900012B00012B20012B50012B80012BE0012C00012C00012C20012C50012C80012D60012D800131000131200131500131800135A00135D00137C",
|
|
44
|
+
"0013800013990013A00013F50013F80013FD00140000169C0016A00016F800170000171500171F00173600174000175300176000176C00176E001770",
|
|
45
|
+
"0017720017730017800017DD0017E00017E90017F00017F90018000018190018200018780018800018AA0018B00018F500190000191E00192000192B",
|
|
46
|
+
"00193000193B00194000194000194400196D0019700019740019800019AB0019B00019C90019D00019DA0019DE001A1B001A1E001A5E001A60001A7C",
|
|
47
|
+
"001A7F001A89001A90001A99001AA0001AAD001AB0001ACE001B00001B4C001B50001B7E001B80001BF3001BFC001C37001C3B001C49001C4D001C88",
|
|
48
|
+
"001C90001CBA001CBD001CC7001CD0001CFA001D00001F15001F18001F1D001F20001F45001F48001F4D001F50001F57001F59001F59001F5B001F5B",
|
|
49
|
+
"001F5D001F5D001F5F001F7D001F80001FB4001FB6001FC4001FC6001FD3001FD6001FDB001FDD001FEF001FF2001FF4001FF6001FFE002000002064",
|
|
50
|
+
"00206600207100207400208E00209000209C0020A00020C00020D00020F000210000218B00219000242600244000244A002460002B73002B76002B95",
|
|
51
|
+
"002B97002CF3002CF9002D25002D27002D27002D2D002D2D002D30002D67002D6F002D70002D7F002D96002DA0002DA6002DA8002DAE002DB0002DB6",
|
|
52
|
+
"002DB8002DBE002DC0002DC6002DC8002DCE002DD0002DD6002DD8002DDE002DE0002E5D002E80002E99002E9B002EF3002F00002FD5002FF000303F",
|
|
53
|
+
"0030410030960030990030FF00310500312F00313100318E0031900031E30031EF00321E00322000A48C00A49000A4C600A4D000A62B00A64000A6F7",
|
|
54
|
+
"00A70000A7CA00A7D000A7D100A7D300A7D300A7D500A7D900A7F200A82C00A83000A83900A84000A87700A88000A8C500A8CE00A8D900A8E000A953",
|
|
55
|
+
"00A95F00A97C00A98000A9CD00A9CF00A9D900A9DE00A9FE00AA0000AA3600AA4000AA4D00AA5000AA5900AA5C00AAC200AADB00AAF600AB0100AB06",
|
|
56
|
+
"00AB0900AB0E00AB1100AB1600AB2000AB2600AB2800AB2E00AB3000AB6B00AB7000ABED00ABF000ABF900AC0000D7A300D7B000D7C600D7CB00D7FB",
|
|
57
|
+
"00D80000FA6D00FA7000FAD900FB0000FB0600FB1300FB1700FB1D00FB3600FB3800FB3C00FB3E00FB3E00FB4000FB4100FB4300FB4400FB4600FBC2",
|
|
58
|
+
"00FBD300FD8F00FD9200FDC700FDCF00FE1900FE2000FE5200FE5400FE6600FE6800FE6B00FE7000FE7400FE7600FEFC00FEFF00FEFF00FF0100FFBE",
|
|
59
|
+
"00FFC200FFC700FFCA00FFCF00FFD200FFD700FFDA00FFDC00FFE000FFE600FFE800FFEE00FFF901000B01000D01002601002801003A01003C01003D",
|
|
60
|
+
"01003F01004D01005001005D0100800100FA01010001010201010701013301013701018E01019001019C0101A00101A00101D00101FD01028001029C",
|
|
61
|
+
"0102A00102D00102E00102FB01030001032301032D01034A01035001037A01038001039D01039F0103C30103C80103D501040001049D0104A00104A9",
|
|
62
|
+
"0104B00104D30104D80104FB01050001052701053001056301056F01057A01057C01058A01058C0105920105940105950105970105A10105A30105B1",
|
|
63
|
+
"0105B30105B90105BB0105BC0106000107360107400107550107600107670107800107850107870107B00107B20107BA010800010805010808010808",
|
|
64
|
+
"01080A01083501083701083801083C01083C01083F01085501085701089E0108A70108AF0108E00108F20108F40108F50108FB01091B01091F010939",
|
|
65
|
+
"01093F01093F0109800109B70109BC0109CF0109D2010A03010A05010A06010A0C010A13010A15010A17010A19010A35010A38010A3A010A3F010A48",
|
|
66
|
+
"010A50010A58010A60010A9F010AC0010AE6010AEB010AF6010B00010B35010B39010B55010B58010B72010B78010B91010B99010B9C010BA9010BAF",
|
|
67
|
+
"010C00010C48010C80010CB2010CC0010CF2010CFA010D27010D30010D39010E60010E7E010E80010EA9010EAB010EAD010EB0010EB1010EFD010F27",
|
|
68
|
+
"010F30010F59010F70010F89010FB0010FCB010FE0010FF601100001104D01105201107501107F0110C20110CD0110CD0110D00110E80110F00110F9",
|
|
69
|
+
"0111000111340111360111470111500111760111800111DF0111E10111F401120001121101121301124101128001128601128801128801128A01128D",
|
|
70
|
+
"01128F01129D01129F0112A90112B00112EA0112F00112F901130001130301130501130C01130F01131001131301132801132A011330011332011333",
|
|
71
|
+
"01133501133901133B01134401134701134801134B01134D01135001135001135701135701135D01136301136601136C01137001137401140001145B",
|
|
72
|
+
"01145D0114610114800114C70114D00114D90115800115B50115B80115DD01160001164401165001165901166001166C0116800116B90116C00116C9",
|
|
73
|
+
"01170001171A01171D01172B01173001174601180001183B0118A00118F20118FF01190601190901190901190C011913011915011916011918011935",
|
|
74
|
+
"01193701193801193B0119460119500119590119A00119A70119AA0119D70119DA0119E4011A00011A47011A50011AA2011AB0011AF8011B00011B09",
|
|
75
|
+
"011C00011C08011C0A011C36011C38011C45011C50011C6C011C70011C8F011C92011CA7011CA9011CB6011D00011D06011D08011D09011D0B011D36",
|
|
76
|
+
"011D3A011D3A011D3C011D3D011D3F011D47011D50011D59011D60011D65011D67011D68011D6A011D8E011D90011D91011D93011D98011DA0011DA9",
|
|
77
|
+
"011EE0011EF8011F00011F10011F12011F3A011F3E011F59011FB0011FB0011FC0011FF1011FFF01239901240001246E012470012474012480012543",
|
|
78
|
+
"012F90012FF2013000013455014400014646016800016A38016A40016A5E016A60016A69016A6E016ABE016AC0016AC9016AD0016AED016AF0016AF5",
|
|
79
|
+
"016B00016B45016B50016B59016B5B016B61016B63016B77016B7D016B8F016E40016E9A016F00016F4A016F4F016F87016F8F016F9F016FE0016FE4",
|
|
80
|
+
"016FF0016FF10170000187F7018800018CD5018D00018D0801AFF001AFF301AFF501AFFB01AFFD01AFFE01B00001B12201B13201B13201B15001B152",
|
|
81
|
+
"01B15501B15501B16401B16701B17001B2FB01BC0001BC6A01BC7001BC7C01BC8001BC8801BC9001BC9901BC9C01BCA301CF0001CF2D01CF3001CF46",
|
|
82
|
+
"01CF5001CFC301D00001D0F501D10001D12601D12901D1EA01D20001D24501D2C001D2D301D2E001D2F301D30001D35601D36001D37801D40001D454",
|
|
83
|
+
"01D45601D49C01D49E01D49F01D4A201D4A201D4A501D4A601D4A901D4AC01D4AE01D4B901D4BB01D4BB01D4BD01D4C301D4C501D50501D50701D50A",
|
|
84
|
+
"01D50D01D51401D51601D51C01D51E01D53901D53B01D53E01D54001D54401D54601D54601D54A01D55001D55201D6A501D6A801D7CB01D7CE01DA8B",
|
|
85
|
+
"01DA9B01DA9F01DAA101DAAF01DF0001DF1E01DF2501DF2A01E00001E00601E00801E01801E01B01E02101E02301E02401E02601E02A01E03001E06D",
|
|
86
|
+
"01E08F01E08F01E10001E12C01E13001E13D01E14001E14901E14E01E14F01E29001E2AE01E2C001E2F901E2FF01E2FF01E4D001E4F901E7E001E7E6",
|
|
87
|
+
"01E7E801E7EB01E7ED01E7EE01E7F001E7FE01E80001E8C401E8C701E8D601E90001E94B01E95001E95901E95E01E95F01EC7101ECB401ED0101ED3D",
|
|
88
|
+
"01EE0001EE0301EE0501EE1F01EE2101EE2201EE2401EE2401EE2701EE2701EE2901EE3201EE3401EE3701EE3901EE3901EE3B01EE3B01EE4201EE42",
|
|
89
|
+
"01EE4701EE4701EE4901EE4901EE4B01EE4B01EE4D01EE4F01EE5101EE5201EE5401EE5401EE5701EE5701EE5901EE5901EE5B01EE5B01EE5D01EE5D",
|
|
90
|
+
"01EE5F01EE5F01EE6101EE6201EE6401EE6401EE6701EE6A01EE6C01EE7201EE7401EE7701EE7901EE7C01EE7E01EE7E01EE8001EE8901EE8B01EE9B",
|
|
91
|
+
"01EEA101EEA301EEA501EEA901EEAB01EEBB01EEF001EEF101F00001F02B01F03001F09301F0A001F0AE01F0B101F0BF01F0C101F0CF01F0D101F0F5",
|
|
92
|
+
"01F10001F1AD01F1E601F20201F21001F23B01F24001F24801F25001F25101F26001F26501F30001F6D701F6DC01F6EC01F6F001F6FC01F70001F776",
|
|
93
|
+
"01F77B01F7D901F7E001F7EB01F7F001F7F001F80001F80B01F81001F84701F85001F85901F86001F88701F89001F8AD01F8B001F8B101F90001FA53",
|
|
94
|
+
"01FA6001FA6D01FA7001FA7C01FA8001FA8801FA9001FABD01FABF01FAC501FACE01FADB01FAE001FAE801FAF001FAF801FB0001FB9201FB9401FBCA",
|
|
95
|
+
"01FBF001FBF901FFFE02A6DF02A70002B73902B74002B81D02B82002CEA102CEB002EBE002EBF002EE5D02F80002FA1D02FFFE03134A0313500323AF",
|
|
96
|
+
"03FFFE03FFFF04FFFE04FFFF05FFFE05FFFF06FFFE06FFFF07FFFE07FFFF08FFFE08FFFF09FFFE09FFFF0AFFFE0AFFFF0BFFFE0BFFFF0CFFFE0CFFFF",
|
|
97
|
+
"0DFFFE0DFFFF0E00010E00010E00200E007F0E01000E01EF0EFFFE10FFFF",
|
|
98
|
+
].join("");
|
|
99
|
+
const UNICODE_15_1_ASSIGNED_RANGES = decodeUnicodeRanges(UNICODE_15_1_ASSIGNED_RANGES_HEX);
|
|
100
|
+
const strictDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
101
|
+
export class ProtocolV3Error extends Error {
|
|
102
|
+
}
|
|
103
|
+
export var DirectionV3;
|
|
104
|
+
(function (DirectionV3) {
|
|
105
|
+
DirectionV3[DirectionV3["ClientToServer"] = 1] = "ClientToServer";
|
|
106
|
+
DirectionV3[DirectionV3["ServerToClient"] = 2] = "ServerToClient";
|
|
107
|
+
})(DirectionV3 || (DirectionV3 = {}));
|
|
108
|
+
export var CipherSuiteV3;
|
|
109
|
+
(function (CipherSuiteV3) {
|
|
110
|
+
CipherSuiteV3[CipherSuiteV3["ChaCha20Poly1305"] = 1] = "ChaCha20Poly1305";
|
|
111
|
+
CipherSuiteV3[CipherSuiteV3["AES256GCM"] = 2] = "AES256GCM";
|
|
112
|
+
})(CipherSuiteV3 || (CipherSuiteV3 = {}));
|
|
113
|
+
export var InnerTypeV3;
|
|
114
|
+
(function (InnerTypeV3) {
|
|
115
|
+
InnerTypeV3[InnerTypeV3["Open"] = 1] = "Open";
|
|
116
|
+
InnerTypeV3[InnerTypeV3["OpenACK"] = 2] = "OpenACK";
|
|
117
|
+
InnerTypeV3[InnerTypeV3["OpenReject"] = 3] = "OpenReject";
|
|
118
|
+
InnerTypeV3[InnerTypeV3["Data"] = 4] = "Data";
|
|
119
|
+
InnerTypeV3[InnerTypeV3["FIN"] = 5] = "FIN";
|
|
120
|
+
InnerTypeV3[InnerTypeV3["StreamKeyUpdate"] = 6] = "StreamKeyUpdate";
|
|
121
|
+
InnerTypeV3[InnerTypeV3["SessionReady"] = 16] = "SessionReady";
|
|
122
|
+
InnerTypeV3[InnerTypeV3["Ping"] = 17] = "Ping";
|
|
123
|
+
InnerTypeV3[InnerTypeV3["Pong"] = 18] = "Pong";
|
|
124
|
+
InnerTypeV3[InnerTypeV3["SessionKeyUpdate"] = 19] = "SessionKeyUpdate";
|
|
125
|
+
InnerTypeV3[InnerTypeV3["StreamReset"] = 20] = "StreamReset";
|
|
126
|
+
InnerTypeV3[InnerTypeV3["GoAway"] = 21] = "GoAway";
|
|
127
|
+
InnerTypeV3[InnerTypeV3["SessionClose"] = 22] = "SessionClose";
|
|
128
|
+
InnerTypeV3[InnerTypeV3["SessionReadyACK"] = 23] = "SessionReadyACK";
|
|
129
|
+
InnerTypeV3[InnerTypeV3["SessionKeyUpdateACK"] = 24] = "SessionKeyUpdateACK";
|
|
130
|
+
InnerTypeV3[InnerTypeV3["StreamKeyUpdateACK"] = 25] = "StreamKeyUpdateACK";
|
|
131
|
+
InnerTypeV3[InnerTypeV3["SessionReadyConfirm"] = 26] = "SessionReadyConfirm";
|
|
132
|
+
})(InnerTypeV3 || (InnerTypeV3 = {}));
|
|
133
|
+
export function deriveEpochZero(sessionPRK, direction) {
|
|
134
|
+
assertBytes("session PRK", sessionPRK, 32);
|
|
135
|
+
assertDirection(direction);
|
|
136
|
+
const epochSecret = expand32(sessionPRK, labelWith(FLOWERSEC_V3_CRYPTO_LABELS["epoch-zero"], byte(direction)));
|
|
137
|
+
return deriveEpochRoots(epochSecret);
|
|
138
|
+
}
|
|
139
|
+
export function deriveEpochRoots(epochSecret) {
|
|
140
|
+
assertBytes("epoch secret", epochSecret, 32);
|
|
141
|
+
return {
|
|
142
|
+
epochSecret: epochSecret.slice(),
|
|
143
|
+
controlRoot: expand32(epochSecret, labelWith(FLOWERSEC_V3_CRYPTO_LABELS["control-root"])),
|
|
144
|
+
streamRoot: expand32(epochSecret, labelWith(FLOWERSEC_V3_CRYPTO_LABELS["stream-root"])),
|
|
145
|
+
setupRoot: expand32(epochSecret, labelWith(FLOWERSEC_V3_CRYPTO_LABELS["setup-root"])),
|
|
146
|
+
rekeyRoot: expand32(epochSecret, labelWith(FLOWERSEC_V3_CRYPTO_LABELS["rekey-root"])),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
export function deriveNextEpoch(rekeyRoot, h3, direction, nextEpoch) {
|
|
150
|
+
assertBytes("rekey root", rekeyRoot, 32);
|
|
151
|
+
assertBytes("H3", h3, 32);
|
|
152
|
+
assertDirection(direction);
|
|
153
|
+
assertU32("next epoch", nextEpoch);
|
|
154
|
+
return expand32(rekeyRoot, labelWith(FLOWERSEC_V3_CRYPTO_LABELS["next-epoch"], h3, byte(direction), u32be(nextEpoch)));
|
|
155
|
+
}
|
|
156
|
+
export function deriveStreamMaterial(streamRoot, h3, logicalStreamID, direction, epoch) {
|
|
157
|
+
assertBytes("stream root", streamRoot, 32);
|
|
158
|
+
assertBytes("H3", h3, 32);
|
|
159
|
+
assertLogicalStreamID(logicalStreamID);
|
|
160
|
+
assertDirection(direction);
|
|
161
|
+
assertU32("epoch", epoch);
|
|
162
|
+
const secret = expand32(streamRoot, labelWith(FLOWERSEC_V3_CRYPTO_LABELS.stream, h3, u64be(logicalStreamID), byte(direction), u32be(epoch)));
|
|
163
|
+
return {
|
|
164
|
+
secret,
|
|
165
|
+
recordKey: expand32(secret, labelWith(FLOWERSEC_V3_CRYPTO_LABELS["record-key"])),
|
|
166
|
+
noncePrefix: expand(sha256, secret, labelWith(FLOWERSEC_V3_CRYPTO_LABELS.nonce), 4),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
export function deriveControlMaterial(controlRoot, h3, direction, epoch) {
|
|
170
|
+
assertBytes("control root", controlRoot, 32);
|
|
171
|
+
assertBytes("H3", h3, 32);
|
|
172
|
+
assertDirection(direction);
|
|
173
|
+
assertU32("epoch", epoch);
|
|
174
|
+
const secret = expand32(controlRoot, labelWith(FLOWERSEC_V3_CRYPTO_LABELS.control, h3, u64be(0n), byte(direction), u32be(epoch)));
|
|
175
|
+
return {
|
|
176
|
+
secret,
|
|
177
|
+
recordKey: expand32(secret, labelWith(FLOWERSEC_V3_CRYPTO_LABELS["record-key"])),
|
|
178
|
+
noncePrefix: expand(sha256, secret, labelWith(FLOWERSEC_V3_CRYPTO_LABELS.nonce), 4),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
export function computeSetupMAC(setupRoot, h3, preface) {
|
|
182
|
+
assertBytes("setup root", setupRoot, 32);
|
|
183
|
+
assertBytes("H3", h3, 32);
|
|
184
|
+
const raw = encodeSetupPreface(preface);
|
|
185
|
+
return hmac(sha256, setupRoot, exactLabelWith(FLOWERSEC_V3_CRYPTO_LABELS["setup-mac"], h3, raw.subarray(0, 24)));
|
|
186
|
+
}
|
|
187
|
+
export function encodeSetupPreface(preface) {
|
|
188
|
+
assertLogicalStreamID(preface.logicalStreamID);
|
|
189
|
+
if ((preface.openerRole !== 1 && preface.openerRole !== 2) ||
|
|
190
|
+
(preface.openerRole === 1 && (preface.logicalStreamID & 1n) !== 1n) ||
|
|
191
|
+
(preface.openerRole === 2 && (preface.logicalStreamID & 1n) !== 0n)) {
|
|
192
|
+
throw new ProtocolV3Error("invalid FSS3 opener or logical stream ID");
|
|
193
|
+
}
|
|
194
|
+
assertU32("initial send epoch", preface.initialSendEpoch);
|
|
195
|
+
assertBytes("setup MAC", preface.setupMAC, 32);
|
|
196
|
+
const out = new Uint8Array(SETUP_PREFACE_BYTES);
|
|
197
|
+
out.set(encoder.encode("FSS3"), 0);
|
|
198
|
+
out[4] = 3;
|
|
199
|
+
out[5] = preface.openerRole;
|
|
200
|
+
out.set(u64be(preface.logicalStreamID), 8);
|
|
201
|
+
out.set(u32be(preface.initialSendEpoch), 16);
|
|
202
|
+
out.set(preface.setupMAC, 24);
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
export function decodeSetupPrefaceV3(raw) {
|
|
206
|
+
if (raw.length !== SETUP_PREFACE_BYTES ||
|
|
207
|
+
raw[0] !== 0x46 || raw[1] !== 0x53 || raw[2] !== 0x53 || raw[3] !== 0x33 ||
|
|
208
|
+
raw[4] !== 3 || raw[6] !== 0 || raw[7] !== 0 || readU32be(raw, 20) !== 0) {
|
|
209
|
+
throw new ProtocolV3Error("invalid FSS3 setup preface");
|
|
210
|
+
}
|
|
211
|
+
const openerRole = raw[5];
|
|
212
|
+
const logicalStreamID = readU64be(raw, 8);
|
|
213
|
+
if ((openerRole !== 1 && openerRole !== 2) ||
|
|
214
|
+
logicalStreamID === 0n ||
|
|
215
|
+
(openerRole === 1 && (logicalStreamID & 1n) !== 1n) ||
|
|
216
|
+
(openerRole === 2 && (logicalStreamID & 1n) !== 0n)) {
|
|
217
|
+
throw new ProtocolV3Error("invalid FSS3 opener or logical stream ID");
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
openerRole,
|
|
221
|
+
logicalStreamID,
|
|
222
|
+
initialSendEpoch: readU32be(raw, 16),
|
|
223
|
+
setupMAC: raw.slice(24, 56),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
export function verifySetupMAC(setupRoot, h3, preface) {
|
|
227
|
+
const want = computeSetupMAC(setupRoot, h3, { ...preface, setupMAC: new Uint8Array(32) });
|
|
228
|
+
return bytesEqual(want, preface.setupMAC);
|
|
229
|
+
}
|
|
230
|
+
export function computeFSS3HashV3(raw) {
|
|
231
|
+
decodeSetupPrefaceV3(raw);
|
|
232
|
+
return computeValidatedFSS3HashV3Internal(raw);
|
|
233
|
+
}
|
|
234
|
+
/** @internal Hashes an FSS3 preface that was encoded or decoded by this module. */
|
|
235
|
+
export function computeValidatedFSS3HashV3Internal(raw) {
|
|
236
|
+
return sha256(raw);
|
|
237
|
+
}
|
|
238
|
+
export function encodeRecordHeader(header) {
|
|
239
|
+
validateRecordHeader(header);
|
|
240
|
+
const out = new Uint8Array(RECORD_HEADER_BYTES);
|
|
241
|
+
out.set(encoder.encode("FSR3"), 0);
|
|
242
|
+
out[4] = 3;
|
|
243
|
+
out[5] = RECORD_HEADER_BYTES;
|
|
244
|
+
out.set(u32be(header.epoch), 8);
|
|
245
|
+
out.set(u64be(header.sequence), 12);
|
|
246
|
+
out.set(u32be(header.ciphertextLength), 20);
|
|
247
|
+
return out;
|
|
248
|
+
}
|
|
249
|
+
export function decodeRecordHeader(raw) {
|
|
250
|
+
if (raw.length !== RECORD_HEADER_BYTES ||
|
|
251
|
+
raw[0] !== 0x46 ||
|
|
252
|
+
raw[1] !== 0x53 ||
|
|
253
|
+
raw[2] !== 0x52 ||
|
|
254
|
+
raw[3] !== 0x33 ||
|
|
255
|
+
raw[4] !== 3 ||
|
|
256
|
+
raw[5] !== RECORD_HEADER_BYTES ||
|
|
257
|
+
raw[6] !== 0 ||
|
|
258
|
+
raw[7] !== 0) {
|
|
259
|
+
throw new ProtocolV3Error("invalid FSR3 header");
|
|
260
|
+
}
|
|
261
|
+
const header = {
|
|
262
|
+
epoch: readU32be(raw, 8),
|
|
263
|
+
sequence: readU64be(raw, 12),
|
|
264
|
+
ciphertextLength: readU32be(raw, 20),
|
|
265
|
+
};
|
|
266
|
+
validateRecordHeader(header);
|
|
267
|
+
return header;
|
|
268
|
+
}
|
|
269
|
+
export function buildDataInner(payload) {
|
|
270
|
+
if (payload.length < 1 || payload.length > MAX_DATA_BYTES) {
|
|
271
|
+
throw new ProtocolV3Error("invalid v3 DATA payload length");
|
|
272
|
+
}
|
|
273
|
+
const out = new Uint8Array(INNER_HEADER_BYTES + payload.length);
|
|
274
|
+
out[0] = 4;
|
|
275
|
+
out.set(u32be(payload.length), 4);
|
|
276
|
+
out.set(payload, INNER_HEADER_BYTES);
|
|
277
|
+
return out;
|
|
278
|
+
}
|
|
279
|
+
export function encodeInnerRecordV3(type, payload) {
|
|
280
|
+
validateInnerPayload(type, payload.length);
|
|
281
|
+
const out = new Uint8Array(INNER_HEADER_BYTES + payload.length);
|
|
282
|
+
out[0] = type;
|
|
283
|
+
out.set(u32be(payload.length), 4);
|
|
284
|
+
out.set(payload, INNER_HEADER_BYTES);
|
|
285
|
+
return out;
|
|
286
|
+
}
|
|
287
|
+
export function decodeInnerRecordV3(raw) {
|
|
288
|
+
if (raw.length < INNER_HEADER_BYTES || raw[1] !== 0 || raw[2] !== 0 || raw[3] !== 0) {
|
|
289
|
+
throw new ProtocolV3Error("invalid FSR3 inner record");
|
|
290
|
+
}
|
|
291
|
+
const length = readU32be(raw, 4);
|
|
292
|
+
if (raw.length !== INNER_HEADER_BYTES + length) {
|
|
293
|
+
throw new ProtocolV3Error("invalid FSR3 inner record length");
|
|
294
|
+
}
|
|
295
|
+
const type = raw[0];
|
|
296
|
+
validateInnerPayload(type, length);
|
|
297
|
+
return { type, payload: raw.slice(INNER_HEADER_BYTES) };
|
|
298
|
+
}
|
|
299
|
+
export function encodeOpenPayload(payload) {
|
|
300
|
+
assertLogicalStreamID(payload.logicalStreamID);
|
|
301
|
+
assertBytes("FSS3 hash", payload.fss3Hash, 32);
|
|
302
|
+
const kind = validateOpenKind(payload.kind);
|
|
303
|
+
const metadata = canonicalMetadata(payload.metadata, true);
|
|
304
|
+
return encodeValidatedOpenPayload(payload.logicalStreamID, payload.fss3Hash, kind, metadata);
|
|
305
|
+
}
|
|
306
|
+
/** @internal Encodes OPEN directly from an application metadata value after one strict validation pass. */
|
|
307
|
+
export function encodeOpenPayloadFromMetadataV3Internal(payload) {
|
|
308
|
+
assertLogicalStreamID(payload.logicalStreamID);
|
|
309
|
+
assertBytes("FSS3 hash", payload.fss3Hash, 32);
|
|
310
|
+
const kind = validateOpenKind(payload.kind);
|
|
311
|
+
const metadata = encoder.encode(canonicalStreamMetadataJSONV3Internal(payload.metadata));
|
|
312
|
+
return encodeValidatedOpenPayload(payload.logicalStreamID, payload.fss3Hash, kind, metadata);
|
|
313
|
+
}
|
|
314
|
+
function encodeValidatedOpenPayload(logicalStreamID, fss3Hash, kind, metadata) {
|
|
315
|
+
const total = OPEN_FIXED_PAYLOAD_BYTES + kind.length + metadata.length;
|
|
316
|
+
if (total > MAX_OPEN_BYTES)
|
|
317
|
+
throw new ProtocolV3Error("OPEN payload is too large");
|
|
318
|
+
const out = new Uint8Array(total);
|
|
319
|
+
out.set(u64be(logicalStreamID), 0);
|
|
320
|
+
out.set(fss3Hash, 8);
|
|
321
|
+
new DataView(out.buffer).setUint16(40, kind.length, false);
|
|
322
|
+
new DataView(out.buffer).setUint32(42, metadata.length, false);
|
|
323
|
+
out.set(kind, OPEN_FIXED_PAYLOAD_BYTES);
|
|
324
|
+
out.set(metadata, OPEN_FIXED_PAYLOAD_BYTES + kind.length);
|
|
325
|
+
return out;
|
|
326
|
+
}
|
|
327
|
+
export function decodeOpenPayload(raw) {
|
|
328
|
+
if (raw.length < OPEN_FIXED_PAYLOAD_BYTES || raw.length > MAX_OPEN_BYTES) {
|
|
329
|
+
throw new ProtocolV3Error("invalid OPEN payload length");
|
|
330
|
+
}
|
|
331
|
+
const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);
|
|
332
|
+
const logicalStreamID = view.getBigUint64(0, false);
|
|
333
|
+
assertLogicalStreamID(logicalStreamID);
|
|
334
|
+
const kindLength = view.getUint16(40, false);
|
|
335
|
+
const metadataLength = view.getUint32(42, false);
|
|
336
|
+
if (OPEN_FIXED_PAYLOAD_BYTES + kindLength + metadataLength !== raw.length) {
|
|
337
|
+
throw new ProtocolV3Error("invalid OPEN field lengths");
|
|
338
|
+
}
|
|
339
|
+
let kind;
|
|
340
|
+
try {
|
|
341
|
+
kind = strictDecoder.decode(raw.subarray(OPEN_FIXED_PAYLOAD_BYTES, OPEN_FIXED_PAYLOAD_BYTES + kindLength));
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
throw new ProtocolV3Error("OPEN kind is not valid UTF-8");
|
|
345
|
+
}
|
|
346
|
+
validateOpenKind(kind);
|
|
347
|
+
const metadata = canonicalMetadata(raw.subarray(OPEN_FIXED_PAYLOAD_BYTES + kindLength), false);
|
|
348
|
+
return {
|
|
349
|
+
logicalStreamID,
|
|
350
|
+
fss3Hash: raw.slice(8, 40),
|
|
351
|
+
kind,
|
|
352
|
+
metadata,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
export function computeOpenHashV3(rawOpenPayload) {
|
|
356
|
+
decodeOpenPayload(rawOpenPayload);
|
|
357
|
+
return computeValidatedOpenHashV3Internal(rawOpenPayload);
|
|
358
|
+
}
|
|
359
|
+
/** @internal Hashes an OPEN payload that was encoded or decoded by this module. */
|
|
360
|
+
export function computeValidatedOpenHashV3Internal(rawOpenPayload) {
|
|
361
|
+
return sha256(concat(encoder.encode(FLOWERSEC_V3_CRYPTO_LABELS.open), u32be(rawOpenPayload.length), rawOpenPayload));
|
|
362
|
+
}
|
|
363
|
+
export function encodeOpenACKV3(openHash) {
|
|
364
|
+
assertBytes("OPEN hash", openHash, 32);
|
|
365
|
+
return openHash.slice();
|
|
366
|
+
}
|
|
367
|
+
export function decodeOpenACKV3(raw) {
|
|
368
|
+
assertBytes("OPEN ACK", raw, 32);
|
|
369
|
+
return raw.slice();
|
|
370
|
+
}
|
|
371
|
+
export function encodeOpenRejectV3(openHash, reason) {
|
|
372
|
+
assertBytes("OPEN hash", openHash, 32);
|
|
373
|
+
if (!Number.isInteger(reason) || reason < 1 || reason > 5) {
|
|
374
|
+
throw new ProtocolV3Error("invalid OPEN reject reason");
|
|
375
|
+
}
|
|
376
|
+
const out = new Uint8Array(34);
|
|
377
|
+
out.set(openHash);
|
|
378
|
+
new DataView(out.buffer).setUint16(32, reason, false);
|
|
379
|
+
return out;
|
|
380
|
+
}
|
|
381
|
+
export function decodeOpenRejectV3(raw) {
|
|
382
|
+
if (raw.length !== 34)
|
|
383
|
+
throw new ProtocolV3Error("invalid OPEN reject payload");
|
|
384
|
+
const reason = new DataView(raw.buffer, raw.byteOffset, raw.byteLength).getUint16(32, false);
|
|
385
|
+
if (reason === 0)
|
|
386
|
+
throw new ProtocolV3Error("invalid OPEN reject reason");
|
|
387
|
+
return { openHash: raw.slice(0, 32), reason, knownReason: reason >= 1 && reason <= 5 };
|
|
388
|
+
}
|
|
389
|
+
export function encodeStreamKeyUpdateACKV3(value) {
|
|
390
|
+
assertLogicalStreamID(value.logicalStreamID);
|
|
391
|
+
assertU64("stream rekey transition", value.transition);
|
|
392
|
+
if (value.transition === 0n)
|
|
393
|
+
throw new ProtocolV3Error("stream rekey transition must be non-zero");
|
|
394
|
+
assertU32("stream rekey epoch", value.epoch);
|
|
395
|
+
return concat(u64be(value.logicalStreamID), u64be(value.transition), u32be(value.epoch));
|
|
396
|
+
}
|
|
397
|
+
export function decodeStreamKeyUpdateACKV3(raw) {
|
|
398
|
+
if (raw.length !== 20)
|
|
399
|
+
throw new ProtocolV3Error("invalid STREAM_KEY_UPDATE_ACK payload");
|
|
400
|
+
const value = {
|
|
401
|
+
logicalStreamID: readU64be(raw, 0),
|
|
402
|
+
transition: readU64be(raw, 8),
|
|
403
|
+
epoch: readU32be(raw, 16),
|
|
404
|
+
};
|
|
405
|
+
assertLogicalStreamID(value.logicalStreamID);
|
|
406
|
+
if (value.transition === 0n)
|
|
407
|
+
throw new ProtocolV3Error("stream rekey transition must be non-zero");
|
|
408
|
+
return value;
|
|
409
|
+
}
|
|
410
|
+
export function buildRecordAAD(h3, logicalStreamID, direction, rawHeader) {
|
|
411
|
+
assertBytes("H3", h3, 32);
|
|
412
|
+
assertU64("logical stream ID", logicalStreamID);
|
|
413
|
+
assertDirection(direction);
|
|
414
|
+
decodeRecordHeader(rawHeader);
|
|
415
|
+
return buildValidatedRecordAAD(h3, logicalStreamID, direction, rawHeader);
|
|
416
|
+
}
|
|
417
|
+
function buildValidatedRecordAAD(h3, logicalStreamID, direction, rawHeader) {
|
|
418
|
+
return exactLabelWith(FLOWERSEC_V3_CRYPTO_LABELS["record-aad"], h3, u64be(logicalStreamID), byte(direction), rawHeader);
|
|
419
|
+
}
|
|
420
|
+
export function sealRecord(suite, material, h3, logicalStreamID, direction, header, plaintext) {
|
|
421
|
+
assertBytes("H3", h3, 32);
|
|
422
|
+
assertDirection(direction);
|
|
423
|
+
return sealRecordWireV3Internal(suite, material, h3, logicalStreamID, direction, header, plaintext).ciphertext;
|
|
424
|
+
}
|
|
425
|
+
/** @internal Seals a validated record and returns the single encoded header used as AAD and on wire. */
|
|
426
|
+
export function sealRecordWireV3Internal(suite, material, h3, logicalStreamID, direction, header, plaintext) {
|
|
427
|
+
validateMaterial(material);
|
|
428
|
+
if (plaintext.length + 16 !== header.ciphertextLength) {
|
|
429
|
+
throw new ProtocolV3Error("FSR3 plaintext length does not match the header");
|
|
430
|
+
}
|
|
431
|
+
const rawHeader = encodeRecordHeader(header);
|
|
432
|
+
const nonce = concat(material.noncePrefix, u64be(header.sequence));
|
|
433
|
+
const aad = buildValidatedRecordAAD(h3, logicalStreamID, direction, rawHeader);
|
|
434
|
+
return { rawHeader, ciphertext: cipher(suite, material.recordKey, nonce, aad).encrypt(plaintext) };
|
|
435
|
+
}
|
|
436
|
+
export function openRecord(suite, material, h3, logicalStreamID, direction, header, ciphertext) {
|
|
437
|
+
assertBytes("H3", h3, 32);
|
|
438
|
+
assertDirection(direction);
|
|
439
|
+
const rawHeader = encodeRecordHeader(header);
|
|
440
|
+
return openRecordWithRawHeaderV3Internal(suite, material, h3, logicalStreamID, direction, header, rawHeader, ciphertext);
|
|
441
|
+
}
|
|
442
|
+
/** @internal Opens a record whose exact raw header was already decoded by this module. */
|
|
443
|
+
export function openRecordWithRawHeaderV3Internal(suite, material, h3, logicalStreamID, direction, header, rawHeader, ciphertext) {
|
|
444
|
+
validateMaterial(material);
|
|
445
|
+
if (ciphertext.length !== header.ciphertextLength) {
|
|
446
|
+
throw new ProtocolV3Error("FSR3 ciphertext length does not match the header");
|
|
447
|
+
}
|
|
448
|
+
const nonce = concat(material.noncePrefix, u64be(header.sequence));
|
|
449
|
+
const aad = buildValidatedRecordAAD(h3, logicalStreamID, direction, rawHeader);
|
|
450
|
+
try {
|
|
451
|
+
return cipher(suite, material.recordKey, nonce, aad).decrypt(ciphertext);
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
throw new ProtocolV3Error("v3 record authentication failed");
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function cipher(suite, key, nonce, aad) {
|
|
458
|
+
switch (suite) {
|
|
459
|
+
case CipherSuiteV3.ChaCha20Poly1305:
|
|
460
|
+
return chacha20poly1305(key, nonce, aad);
|
|
461
|
+
case CipherSuiteV3.AES256GCM:
|
|
462
|
+
return gcm(key, nonce, aad);
|
|
463
|
+
default:
|
|
464
|
+
throw new ProtocolV3Error("invalid v3 cipher suite");
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function validateMaterial(material) {
|
|
468
|
+
assertBytes("record key", material.recordKey, 32);
|
|
469
|
+
assertBytes("nonce prefix", material.noncePrefix, 4);
|
|
470
|
+
}
|
|
471
|
+
function validateRecordHeader(header) {
|
|
472
|
+
assertU32("record epoch", header.epoch);
|
|
473
|
+
assertU64("record sequence", header.sequence);
|
|
474
|
+
if (!Number.isInteger(header.ciphertextLength) ||
|
|
475
|
+
header.ciphertextLength < 16 ||
|
|
476
|
+
header.ciphertextLength > MAX_CIPHERTEXT_BYTES) {
|
|
477
|
+
throw new ProtocolV3Error("invalid FSR3 ciphertext length");
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function validateInnerPayload(type, length) {
|
|
481
|
+
switch (type) {
|
|
482
|
+
case InnerTypeV3.Open:
|
|
483
|
+
if (length >= 1 && length <= MAX_OPEN_BYTES)
|
|
484
|
+
return;
|
|
485
|
+
break;
|
|
486
|
+
case InnerTypeV3.Data:
|
|
487
|
+
if (length >= 1 && length <= MAX_DATA_BYTES)
|
|
488
|
+
return;
|
|
489
|
+
break;
|
|
490
|
+
case InnerTypeV3.FIN:
|
|
491
|
+
case InnerTypeV3.SessionReady:
|
|
492
|
+
case InnerTypeV3.SessionReadyACK:
|
|
493
|
+
case InnerTypeV3.SessionReadyConfirm:
|
|
494
|
+
if (length === 0)
|
|
495
|
+
return;
|
|
496
|
+
break;
|
|
497
|
+
case InnerTypeV3.OpenACK:
|
|
498
|
+
if (length === 32)
|
|
499
|
+
return;
|
|
500
|
+
break;
|
|
501
|
+
case InnerTypeV3.OpenReject:
|
|
502
|
+
if (length === 34)
|
|
503
|
+
return;
|
|
504
|
+
break;
|
|
505
|
+
case InnerTypeV3.StreamKeyUpdate:
|
|
506
|
+
if (length === 12)
|
|
507
|
+
return;
|
|
508
|
+
break;
|
|
509
|
+
case InnerTypeV3.Ping:
|
|
510
|
+
case InnerTypeV3.Pong:
|
|
511
|
+
if (length === 8)
|
|
512
|
+
return;
|
|
513
|
+
break;
|
|
514
|
+
case InnerTypeV3.SessionKeyUpdate:
|
|
515
|
+
case InnerTypeV3.SessionKeyUpdateACK:
|
|
516
|
+
case InnerTypeV3.StreamKeyUpdateACK:
|
|
517
|
+
if (length === 20)
|
|
518
|
+
return;
|
|
519
|
+
break;
|
|
520
|
+
case InnerTypeV3.StreamReset:
|
|
521
|
+
case InnerTypeV3.GoAway:
|
|
522
|
+
if (length === 10)
|
|
523
|
+
return;
|
|
524
|
+
break;
|
|
525
|
+
case InnerTypeV3.SessionClose:
|
|
526
|
+
if (length === 2)
|
|
527
|
+
return;
|
|
528
|
+
break;
|
|
529
|
+
default:
|
|
530
|
+
throw new ProtocolV3Error(`unknown FSR3 inner type ${type}`);
|
|
531
|
+
}
|
|
532
|
+
throw new ProtocolV3Error(`invalid FSR3 inner payload length for type ${type}`);
|
|
533
|
+
}
|
|
534
|
+
function expand32(prk, info) {
|
|
535
|
+
return expand(sha256, prk, info, 32);
|
|
536
|
+
}
|
|
537
|
+
function labelWith(label, ...parts) {
|
|
538
|
+
return concat(encoder.encode(label), byte(0), ...parts);
|
|
539
|
+
}
|
|
540
|
+
function exactLabelWith(label, ...parts) {
|
|
541
|
+
return concat(encoder.encode(label), ...parts);
|
|
542
|
+
}
|
|
543
|
+
function concat(...parts) {
|
|
544
|
+
const size = parts.reduce((total, part) => total + part.length, 0);
|
|
545
|
+
const out = new Uint8Array(size);
|
|
546
|
+
let offset = 0;
|
|
547
|
+
for (const part of parts) {
|
|
548
|
+
out.set(part, offset);
|
|
549
|
+
offset += part.length;
|
|
550
|
+
}
|
|
551
|
+
return out;
|
|
552
|
+
}
|
|
553
|
+
function byte(value) {
|
|
554
|
+
return Uint8Array.of(value);
|
|
555
|
+
}
|
|
556
|
+
function u32be(value) {
|
|
557
|
+
assertU32("uint32", value);
|
|
558
|
+
const out = new Uint8Array(4);
|
|
559
|
+
new DataView(out.buffer).setUint32(0, value, false);
|
|
560
|
+
return out;
|
|
561
|
+
}
|
|
562
|
+
function u64be(value) {
|
|
563
|
+
assertU64("uint64", value);
|
|
564
|
+
const out = new Uint8Array(8);
|
|
565
|
+
new DataView(out.buffer).setBigUint64(0, value, false);
|
|
566
|
+
return out;
|
|
567
|
+
}
|
|
568
|
+
function readU32be(value, offset) {
|
|
569
|
+
return new DataView(value.buffer, value.byteOffset, value.byteLength).getUint32(offset, false);
|
|
570
|
+
}
|
|
571
|
+
function readU64be(value, offset) {
|
|
572
|
+
return new DataView(value.buffer, value.byteOffset, value.byteLength).getBigUint64(offset, false);
|
|
573
|
+
}
|
|
574
|
+
function assertDirection(direction) {
|
|
575
|
+
if (direction !== DirectionV3.ClientToServer &&
|
|
576
|
+
direction !== DirectionV3.ServerToClient) {
|
|
577
|
+
throw new ProtocolV3Error("invalid v3 direction");
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
function assertLogicalStreamID(value) {
|
|
581
|
+
assertU64("logical stream ID", value);
|
|
582
|
+
if (value === 0n)
|
|
583
|
+
throw new ProtocolV3Error("logical stream ID must be non-zero");
|
|
584
|
+
}
|
|
585
|
+
function assertU32(name, value) {
|
|
586
|
+
if (!Number.isInteger(value) || value < 0 || value > MAX_UINT32) {
|
|
587
|
+
throw new ProtocolV3Error(`${name} must be uint32`);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
function assertU64(name, value) {
|
|
591
|
+
if (value < 0n || value > MAX_UINT64)
|
|
592
|
+
throw new ProtocolV3Error(`${name} must be uint64`);
|
|
593
|
+
}
|
|
594
|
+
function assertBytes(name, value, length) {
|
|
595
|
+
if (value.length !== length)
|
|
596
|
+
throw new ProtocolV3Error(`${name} must be ${length} bytes`);
|
|
597
|
+
}
|
|
598
|
+
function bytesEqual(left, right) {
|
|
599
|
+
if (left.length !== right.length)
|
|
600
|
+
return false;
|
|
601
|
+
let difference = 0;
|
|
602
|
+
for (let index = 0; index < left.length; index++)
|
|
603
|
+
difference |= left[index] ^ right[index];
|
|
604
|
+
return difference === 0;
|
|
605
|
+
}
|
|
606
|
+
function validateOpenKind(value) {
|
|
607
|
+
const encoded = encoder.encode(value);
|
|
608
|
+
if (!validApplicationStreamKind(value)) {
|
|
609
|
+
throw new ProtocolV3Error("invalid OPEN kind");
|
|
610
|
+
}
|
|
611
|
+
return encoded;
|
|
612
|
+
}
|
|
613
|
+
/** @internal */
|
|
614
|
+
export function validApplicationStreamKind(value) {
|
|
615
|
+
if (typeof value !== "string" ||
|
|
616
|
+
!validOpenUnicodeString(value, MAX_OPEN_KIND_BYTES, false)) {
|
|
617
|
+
return false;
|
|
618
|
+
}
|
|
619
|
+
const scalars = Array.from(value);
|
|
620
|
+
return !(isUnicodeWhitespace(scalars[0].codePointAt(0)) ||
|
|
621
|
+
isUnicodeWhitespace(scalars.at(-1).codePointAt(0)));
|
|
622
|
+
}
|
|
623
|
+
function canonicalMetadata(raw, allowEmpty) {
|
|
624
|
+
if (raw.length === 0 && allowEmpty)
|
|
625
|
+
return encoder.encode("{}");
|
|
626
|
+
if (raw.length === 0 || raw.length > MAX_OPEN_METADATA_BYTES) {
|
|
627
|
+
throw new ProtocolV3Error("invalid OPEN metadata length");
|
|
628
|
+
}
|
|
629
|
+
let text;
|
|
630
|
+
try {
|
|
631
|
+
text = strictDecoder.decode(raw);
|
|
632
|
+
}
|
|
633
|
+
catch {
|
|
634
|
+
throw new ProtocolV3Error("OPEN metadata is not valid UTF-8");
|
|
635
|
+
}
|
|
636
|
+
let value;
|
|
637
|
+
try {
|
|
638
|
+
value = JSON.parse(text);
|
|
639
|
+
}
|
|
640
|
+
catch {
|
|
641
|
+
throw new ProtocolV3Error("OPEN metadata is not valid JSON");
|
|
642
|
+
}
|
|
643
|
+
if (!isJSONObject(value))
|
|
644
|
+
throw new ProtocolV3Error("OPEN metadata root must be an object");
|
|
645
|
+
const canonical = canonicalStreamMetadataJSONV3Internal(value);
|
|
646
|
+
if (canonical !== text ||
|
|
647
|
+
encoder.encode(canonical).length > MAX_OPEN_METADATA_BYTES) {
|
|
648
|
+
throw new ProtocolV3Error("OPEN metadata is not canonical JSON");
|
|
649
|
+
}
|
|
650
|
+
return encoder.encode(canonical);
|
|
651
|
+
}
|
|
652
|
+
/** @internal Validates a public metadata value against the exact OPEN contract. */
|
|
653
|
+
export function canonicalStreamMetadataJSONV3Internal(value) {
|
|
654
|
+
if (!isJSONObject(value))
|
|
655
|
+
throw new ProtocolV3Error("OPEN metadata root must be an object");
|
|
656
|
+
const state = { nodes: -1 };
|
|
657
|
+
const canonical = canonicalValidatedMetadataJSONString(value, 1, state);
|
|
658
|
+
if (encoder.encode(canonical).length > MAX_OPEN_METADATA_BYTES) {
|
|
659
|
+
throw new ProtocolV3Error("OPEN metadata is too large");
|
|
660
|
+
}
|
|
661
|
+
return canonical;
|
|
662
|
+
}
|
|
663
|
+
function canonicalValidatedMetadataJSONString(value, depth, state) {
|
|
664
|
+
if (depth > MAX_OPEN_METADATA_DEPTH)
|
|
665
|
+
throw new ProtocolV3Error("OPEN metadata exceeds depth limit");
|
|
666
|
+
state.nodes += 1;
|
|
667
|
+
if (state.nodes > MAX_OPEN_METADATA_NODES)
|
|
668
|
+
throw new ProtocolV3Error("OPEN metadata exceeds node limit");
|
|
669
|
+
if (value === null)
|
|
670
|
+
return "null";
|
|
671
|
+
if (typeof value === "boolean")
|
|
672
|
+
return value ? "true" : "false";
|
|
673
|
+
if (typeof value === "number") {
|
|
674
|
+
if (!Number.isSafeInteger(value) || Object.is(value, -0))
|
|
675
|
+
throw new ProtocolV3Error("OPEN metadata number is not an I-JSON safe integer");
|
|
676
|
+
return value.toString(10);
|
|
677
|
+
}
|
|
678
|
+
if (typeof value === "string") {
|
|
679
|
+
if (!validOpenUnicodeString(value, MAX_OPEN_METADATA_STRING_BYTES, true)) {
|
|
680
|
+
throw new ProtocolV3Error("invalid OPEN metadata string");
|
|
681
|
+
}
|
|
682
|
+
return quoteCanonicalJSONString(value);
|
|
683
|
+
}
|
|
684
|
+
if (Array.isArray(value)) {
|
|
685
|
+
const length = value.length;
|
|
686
|
+
if (length > MAX_OPEN_METADATA_ARRAY)
|
|
687
|
+
throw new ProtocolV3Error("OPEN metadata array is too large");
|
|
688
|
+
const items = new Array(length);
|
|
689
|
+
for (let index = 0; index < length; index++) {
|
|
690
|
+
const item = value[index];
|
|
691
|
+
items[index] = canonicalValidatedMetadataJSONString(item, depth + 1, state);
|
|
692
|
+
}
|
|
693
|
+
return `[${items.join(",")}]`;
|
|
694
|
+
}
|
|
695
|
+
if (!isJSONObject(value))
|
|
696
|
+
throw new ProtocolV3Error("unsupported OPEN metadata value");
|
|
697
|
+
const keys = Object.keys(value).sort();
|
|
698
|
+
if (keys.length > MAX_OPEN_METADATA_KEYS)
|
|
699
|
+
throw new ProtocolV3Error("OPEN metadata object is too large");
|
|
700
|
+
const members = new Array(keys.length);
|
|
701
|
+
let index = 0;
|
|
702
|
+
for (const key of keys) {
|
|
703
|
+
if (!validOpenUnicodeString(key, MAX_OPEN_METADATA_KEY_BYTES, false)) {
|
|
704
|
+
throw new ProtocolV3Error("invalid OPEN metadata key");
|
|
705
|
+
}
|
|
706
|
+
const item = value[key];
|
|
707
|
+
members[index++] = `${quoteCanonicalJSONString(key)}:${canonicalValidatedMetadataJSONString(item, depth + 1, state)}`;
|
|
708
|
+
}
|
|
709
|
+
return `{${members.join(",")}}`;
|
|
710
|
+
}
|
|
711
|
+
function quoteCanonicalJSONString(value) {
|
|
712
|
+
return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
713
|
+
}
|
|
714
|
+
function validOpenUnicodeString(value, maxBytes, allowEmpty) {
|
|
715
|
+
const bytes = encoder.encode(value);
|
|
716
|
+
if (bytes.length > maxBytes ||
|
|
717
|
+
(!allowEmpty && bytes.length === 0) ||
|
|
718
|
+
value.normalize("NFC") !== value)
|
|
719
|
+
return false;
|
|
720
|
+
for (const scalar of value) {
|
|
721
|
+
const codePoint = scalar.codePointAt(0);
|
|
722
|
+
if ((codePoint >= 0xd800 && codePoint <= 0xdfff) ||
|
|
723
|
+
codePoint <= 0x1f ||
|
|
724
|
+
(codePoint >= 0x7f && codePoint <= 0x9f) ||
|
|
725
|
+
!unicode15_1Assigned(codePoint)) {
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
function unicode15_1Assigned(codePoint) {
|
|
732
|
+
let low = 0;
|
|
733
|
+
let high = UNICODE_15_1_ASSIGNED_RANGES.length - 1;
|
|
734
|
+
while (low <= high) {
|
|
735
|
+
const middle = (low + high) >>> 1;
|
|
736
|
+
const [start, end] = UNICODE_15_1_ASSIGNED_RANGES[middle];
|
|
737
|
+
if (codePoint < start)
|
|
738
|
+
high = middle - 1;
|
|
739
|
+
else if (codePoint > end)
|
|
740
|
+
low = middle + 1;
|
|
741
|
+
else
|
|
742
|
+
return true;
|
|
743
|
+
}
|
|
744
|
+
return false;
|
|
745
|
+
}
|
|
746
|
+
function decodeUnicodeRanges(value) {
|
|
747
|
+
if (value.length % 12 !== 0)
|
|
748
|
+
throw new Error("invalid committed Unicode range table");
|
|
749
|
+
const ranges = [];
|
|
750
|
+
for (let offset = 0; offset < value.length; offset += 12) {
|
|
751
|
+
ranges.push([
|
|
752
|
+
Number.parseInt(value.slice(offset, offset + 6), 16),
|
|
753
|
+
Number.parseInt(value.slice(offset + 6, offset + 12), 16),
|
|
754
|
+
]);
|
|
755
|
+
}
|
|
756
|
+
return ranges;
|
|
757
|
+
}
|
|
758
|
+
function isUnicodeWhitespace(codePoint) {
|
|
759
|
+
return ((codePoint >= 0x0009 && codePoint <= 0x000d) ||
|
|
760
|
+
codePoint === 0x0020 ||
|
|
761
|
+
codePoint === 0x0085 ||
|
|
762
|
+
codePoint === 0x00a0 ||
|
|
763
|
+
codePoint === 0x1680 ||
|
|
764
|
+
(codePoint >= 0x2000 && codePoint <= 0x200a) ||
|
|
765
|
+
codePoint === 0x2028 ||
|
|
766
|
+
codePoint === 0x2029 ||
|
|
767
|
+
codePoint === 0x202f ||
|
|
768
|
+
codePoint === 0x205f ||
|
|
769
|
+
codePoint === 0x3000);
|
|
770
|
+
}
|
|
771
|
+
function isJSONObject(value) {
|
|
772
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
773
|
+
}
|