ai-remote 0.1.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 (114) hide show
  1. package/README.md +50 -0
  2. package/dist/index.js +7 -0
  3. package/dist/index.js.map +7 -0
  4. package/dist/protocols/index.js +35 -0
  5. package/dist/protocols/index.js.map +7 -0
  6. package/dist/protocols/rdp/buffer.js +170 -0
  7. package/dist/protocols/rdp/buffer.js.map +7 -0
  8. package/dist/protocols/rdp/caps.js +260 -0
  9. package/dist/protocols/rdp/caps.js.map +7 -0
  10. package/dist/protocols/rdp/cert.js +135 -0
  11. package/dist/protocols/rdp/cert.js.map +7 -0
  12. package/dist/protocols/rdp/client.js +1068 -0
  13. package/dist/protocols/rdp/client.js.map +7 -0
  14. package/dist/protocols/rdp/cliprdr.js +240 -0
  15. package/dist/protocols/rdp/cliprdr.js.map +7 -0
  16. package/dist/protocols/rdp/credssp.js +254 -0
  17. package/dist/protocols/rdp/credssp.js.map +7 -0
  18. package/dist/protocols/rdp/crypto.js +369 -0
  19. package/dist/protocols/rdp/crypto.js.map +7 -0
  20. package/dist/protocols/rdp/display.js +589 -0
  21. package/dist/protocols/rdp/display.js.map +7 -0
  22. package/dist/protocols/rdp/gcc.js +132 -0
  23. package/dist/protocols/rdp/gcc.js.map +7 -0
  24. package/dist/protocols/rdp/keymap.js +132 -0
  25. package/dist/protocols/rdp/keymap.js.map +7 -0
  26. package/dist/protocols/rdp/mcs.js +223 -0
  27. package/dist/protocols/rdp/mcs.js.map +7 -0
  28. package/dist/protocols/rdp/ntlm.js +305 -0
  29. package/dist/protocols/rdp/ntlm.js.map +7 -0
  30. package/dist/protocols/rdp/pdu.js +444 -0
  31. package/dist/protocols/rdp/pdu.js.map +7 -0
  32. package/dist/protocols/rdp/rail.js +383 -0
  33. package/dist/protocols/rdp/rail.js.map +7 -0
  34. package/dist/protocols/rdp/rle.js +276 -0
  35. package/dist/protocols/rdp/rle.js.map +7 -0
  36. package/dist/protocols/rdp/sec.js +156 -0
  37. package/dist/protocols/rdp/sec.js.map +7 -0
  38. package/dist/protocols/rdp/session.js +307 -0
  39. package/dist/protocols/rdp/session.js.map +7 -0
  40. package/dist/protocols/rdp/tls.js +354 -0
  41. package/dist/protocols/rdp/tls.js.map +7 -0
  42. package/dist/protocols/rdp/vchannel.js +59 -0
  43. package/dist/protocols/rdp/vchannel.js.map +7 -0
  44. package/dist/protocols/rdp/x224.js +110 -0
  45. package/dist/protocols/rdp/x224.js.map +7 -0
  46. package/dist/protocols/ssh/kex.js +177 -0
  47. package/dist/protocols/ssh/kex.js.map +7 -0
  48. package/dist/protocols/ssh/messages.js +55 -0
  49. package/dist/protocols/ssh/messages.js.map +7 -0
  50. package/dist/protocols/ssh/session.js +608 -0
  51. package/dist/protocols/ssh/session.js.map +7 -0
  52. package/dist/protocols/ssh/terminal.js +191 -0
  53. package/dist/protocols/ssh/terminal.js.map +7 -0
  54. package/dist/protocols/ssh/transport.js +611 -0
  55. package/dist/protocols/ssh/transport.js.map +7 -0
  56. package/dist/protocols/ssh/wire.js +189 -0
  57. package/dist/protocols/ssh/wire.js.map +7 -0
  58. package/dist/protocols/types.js +1 -0
  59. package/dist/protocols/types.js.map +7 -0
  60. package/dist/protocols/vnc/input.js +16 -0
  61. package/dist/protocols/vnc/input.js.map +7 -0
  62. package/dist/protocols/vnc/session.js +259 -0
  63. package/dist/protocols/vnc/session.js.map +7 -0
  64. package/dist/shared/connection.js +114 -0
  65. package/dist/shared/connection.js.map +7 -0
  66. package/dist/shared/device.js +1 -0
  67. package/dist/shared/device.js.map +7 -0
  68. package/dist/shared/hosts.js +133 -0
  69. package/dist/shared/hosts.js.map +7 -0
  70. package/dist/shared/icons.js +42 -0
  71. package/dist/shared/icons.js.map +7 -0
  72. package/dist/shared/protocol.js +40 -0
  73. package/dist/shared/protocol.js.map +7 -0
  74. package/dist/shared/signals.js +42 -0
  75. package/dist/shared/signals.js.map +7 -0
  76. package/package.json +82 -0
  77. package/src/index.ts +14 -0
  78. package/src/protocols/index.ts +72 -0
  79. package/src/protocols/rdp/buffer.ts +204 -0
  80. package/src/protocols/rdp/caps.ts +304 -0
  81. package/src/protocols/rdp/cert.ts +190 -0
  82. package/src/protocols/rdp/client.ts +1363 -0
  83. package/src/protocols/rdp/cliprdr.ts +344 -0
  84. package/src/protocols/rdp/credssp.ts +319 -0
  85. package/src/protocols/rdp/crypto.ts +416 -0
  86. package/src/protocols/rdp/display.ts +750 -0
  87. package/src/protocols/rdp/gcc.ts +162 -0
  88. package/src/protocols/rdp/keymap.ts +68 -0
  89. package/src/protocols/rdp/mcs.ts +283 -0
  90. package/src/protocols/rdp/ntlm.ts +380 -0
  91. package/src/protocols/rdp/pdu.ts +497 -0
  92. package/src/protocols/rdp/rail.ts +480 -0
  93. package/src/protocols/rdp/rle.ts +343 -0
  94. package/src/protocols/rdp/sec.ts +184 -0
  95. package/src/protocols/rdp/session.ts +393 -0
  96. package/src/protocols/rdp/tls.ts +452 -0
  97. package/src/protocols/rdp/vchannel.ts +89 -0
  98. package/src/protocols/rdp/x224.ts +139 -0
  99. package/src/protocols/ssh/kex.ts +227 -0
  100. package/src/protocols/ssh/messages.ts +54 -0
  101. package/src/protocols/ssh/session.ts +878 -0
  102. package/src/protocols/ssh/terminal.ts +242 -0
  103. package/src/protocols/ssh/transport.ts +732 -0
  104. package/src/protocols/ssh/wire.ts +220 -0
  105. package/src/protocols/types.ts +146 -0
  106. package/src/protocols/vnc/input.ts +13 -0
  107. package/src/protocols/vnc/novnc.d.ts +75 -0
  108. package/src/protocols/vnc/session.ts +367 -0
  109. package/src/shared/connection.ts +206 -0
  110. package/src/shared/device.ts +28 -0
  111. package/src/shared/hosts.ts +240 -0
  112. package/src/shared/icons.ts +49 -0
  113. package/src/shared/protocol.ts +55 -0
  114. package/src/shared/signals.ts +67 -0
@@ -0,0 +1,343 @@
1
+ // Interleaved RLE bitmap decompression (MS-RDPEGDI 3.1.9) plus the pixel
2
+ // conversion that turns decoded pixels into something a canvas can accept.
3
+ //
4
+ // Pixels are decoded into a Uint32Array of raw values first, so one copy of the
5
+ // algorithm serves every colour depth; only the final conversion to RGBA knows
6
+ // how a pixel is laid out.
7
+
8
+ const REGULAR_BG_RUN = 0x00;
9
+ const REGULAR_FG_RUN = 0x01;
10
+ const REGULAR_FGBG_IMAGE = 0x02;
11
+ const REGULAR_COLOR_RUN = 0x03;
12
+ const REGULAR_COLOR_IMAGE = 0x04;
13
+ const LITE_SET_FG_FG_RUN = 0x0c;
14
+ const LITE_SET_FG_FGBG_IMAGE = 0x0d;
15
+ const LITE_DITHERED_RUN = 0x0e;
16
+ const MEGA_MEGA_BG_RUN = 0xf0;
17
+ const MEGA_MEGA_FG_RUN = 0xf1;
18
+ const MEGA_MEGA_FGBG_IMAGE = 0xf2;
19
+ const MEGA_MEGA_COLOR_RUN = 0xf3;
20
+ const MEGA_MEGA_COLOR_IMAGE = 0xf4;
21
+ const MEGA_MEGA_SET_FG_RUN = 0xf6;
22
+ const MEGA_MEGA_SET_FGBG_IMAGE = 0xf7;
23
+ const MEGA_MEGA_DITHERED_RUN = 0xf8;
24
+ const SPECIAL_FGBG_1 = 0xf9;
25
+ const SPECIAL_FGBG_2 = 0xfa;
26
+ const SPECIAL_WHITE = 0xfd;
27
+ const SPECIAL_BLACK = 0xfe;
28
+
29
+ const MASK_SPECIAL_FGBG_1 = 0x03;
30
+ const MASK_SPECIAL_FGBG_2 = 0x05;
31
+
32
+ /** Regular orders pack the code in the top bits; LITE and MEGA use a nibble. */
33
+ function extractCodeId(header: number) {
34
+ if ((header & 0xc0) !== 0xc0) return header >> 5;
35
+ if ((header & 0xf0) === 0xf0) return header;
36
+ return header >> 4;
37
+ }
38
+
39
+ export function rleDecompress(src: Uint8Array, width: number, height: number, bytesPerPixel: number) {
40
+ const pixels = new Uint32Array(width * height);
41
+ const white = bytesPerPixel >= 4 ? 0xffffffff : (1 << (bytesPerPixel * 8)) - 1;
42
+
43
+ let srcOffset = 0;
44
+ let dest = 0;
45
+ let foreground = white;
46
+ let insertForegroundPixel = false;
47
+ let firstLine = true;
48
+
49
+ const readPixel = () => {
50
+ let value = 0;
51
+ for (let i = 0; i < bytesPerPixel; i++) value |= src[srcOffset + i] << (i * 8);
52
+ srcOffset += bytesPerPixel;
53
+ return value >>> 0;
54
+ };
55
+
56
+ const runLengthOf = (code: number) => {
57
+ const header = src[srcOffset];
58
+
59
+ if (code === REGULAR_FGBG_IMAGE) {
60
+ const packed = header & 0x1f;
61
+ if (packed === 0) {
62
+ srcOffset += 2;
63
+ return src[srcOffset - 1] + 1;
64
+ }
65
+ srcOffset += 1;
66
+ return packed * 8;
67
+ }
68
+
69
+ if (code === LITE_SET_FG_FGBG_IMAGE) {
70
+ const packed = header & 0x0f;
71
+ if (packed === 0) {
72
+ srcOffset += 2;
73
+ return src[srcOffset - 1] + 1;
74
+ }
75
+ srcOffset += 1;
76
+ return packed * 8;
77
+ }
78
+
79
+ if (
80
+ code === REGULAR_BG_RUN ||
81
+ code === REGULAR_FG_RUN ||
82
+ code === REGULAR_COLOR_RUN ||
83
+ code === REGULAR_COLOR_IMAGE
84
+ ) {
85
+ const packed = header & 0x1f;
86
+ if (packed === 0) {
87
+ srcOffset += 2;
88
+ return src[srcOffset - 1] + 32;
89
+ }
90
+ srcOffset += 1;
91
+ return packed;
92
+ }
93
+
94
+ if (code === LITE_SET_FG_FG_RUN || code === LITE_DITHERED_RUN) {
95
+ const packed = header & 0x0f;
96
+ if (packed === 0) {
97
+ srcOffset += 2;
98
+ return src[srcOffset - 1] + 16;
99
+ }
100
+ srcOffset += 1;
101
+ return packed;
102
+ }
103
+
104
+ // Every MEGA_MEGA order carries a 16-bit run length.
105
+ const length = src[srcOffset + 1] | (src[srcOffset + 2] << 8);
106
+ srcOffset += 3;
107
+ return length;
108
+ };
109
+
110
+ /** Foreground/background image: one bit per pixel, least significant first. */
111
+ const writeFgBgImage = (bitmask: number, count: number) => {
112
+ let mask = 0x01;
113
+ for (let i = 0; i < count; i++) {
114
+ if (firstLine) {
115
+ pixels[dest] = bitmask & mask ? foreground : 0;
116
+ } else {
117
+ const above = pixels[dest - width];
118
+ pixels[dest] = bitmask & mask ? (above ^ foreground) >>> 0 : above;
119
+ }
120
+ dest++;
121
+ mask = (mask << 1) & 0xff;
122
+ }
123
+ };
124
+
125
+ while (srcOffset < src.length && dest < pixels.length) {
126
+ // The first scanline has no row above it to delta against. The switch
127
+ // happens between orders, so a run that crosses the boundary keeps
128
+ // first-line semantics throughout.
129
+ if (firstLine && dest >= width) firstLine = false;
130
+
131
+ const code = extractCodeId(src[srcOffset]);
132
+
133
+ if (code === REGULAR_BG_RUN || code === MEGA_MEGA_BG_RUN) {
134
+ let runLength = runLengthOf(code);
135
+
136
+ if (firstLine) {
137
+ if (insertForegroundPixel) {
138
+ pixels[dest++] = foreground;
139
+ runLength--;
140
+ }
141
+ while (runLength-- > 0 && dest < pixels.length) pixels[dest++] = 0;
142
+ } else {
143
+ if (insertForegroundPixel) {
144
+ pixels[dest] = (pixels[dest - width] ^ foreground) >>> 0;
145
+ dest++;
146
+ runLength--;
147
+ }
148
+ while (runLength-- > 0 && dest < pixels.length) {
149
+ pixels[dest] = pixels[dest - width];
150
+ dest++;
151
+ }
152
+ }
153
+
154
+ // Two background runs in a row mean a single foreground pixel between them.
155
+ insertForegroundPixel = true;
156
+ continue;
157
+ }
158
+
159
+ insertForegroundPixel = false;
160
+
161
+ switch (code) {
162
+ case REGULAR_FG_RUN:
163
+ case MEGA_MEGA_FG_RUN:
164
+ case LITE_SET_FG_FG_RUN:
165
+ case MEGA_MEGA_SET_FG_RUN: {
166
+ let runLength = runLengthOf(code);
167
+ if (code === LITE_SET_FG_FG_RUN || code === MEGA_MEGA_SET_FG_RUN) foreground = readPixel();
168
+
169
+ if (firstLine) {
170
+ while (runLength-- > 0 && dest < pixels.length) pixels[dest++] = foreground;
171
+ } else {
172
+ while (runLength-- > 0 && dest < pixels.length) {
173
+ pixels[dest] = (pixels[dest - width] ^ foreground) >>> 0;
174
+ dest++;
175
+ }
176
+ }
177
+ break;
178
+ }
179
+
180
+ case LITE_DITHERED_RUN:
181
+ case MEGA_MEGA_DITHERED_RUN: {
182
+ let runLength = runLengthOf(code);
183
+ const pixelA = readPixel();
184
+ const pixelB = readPixel();
185
+ while (runLength-- > 0 && dest < pixels.length - 1) {
186
+ pixels[dest++] = pixelA;
187
+ pixels[dest++] = pixelB;
188
+ }
189
+ break;
190
+ }
191
+
192
+ case REGULAR_COLOR_RUN:
193
+ case MEGA_MEGA_COLOR_RUN: {
194
+ let runLength = runLengthOf(code);
195
+ const color = readPixel();
196
+ while (runLength-- > 0 && dest < pixels.length) pixels[dest++] = color;
197
+ break;
198
+ }
199
+
200
+ case REGULAR_FGBG_IMAGE:
201
+ case MEGA_MEGA_FGBG_IMAGE:
202
+ case LITE_SET_FG_FGBG_IMAGE:
203
+ case MEGA_MEGA_SET_FGBG_IMAGE: {
204
+ let runLength = runLengthOf(code);
205
+ if (code === LITE_SET_FG_FGBG_IMAGE || code === MEGA_MEGA_SET_FGBG_IMAGE) {
206
+ foreground = readPixel();
207
+ }
208
+
209
+ while (runLength > 8) {
210
+ writeFgBgImage(src[srcOffset++], 8);
211
+ runLength -= 8;
212
+ }
213
+ if (runLength > 0) writeFgBgImage(src[srcOffset++], runLength);
214
+ break;
215
+ }
216
+
217
+ case REGULAR_COLOR_IMAGE:
218
+ case MEGA_MEGA_COLOR_IMAGE: {
219
+ let runLength = runLengthOf(code);
220
+ while (runLength-- > 0 && dest < pixels.length) pixels[dest++] = readPixel();
221
+ break;
222
+ }
223
+
224
+ case SPECIAL_FGBG_1:
225
+ srcOffset++;
226
+ writeFgBgImage(MASK_SPECIAL_FGBG_1, 8);
227
+ break;
228
+
229
+ case SPECIAL_FGBG_2:
230
+ srcOffset++;
231
+ writeFgBgImage(MASK_SPECIAL_FGBG_2, 8);
232
+ break;
233
+
234
+ case SPECIAL_WHITE:
235
+ srcOffset++;
236
+ pixels[dest++] = white;
237
+ break;
238
+
239
+ case SPECIAL_BLACK:
240
+ srcOffset++;
241
+ pixels[dest++] = 0;
242
+ break;
243
+
244
+ default:
245
+ throw new Error(`RDP: unsupported bitmap compression order 0x${code.toString(16)}`);
246
+ }
247
+ }
248
+
249
+ return pixels;
250
+ }
251
+
252
+ /**
253
+ * Reverse the row order of a decoded rectangle. RDP stores bitmaps bottom-up
254
+ * and a canvas expects top-down, so without this every rectangle is painted
255
+ * upside down.
256
+ */
257
+ export function flipRows(pixels: Uint8Array, width: number) {
258
+ if (width <= 0) return pixels;
259
+ const height = Math.floor(pixels.length / width);
260
+ if (height < 2) return pixels;
261
+
262
+ const flipped = new Uint32Array(pixels.length);
263
+ for (let y = 0; y < height; y++) {
264
+ flipped.set(pixels.subarray(y * width, (y + 1) * width), (height - 1 - y) * width);
265
+ }
266
+ return flipped;
267
+ }
268
+
269
+ /** Read uncompressed bitmap data, which RDP stores bottom-up. */
270
+ export function readUncompressed(src: Uint8Array, width: number, height: number, bytesPerPixel: number) {
271
+ const pixels = new Uint32Array(width * height);
272
+
273
+ for (let y = 0; y < height; y++) {
274
+ // Source row 0 is the bottom row of the rectangle.
275
+ const destRow = (height - 1 - y) * width;
276
+ let offset = y * width * bytesPerPixel;
277
+ for (let x = 0; x < width; x++) {
278
+ let value = 0;
279
+ for (let i = 0; i < bytesPerPixel; i++) value |= src[offset + i] << (i * 8);
280
+ pixels[destRow + x] = value >>> 0;
281
+ offset += bytesPerPixel;
282
+ }
283
+ }
284
+
285
+ return pixels;
286
+ }
287
+
288
+ /** Expand raw pixel values into RGBA for putImageData. */
289
+ export function pixelsToRgba(pixels: Uint8Array, bitsPerPixel: number, palette: Uint32Array) {
290
+ const rgba = new Uint8ClampedArray(pixels.length * 4);
291
+
292
+ for (let i = 0; i < pixels.length; i++) {
293
+ const value = pixels[i];
294
+ let r;
295
+ let g;
296
+ let b;
297
+
298
+ switch (bitsPerPixel) {
299
+ case 15:
300
+ r = ((value >> 10) & 0x1f) * 255 / 31;
301
+ g = ((value >> 5) & 0x1f) * 255 / 31;
302
+ b = (value & 0x1f) * 255 / 31;
303
+ break;
304
+ case 16:
305
+ r = ((value >> 11) & 0x1f) * 255 / 31;
306
+ g = ((value >> 5) & 0x3f) * 255 / 63;
307
+ b = (value & 0x1f) * 255 / 31;
308
+ break;
309
+ case 8: {
310
+ const index = (value & 0xff) * 3;
311
+ r = palette ? palette[index] : value;
312
+ g = palette ? palette[index + 1] : value;
313
+ b = palette ? palette[index + 2] : value;
314
+ break;
315
+ }
316
+ default: // 24 and 32 bits per pixel are both BGR in the low three bytes
317
+ r = (value >> 16) & 0xff;
318
+ g = (value >> 8) & 0xff;
319
+ b = value & 0xff;
320
+ break;
321
+ }
322
+
323
+ const offset = i * 4;
324
+ rgba[offset] = r;
325
+ rgba[offset + 1] = g;
326
+ rgba[offset + 2] = b;
327
+ rgba[offset + 3] = 255;
328
+ }
329
+
330
+ return rgba;
331
+ }
332
+
333
+ /** Decode one TS_BITMAP_DATA rectangle to RGBA. */
334
+ export function decodeBitmapRect(rect: BitmapRect, palette: Uint32Array) {
335
+ const bytesPerPixel = Math.max(1, Math.ceil(rect.bitsPerPixel / 8));
336
+ const pixels = rect.compressed
337
+ // Compressed data has to be expanded in wire order, because its runs delta
338
+ // against the row above, so the flip to top-down happens afterwards.
339
+ ? flipRows(rleDecompress(rect.data, rect.width, rect.height, bytesPerPixel), rect.width)
340
+ : readUncompressed(rect.data, rect.width, rect.height, bytesPerPixel);
341
+
342
+ return pixelsToRgba(pixels, rect.bitsPerPixel, palette);
343
+ }
@@ -0,0 +1,184 @@
1
+ // RDP Standard Security layer (MS-RDPBCGR 2.2.8.1.1.2, 5.3).
2
+ //
3
+ // When the host negotiates TLS this layer stays inert: the host reports no
4
+ // encryption method and the transport is already protected. With the RDP
5
+ // security layer selected it owns the per-PDU MAC, the RC4 streams in both
6
+ // directions, and the key rotation that RDP requires every 4096 PDUs.
7
+
8
+ import { Reader, Writer, concatBytes } from './buffer';
9
+ import {
10
+ ENCRYPTION_METHOD,
11
+ deriveSessionKeys,
12
+ macSignature,
13
+ rc4Crypt,
14
+ rc4Init,
15
+ updateSessionKey,
16
+ } from './crypto';
17
+ import { ENCRYPTION_LEVEL } from './gcc';
18
+
19
+ export const SEC_FLAG = {
20
+ EXCHANGE_PKT: 0x0001,
21
+ TRANSPORT_REQ: 0x0002,
22
+ ENCRYPT: 0x0008,
23
+ RESET_SEQNO: 0x0010,
24
+ IGNORE_SEQNO: 0x0020,
25
+ INFO_PKT: 0x0040,
26
+ LICENSE_PKT: 0x0080,
27
+ LICENSE_ENCRYPT_CS: 0x0200,
28
+ REDIRECTION_PKT: 0x0400,
29
+ SECURE_CHECKSUM: 0x0800,
30
+ AUTODETECT_REQ: 0x1000,
31
+ AUTODETECT_RSP: 0x2000,
32
+ HEARTBEAT: 0x4000,
33
+ };
34
+
35
+ const KEY_ROTATION_INTERVAL = 4096;
36
+
37
+ export class SecurityLayer {
38
+ private encryptionMethod: number;
39
+ private encryptionLevel: number;
40
+ private keys: SessionKeys | null;
41
+ private encryptState: Rc4State | null;
42
+ private decryptState: Rc4State | null;
43
+ private encryptedPdus: number;
44
+ private decryptedPdus: number;
45
+
46
+ constructor() {
47
+ this.encryptionMethod = ENCRYPTION_METHOD.NONE;
48
+ this.encryptionLevel = ENCRYPTION_LEVEL.NONE;
49
+ this.keys = null;
50
+ this.encryptState = null;
51
+ this.decryptState = null;
52
+ this.encryptedPdus = 0;
53
+ this.decryptedPdus = 0;
54
+ }
55
+
56
+ /** True when data PDUs carry a basic security header at all. */
57
+ get headerPresent() {
58
+ return this.encryptionLevel !== ENCRYPTION_LEVEL.NONE || this.encryptionMethod !== ENCRYPTION_METHOD.NONE;
59
+ }
60
+
61
+ get encryptionEnabled() {
62
+ return this.encryptionMethod !== ENCRYPTION_METHOD.NONE && this.keys !== null;
63
+ }
64
+
65
+ configure(encryptionMethod: number, encryptionLevel: number, clientRandom: Uint8Array, serverRandom: Uint8Array) {
66
+ this.encryptionMethod = encryptionMethod;
67
+ this.encryptionLevel = encryptionLevel;
68
+
69
+ if (encryptionMethod === ENCRYPTION_METHOD.NONE) return;
70
+ if (encryptionMethod & ENCRYPTION_METHOD.FIPS) {
71
+ throw new Error('RDP: this host requires FIPS encryption, which this client does not implement');
72
+ }
73
+
74
+ this.keys = deriveSessionKeys(clientRandom, serverRandom, encryptionMethod);
75
+ this.encryptState = rc4Init(this.keys.encryptKey);
76
+ this.decryptState = rc4Init(this.keys.decryptKey);
77
+ }
78
+
79
+ #rotateEncryptKey() {
80
+ this.keys.encryptKey = updateSessionKey(
81
+ this.keys.initialEncryptKey,
82
+ this.keys.encryptKey,
83
+ this.keys.keyLength,
84
+ this.keys.is40Bit
85
+ );
86
+ this.encryptState = rc4Init(this.keys.encryptKey);
87
+ this.encryptedPdus = 0;
88
+ }
89
+
90
+ #rotateDecryptKey() {
91
+ this.keys.decryptKey = updateSessionKey(
92
+ this.keys.initialDecryptKey,
93
+ this.keys.decryptKey,
94
+ this.keys.keyLength,
95
+ this.keys.is40Bit
96
+ );
97
+ this.decryptState = rc4Init(this.keys.decryptKey);
98
+ this.decryptedPdus = 0;
99
+ }
100
+
101
+ /**
102
+ * Prefix a payload with the basic security header, signing and encrypting it
103
+ * when the negotiated level asks for that.
104
+ *
105
+ * `flags` carries the PDU-kind bits (SEC_INFO_PKT and friends). A payload with
106
+ * no flags and no encryption needs no header at all.
107
+ */
108
+ wrap(payload: Uint8Array, flags = 0) {
109
+ const encrypt = this.encryptionEnabled;
110
+ if (!encrypt && flags === 0 && !this.headerPresent) return payload;
111
+
112
+ const writer = new Writer(payload.length + 16);
113
+ writer.u16le(flags | (encrypt ? SEC_FLAG.ENCRYPT : 0));
114
+ writer.u16le(0); // flagsHi, reserved
115
+
116
+ if (!encrypt) {
117
+ writer.raw(payload);
118
+ return writer.take();
119
+ }
120
+
121
+ // The MAC covers the plaintext, and is sent alongside the ciphertext.
122
+ writer.raw(macSignature(this.keys.macKey, payload));
123
+ writer.raw(rc4Crypt(this.encryptState, payload));
124
+
125
+ if (++this.encryptedPdus === KEY_ROTATION_INTERVAL) this.#rotateEncryptKey();
126
+ return writer.take();
127
+ }
128
+
129
+ /**
130
+ * Strip (and if needed decrypt) the basic security header from a host PDU.
131
+ * `headerMandatory` forces the header to be read even when the host reported
132
+ * no encryption, which licensing PDUs require.
133
+ */
134
+ unwrap(bytes: Uint8Array, headerMandatory = false) {
135
+ if (!this.headerPresent && !headerMandatory) return { flags: 0, payload: bytes };
136
+ if (bytes.length < 4) return { flags: 0, payload: bytes };
137
+
138
+ const reader = new Reader(bytes);
139
+ const flags = reader.u16le();
140
+ reader.u16le(); // flagsHi
141
+
142
+ if (!(flags & SEC_FLAG.ENCRYPT)) {
143
+ return { flags, payload: reader.rest() };
144
+ }
145
+ if (!this.encryptionEnabled) {
146
+ throw new Error('RDP: the host encrypted a PDU before the session keys existed');
147
+ }
148
+
149
+ reader.skip(8); // dataSignature -- verified implicitly by the stream decrypting
150
+ const payload = rc4Crypt(this.decryptState, reader.rest());
151
+
152
+ if (++this.decryptedPdus === KEY_ROTATION_INTERVAL) this.#rotateDecryptKey();
153
+ return { flags, payload };
154
+ }
155
+
156
+ /**
157
+ * Fast-path output carries the same signature and RC4 stream as a slow-path
158
+ * PDU, but without the basic security header in front of it.
159
+ */
160
+ decryptFastPath(payload: Uint8Array) {
161
+ if (!this.encryptionEnabled) return payload;
162
+ if (payload.length <= 8) return payload;
163
+
164
+ const decrypted = rc4Crypt(this.decryptState, payload.subarray(8));
165
+ if (++this.decryptedPdus === KEY_ROTATION_INTERVAL) this.#rotateDecryptKey();
166
+ return decrypted;
167
+ }
168
+
169
+ /**
170
+ * Security Exchange PDU (MS-RDPBCGR 2.2.1.10): the client random encrypted
171
+ * with the host public key, followed by eight bytes of padding.
172
+ */
173
+ static securityExchange(encryptedClientRandom) {
174
+ const writer = new Writer(encryptedClientRandom.length + 16);
175
+ writer.u16le(SEC_FLAG.EXCHANGE_PKT);
176
+ writer.u16le(0);
177
+ writer.u32le(encryptedClientRandom.length + 8);
178
+ writer.raw(encryptedClientRandom);
179
+ writer.zeros(8);
180
+ return writer.take();
181
+ }
182
+ }
183
+
184
+ export { concatBytes };