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,380 @@
1
+ // NTLMv2 (MS-NLMP), the authentication CredSSP carries for a local Windows
2
+ // account. MD5, RC4 and the HMAC construction already exist for RDP Standard
3
+ // Security; only MD4 and the NTLM message formats are new here.
4
+
5
+ import { Writer, concatBytes } from './buffer';
6
+ import { md5, rc4Crypt, rc4Init } from './crypto';
7
+
8
+ // --- MD4 (RFC 1320), used only to hash the password ------------------------
9
+
10
+ function rotl32(value, count: number) {
11
+ return (value << count) | (value >>> (32 - count));
12
+ }
13
+
14
+ export function md4(message: string) {
15
+ const blockCount = Math.floor((message.length + 8) / 64) + 1;
16
+ const padded = new Uint8Array(blockCount * 64);
17
+ padded.set(message);
18
+ padded[message.length] = 0x80;
19
+
20
+ const view = new DataView(padded.buffer);
21
+ const bitLength = message.length * 8;
22
+ view.setUint32(padded.length - 8, bitLength >>> 0, true);
23
+ view.setUint32(padded.length - 4, Math.floor(bitLength / 4294967296), true);
24
+
25
+ let a0 = 0x67452301, b0 = 0xefcdab89, c0 = 0x98badcfe, d0 = 0x10325476;
26
+ const x = new Uint32Array(16);
27
+
28
+ const round1Shifts = [3, 7, 11, 19];
29
+ const round2Order = [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15];
30
+ const round2Shifts = [3, 5, 9, 13];
31
+ const round3Order = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15];
32
+ const round3Shifts = [3, 9, 11, 15];
33
+
34
+ for (let block = 0; block < blockCount; block++) {
35
+ for (let i = 0; i < 16; i++) x[i] = view.getUint32(block * 64 + i * 4, true);
36
+
37
+ let a = a0, b = b0, c = c0, d = d0;
38
+ const step = (value, shift) => rotl32(value | 0, shift);
39
+
40
+ for (let i = 0; i < 16; i++) {
41
+ const f = (b & c) | (~b & d);
42
+ const shift = round1Shifts[i % 4];
43
+ const next = step((a + f + x[i]) | 0, shift);
44
+ a = d; d = c; c = b; b = next;
45
+ }
46
+ for (let i = 0; i < 16; i++) {
47
+ const g = (b & c) | (b & d) | (c & d);
48
+ const shift = round2Shifts[i % 4];
49
+ const next = step((a + g + x[round2Order[i]] + 0x5a827999) | 0, shift);
50
+ a = d; d = c; c = b; b = next;
51
+ }
52
+ for (let i = 0; i < 16; i++) {
53
+ const h = b ^ c ^ d;
54
+ const shift = round3Shifts[i % 4];
55
+ const next = step((a + h + x[round3Order[i]] + 0x6ed9eba1) | 0, shift);
56
+ a = d; d = c; c = b; b = next;
57
+ }
58
+
59
+ a0 = (a0 + a) | 0;
60
+ b0 = (b0 + b) | 0;
61
+ c0 = (c0 + c) | 0;
62
+ d0 = (d0 + d) | 0;
63
+ }
64
+
65
+ const digest = new Uint8Array(16);
66
+ const out = new DataView(digest.buffer);
67
+ out.setUint32(0, a0 >>> 0, true);
68
+ out.setUint32(4, b0 >>> 0, true);
69
+ out.setUint32(8, c0 >>> 0, true);
70
+ out.setUint32(12, d0 >>> 0, true);
71
+ return digest;
72
+ }
73
+
74
+ /** HMAC-MD5. WebCrypto has no MD5, so it is built from the MD5 here. */
75
+ export function hmacMd5(key: Uint8Array, message: string) {
76
+ const blockSize = 64;
77
+ const normalized = key.length > blockSize ? md5(key) : key;
78
+ const padded = new Uint8Array(blockSize);
79
+ padded.set(normalized);
80
+
81
+ const inner = new Uint8Array(blockSize);
82
+ const outer = new Uint8Array(blockSize);
83
+ for (let i = 0; i < blockSize; i++) {
84
+ inner[i] = padded[i] ^ 0x36;
85
+ outer[i] = padded[i] ^ 0x5c;
86
+ }
87
+
88
+ return md5(concatBytes(outer, md5(concatBytes(inner, message))));
89
+ }
90
+
91
+ // --- NTLM messages ----------------------------------------------------------
92
+
93
+ const NTLMSSP_SIGNATURE = new Uint8Array([0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00]);
94
+
95
+ export const NTLM_FLAG = {
96
+ UNICODE: 0x00000001,
97
+ REQUEST_TARGET: 0x00000004,
98
+ SIGN: 0x00000010,
99
+ SEAL: 0x00000020,
100
+ NTLM: 0x00000200,
101
+ ALWAYS_SIGN: 0x00008000,
102
+ EXTENDED_SESSION_SECURITY: 0x00080000,
103
+ TARGET_INFO: 0x00800000,
104
+ VERSION: 0x02000000,
105
+ KEY_128: 0x20000000,
106
+ KEY_EXCH: 0x40000000,
107
+ KEY_56: 0x80000000,
108
+ };
109
+
110
+ // CredSSP needs SEAL and KEY_EXCH: the sealing keys are what protect the
111
+ // public-key binding and the credentials.
112
+ const CLIENT_FLAGS =
113
+ NTLM_FLAG.UNICODE |
114
+ NTLM_FLAG.REQUEST_TARGET |
115
+ NTLM_FLAG.SIGN |
116
+ NTLM_FLAG.SEAL |
117
+ NTLM_FLAG.NTLM |
118
+ NTLM_FLAG.ALWAYS_SIGN |
119
+ NTLM_FLAG.EXTENDED_SESSION_SECURITY |
120
+ NTLM_FLAG.TARGET_INFO |
121
+ NTLM_FLAG.VERSION |
122
+ NTLM_FLAG.KEY_128 |
123
+ NTLM_FLAG.KEY_EXCH |
124
+ NTLM_FLAG.KEY_56;
125
+
126
+ const MSV_AV_EOL = 0x0000;
127
+ const MSV_AV_TIMESTAMP = 0x0007;
128
+ const MSV_AV_FLAGS = 0x0006;
129
+ const MSV_AV_FLAGS_MIC = 0x00000002;
130
+
131
+ const utf16 = (text: string) => {
132
+ const writer = new Writer(text.length * 2 + 2);
133
+ writer.utf16le(text);
134
+ return writer.take();
135
+ };
136
+
137
+ /** Windows 10 build 19041, which is what a current client reports. */
138
+ const VERSION_FIELD = new Uint8Array([10, 0, 0x63, 0x45, 0x00, 0x00, 0x00, 0x0f]);
139
+
140
+ export function negotiateMessage() {
141
+ const writer = new Writer(40);
142
+ writer.raw(NTLMSSP_SIGNATURE);
143
+ writer.u32le(1); // MessageType: NEGOTIATE
144
+ writer.u32le(CLIENT_FLAGS >>> 0);
145
+ writer.u16le(0).u16le(0).u32le(0); // DomainNameFields: absent
146
+ writer.u16le(0).u16le(0).u32le(0); // WorkstationFields: absent
147
+ writer.raw(VERSION_FIELD);
148
+ return writer.take();
149
+ }
150
+
151
+ export function parseChallengeMessage(bytes: Uint8Array) {
152
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
153
+ if (view.getUint32(8, true) !== 2) {
154
+ throw new Error('NTLM: the host did not answer with a challenge message');
155
+ }
156
+
157
+ const targetNameLength = view.getUint16(12, true);
158
+ const targetNameOffset = view.getUint32(16, true);
159
+ const flags = view.getUint32(20, true);
160
+ const serverChallenge = bytes.subarray(24, 32).slice();
161
+ const targetInfoLength = view.getUint16(40, true);
162
+ const targetInfoOffset = view.getUint32(44, true);
163
+
164
+ return {
165
+ flags,
166
+ serverChallenge,
167
+ targetName: bytes.subarray(targetNameOffset, targetNameOffset + targetNameLength).slice(),
168
+ targetInfo: bytes.subarray(targetInfoOffset, targetInfoOffset + targetInfoLength).slice(),
169
+ };
170
+ }
171
+
172
+ /** Walk the AV pair list the host sent, so its timestamp can be reused. */
173
+ function readAvPairs(targetInfo) {
174
+ const pairs = [];
175
+ let offset = 0;
176
+ const view = new DataView(targetInfo.buffer, targetInfo.byteOffset, targetInfo.byteLength);
177
+
178
+ while (offset + 4 <= targetInfo.length) {
179
+ const id = view.getUint16(offset, true);
180
+ const length = view.getUint16(offset + 2, true);
181
+ const value = targetInfo.subarray(offset + 4, offset + 4 + length);
182
+ offset += 4 + length;
183
+ if (id === MSV_AV_EOL) break;
184
+ pairs.push({ id, value });
185
+ }
186
+
187
+ return pairs;
188
+ }
189
+
190
+ function writeAvPairs(pairs) {
191
+ const writer = new Writer(256);
192
+ for (const pair of pairs) {
193
+ writer.u16le(pair.id);
194
+ writer.u16le(pair.value.length);
195
+ writer.raw(pair.value);
196
+ }
197
+ writer.u16le(MSV_AV_EOL).u16le(0);
198
+ return writer.take();
199
+ }
200
+
201
+ /** Windows FILETIME: 100ns ticks since 1601. */
202
+ function fileTimeNow() {
203
+ const ticks = (BigInt(Date.now()) + 11644473600000n) * 10000n;
204
+ const out = new Uint8Array(8);
205
+ new DataView(out.buffer).setBigUint64(0, ticks, true);
206
+ return out;
207
+ }
208
+
209
+ /**
210
+ * Build the AUTHENTICATE message plus every key CredSSP needs afterwards.
211
+ *
212
+ * The MIC is always sent: a current Windows host expects one, and sending it
213
+ * obliges the client to flag it in the AV pair list, which in turn feeds the
214
+ * NTProofStr -- so the modified list has to be the one that goes on the wire.
215
+ */
216
+ export function authenticateMessage({ username, domain, password, challenge, negotiateBytes, challengeBytes }) {
217
+ const responseKey = hmacMd5(
218
+ md4(utf16(password)),
219
+ utf16(username.toUpperCase() + domain)
220
+ );
221
+
222
+ const pairs = readAvPairs(challenge.targetInfo);
223
+ const timestamp = pairs.find((pair) => pair.id === MSV_AV_TIMESTAMP)?.value.slice() || fileTimeNow();
224
+
225
+ const flagsPair = pairs.find((pair) => pair.id === MSV_AV_FLAGS);
226
+ if (flagsPair) {
227
+ const view = new DataView(flagsPair.value.buffer, flagsPair.value.byteOffset, 4);
228
+ view.setUint32(0, view.getUint32(0, true) | MSV_AV_FLAGS_MIC, true);
229
+ } else {
230
+ const value = new Uint8Array(4);
231
+ new DataView(value.buffer).setUint32(0, MSV_AV_FLAGS_MIC, true);
232
+ pairs.push({ id: MSV_AV_FLAGS, value });
233
+ }
234
+ const targetInfo = writeAvPairs(pairs);
235
+
236
+ const clientChallenge = new Uint8Array(8);
237
+ crypto.getRandomValues(clientChallenge);
238
+
239
+ const temp = concatBytes(
240
+ new Uint8Array([0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), // version, reserved
241
+ timestamp,
242
+ clientChallenge,
243
+ new Uint8Array(4),
244
+ targetInfo,
245
+ new Uint8Array(4)
246
+ );
247
+
248
+ const proof = hmacMd5(responseKey, concatBytes(challenge.serverChallenge, temp));
249
+ const ntResponse = concatBytes(proof, temp);
250
+ const sessionBaseKey = hmacMd5(responseKey, proof);
251
+
252
+ // With KEY_EXCH the client picks the session key and sends it sealed.
253
+ const exportedSessionKey = new Uint8Array(16);
254
+ crypto.getRandomValues(exportedSessionKey);
255
+ const encryptedSessionKey = rc4Crypt(rc4Init(sessionBaseKey), exportedSessionKey);
256
+
257
+ const domainBytes = utf16Trimmed(domain);
258
+ const userBytes = utf16Trimmed(username);
259
+ const workstationBytes = utf16Trimmed('');
260
+ const lmResponse = new Uint8Array(24);
261
+
262
+ const headerLength = 72;
263
+ let offset = headerLength;
264
+ const layout = [];
265
+ const place = (bytes: Uint8Array) => {
266
+ layout.push({ bytes, offset });
267
+ offset += bytes.length;
268
+ return layout[layout.length - 1];
269
+ };
270
+
271
+ const lm = place(lmResponse);
272
+ const nt = place(ntResponse);
273
+ const dom = place(domainBytes);
274
+ const user = place(userBytes);
275
+ const workstation = place(workstationBytes);
276
+ const sessionKey = place(encryptedSessionKey);
277
+
278
+ const writer = new Writer(offset + 16);
279
+ writer.raw(NTLMSSP_SIGNATURE);
280
+ writer.u32le(3); // MessageType: AUTHENTICATE
281
+ const field = (entry) => {
282
+ writer.u16le(entry.bytes.length);
283
+ writer.u16le(entry.bytes.length);
284
+ writer.u32le(entry.offset + 16); // the MIC sits between header and payload
285
+ };
286
+ field(lm);
287
+ field(nt);
288
+ field(dom);
289
+ field(user);
290
+ field(workstation);
291
+ field(sessionKey);
292
+ writer.u32le(CLIENT_FLAGS >>> 0);
293
+ writer.raw(VERSION_FIELD);
294
+ writer.zeros(16); // MIC, filled in below
295
+ for (const entry of layout) writer.raw(entry.bytes);
296
+
297
+ const message = writer.take();
298
+ const mic = hmacMd5(
299
+ exportedSessionKey,
300
+ concatBytes(negotiateBytes, challengeBytes, message)
301
+ );
302
+ message.set(mic, headerLength);
303
+
304
+ return {
305
+ message,
306
+ exportedSessionKey,
307
+ keys: sessionKeys(exportedSessionKey),
308
+ };
309
+ }
310
+
311
+ function utf16Trimmed(text: string) {
312
+ if (!text) return new Uint8Array(0);
313
+ const bytes = utf16(text);
314
+ // utf16le() appends no terminator, but Writer sizing may leave slack.
315
+ return bytes.subarray(0, text.length * 2).slice();
316
+ }
317
+
318
+ const CLIENT_SIGNING = 'session key to client-to-server signing key magic constant\0';
319
+ const SERVER_SIGNING = 'session key to server-to-client signing key magic constant\0';
320
+ const CLIENT_SEALING = 'session key to client-to-server sealing key magic constant\0';
321
+ const SERVER_SEALING = 'session key to server-to-client sealing key magic constant\0';
322
+
323
+ function sessionKeys(exportedSessionKey) {
324
+ const derive = (label: string) => md5(concatBytes(exportedSessionKey, new TextEncoder().encode(label)));
325
+
326
+ return {
327
+ clientSigningKey: derive(CLIENT_SIGNING),
328
+ serverSigningKey: derive(SERVER_SIGNING),
329
+ clientSealing: rc4Init(derive(CLIENT_SEALING)),
330
+ serverSealing: rc4Init(derive(SERVER_SEALING)),
331
+ };
332
+ }
333
+
334
+ /**
335
+ * NTLM message confidentiality (MS-NLMP 3.4.3). The signature covers the
336
+ * plaintext, and both the payload and the checksum draw from the same RC4
337
+ * stream -- payload first.
338
+ */
339
+ export class NtlmSealer {
340
+ private readonly keys: NtlmSessionKeys;
341
+ private sendSequence: number;
342
+ private receiveSequence: number;
343
+
344
+ constructor(keys) {
345
+ this.keys = keys;
346
+ this.sendSequence = 0;
347
+ this.receiveSequence = 0;
348
+ }
349
+
350
+ seal(plaintext: Uint8Array) {
351
+ const sequence = new Uint8Array(4);
352
+ new DataView(sequence.buffer).setUint32(0, this.sendSequence, true);
353
+
354
+ const digest = hmacMd5(this.keys.clientSigningKey, concatBytes(sequence, plaintext));
355
+ const sealed = rc4Crypt(this.keys.clientSealing, plaintext);
356
+ const checksum = rc4Crypt(this.keys.clientSealing, digest.subarray(0, 8));
357
+
358
+ this.sendSequence += 1;
359
+ return concatBytes(new Uint8Array([1, 0, 0, 0]), checksum, sequence, sealed);
360
+ }
361
+
362
+ unseal(bytes: Uint8Array) {
363
+ if (bytes.length < 16) throw new Error('NTLM: sealed message is too short');
364
+ const sealed = bytes.subarray(16);
365
+ const plaintext = rc4Crypt(this.keys.serverSealing, sealed);
366
+
367
+ // The checksum is verified so a tampered pubKeyAuth is reported as such
368
+ // rather than surfacing later as a confusing protocol error.
369
+ const sequence = bytes.subarray(12, 16);
370
+ const digest = hmacMd5(this.keys.serverSigningKey, concatBytes(sequence, plaintext));
371
+ const expected = rc4Crypt(this.keys.serverSealing, digest.subarray(0, 8));
372
+ const actual = bytes.subarray(4, 12);
373
+ if (!expected.every((byte, i) => byte === actual[i])) {
374
+ throw new Error('NTLM: the host signature did not verify');
375
+ }
376
+
377
+ this.receiveSequence += 1;
378
+ return plaintext;
379
+ }
380
+ }