ai-remote 0.4.2 → 0.4.3

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 (62) hide show
  1. package/dist/index.js +1 -6
  2. package/dist/protocols/index.js +3 -34
  3. package/dist/protocols/ssh/session.js +17 -608
  4. package/dist/protocols/ssh/terminal.js +6 -190
  5. package/dist/shared/connection.js +1 -113
  6. package/dist/shared/hosts.js +1 -132
  7. package/dist/shared/icons.js +1 -41
  8. package/dist/shared/protocol.js +1 -39
  9. package/dist/shared/signals.js +1 -41
  10. package/package.json +1 -1
  11. package/dist/protocols/rdp/buffer.d.ts +0 -38
  12. package/dist/protocols/rdp/buffer.js +0 -169
  13. package/dist/protocols/rdp/caps.d.ts +0 -49
  14. package/dist/protocols/rdp/caps.js +0 -259
  15. package/dist/protocols/rdp/cert.d.ts +0 -18
  16. package/dist/protocols/rdp/cert.js +0 -134
  17. package/dist/protocols/rdp/client.d.ts +0 -56
  18. package/dist/protocols/rdp/client.js +0 -1069
  19. package/dist/protocols/rdp/cliprdr.d.ts +0 -27
  20. package/dist/protocols/rdp/cliprdr.js +0 -239
  21. package/dist/protocols/rdp/credssp.d.ts +0 -34
  22. package/dist/protocols/rdp/credssp.js +0 -253
  23. package/dist/protocols/rdp/crypto.d.ts +0 -63
  24. package/dist/protocols/rdp/crypto.js +0 -368
  25. package/dist/protocols/rdp/display.d.ts +0 -38
  26. package/dist/protocols/rdp/display.js +0 -588
  27. package/dist/protocols/rdp/gcc.d.ts +0 -23
  28. package/dist/protocols/rdp/gcc.js +0 -131
  29. package/dist/protocols/rdp/keymap.d.ts +0 -9
  30. package/dist/protocols/rdp/keymap.js +0 -131
  31. package/dist/protocols/rdp/mcs.d.ts +0 -40
  32. package/dist/protocols/rdp/mcs.js +0 -222
  33. package/dist/protocols/rdp/ntlm.d.ts +0 -61
  34. package/dist/protocols/rdp/ntlm.js +0 -304
  35. package/dist/protocols/rdp/pdu.d.ts +0 -155
  36. package/dist/protocols/rdp/pdu.js +0 -443
  37. package/dist/protocols/rdp/rail.d.ts +0 -119
  38. package/dist/protocols/rdp/rail.js +0 -382
  39. package/dist/protocols/rdp/rle.d.ts +0 -13
  40. package/dist/protocols/rdp/rle.js +0 -275
  41. package/dist/protocols/rdp/sec.d.ts +0 -59
  42. package/dist/protocols/rdp/sec.js +0 -155
  43. package/dist/protocols/rdp/session.d.ts +0 -62
  44. package/dist/protocols/rdp/session.js +0 -306
  45. package/dist/protocols/rdp/tls.d.ts +0 -18
  46. package/dist/protocols/rdp/tls.js +0 -353
  47. package/dist/protocols/rdp/vchannel.d.ts +0 -28
  48. package/dist/protocols/rdp/vchannel.js +0 -58
  49. package/dist/protocols/rdp/x224.d.ts +0 -40
  50. package/dist/protocols/rdp/x224.js +0 -109
  51. package/dist/protocols/ssh/kex.d.ts +0 -97
  52. package/dist/protocols/ssh/kex.js +0 -176
  53. package/dist/protocols/ssh/messages.d.ts +0 -50
  54. package/dist/protocols/ssh/messages.js +0 -54
  55. package/dist/protocols/ssh/transport.d.ts +0 -62
  56. package/dist/protocols/ssh/transport.js +0 -612
  57. package/dist/protocols/ssh/wire.d.ts +0 -52
  58. package/dist/protocols/ssh/wire.js +0 -188
  59. package/dist/protocols/vnc/input.d.ts +0 -5
  60. package/dist/protocols/vnc/input.js +0 -15
  61. package/dist/protocols/vnc/session.d.ts +0 -12
  62. package/dist/protocols/vnc/session.js +0 -258
@@ -1,27 +0,0 @@
1
- /**
2
- * One clipboard channel. Emits:
3
- * 'text' { text } -- the host's clipboard now holds this text
4
- */
5
- export declare class ClipboardChannel extends EventTarget {
6
- #private;
7
- private readonly send;
8
- private readonly log;
9
- private ready;
10
- private longFormatNames;
11
- private localText;
12
- private pendingAck;
13
- /**
14
- * @param {(bytes: Uint8Array) => void} send delivers one complete channel message
15
- * @param {(step: string, detail?: unknown) => void} [log]
16
- */
17
- constructor({ send, log }: {
18
- log: any;
19
- send: any;
20
- });
21
- /**
22
- * Announce that this side now owns the clipboard and holds `text`. Resolves
23
- * once the host has acknowledged, so a paste keystroke can safely follow.
24
- */
25
- setLocalText(text: string): Promise<unknown>;
26
- receive(bytes: Uint8Array): void;
27
- }
@@ -1,239 +0,0 @@
1
- import { Reader, Writer, concatBytes } from "./buffer.js";
2
- const MSG = {
3
- MONITOR_READY: 1,
4
- FORMAT_LIST: 2,
5
- FORMAT_LIST_RESPONSE: 3,
6
- FORMAT_DATA_REQUEST: 4,
7
- FORMAT_DATA_RESPONSE: 5,
8
- CLIP_CAPS: 7
9
- };
10
- const RESPONSE_OK = 1;
11
- const RESPONSE_FAIL = 2;
12
- const ASCII_NAMES = 4;
13
- const CF_TEXT = 1;
14
- const CF_UNICODETEXT = 13;
15
- const CAPSTYPE_GENERAL = 1;
16
- const CAPS_VERSION_2 = 2;
17
- const USE_LONG_FORMAT_NAMES = 2;
18
- const SHORT_FORMAT_NAME_BYTES = 32;
19
- const FORMAT_LIST_ACK_TIMEOUT_MS = 400;
20
- function cliprdrPdu(msgType, msgFlags, body = new Uint8Array(0)) {
21
- const writer = new Writer(body.length + 8);
22
- writer.u16le(msgType).u16le(msgFlags).u32le(body.length).raw(body);
23
- return writer.take();
24
- }
25
- function encodeUnicodeText(text) {
26
- const writer = new Writer(text.length * 2 + 2);
27
- writer.utf16le(text);
28
- writer.u16le(0);
29
- return writer.take();
30
- }
31
- function decodeUnicodeText(bytes) {
32
- const units = new Uint16Array(bytes.length >> 1);
33
- for (let i = 0; i < units.length; i++) {
34
- units[i] = bytes[i * 2] | bytes[i * 2 + 1] << 8;
35
- }
36
- let end = units.indexOf(0);
37
- if (end === -1) end = units.length;
38
- let text = "";
39
- for (let i = 0; i < end; i += 4096) {
40
- text += String.fromCharCode(...units.subarray(i, Math.min(i + 4096, end)));
41
- }
42
- return text;
43
- }
44
- function encodeAsciiText(text) {
45
- const writer = new Writer(text.length + 1);
46
- for (const character of text) {
47
- const code = character.codePointAt(0);
48
- writer.u8(code > 255 ? 63 : code);
49
- }
50
- writer.u8(0);
51
- return writer.take();
52
- }
53
- function decodeAsciiText(bytes) {
54
- let end = bytes.indexOf(0);
55
- if (end === -1) end = bytes.length;
56
- return String.fromCharCode(...bytes.subarray(0, end));
57
- }
58
- class ClipboardChannel extends EventTarget {
59
- send;
60
- log;
61
- ready;
62
- longFormatNames;
63
- localText;
64
- pendingAck;
65
- /**
66
- * @param {(bytes: Uint8Array) => void} send delivers one complete channel message
67
- * @param {(step: string, detail?: unknown) => void} [log]
68
- */
69
- constructor({ send, log }) {
70
- super();
71
- this.send = send;
72
- this.log = log || (() => {
73
- });
74
- this.ready = false;
75
- this.longFormatNames = true;
76
- this.localText = "";
77
- this.pendingAck = null;
78
- }
79
- #sendPdu(msgType, msgFlags, body) {
80
- this.send(cliprdrPdu(msgType, msgFlags, body));
81
- }
82
- // --- outbound ------------------------------------------------------------
83
- /**
84
- * Announce that this side now owns the clipboard and holds `text`. Resolves
85
- * once the host has acknowledged, so a paste keystroke can safely follow.
86
- */
87
- setLocalText(text) {
88
- this.localText = text;
89
- if (!this.ready) return Promise.resolve(false);
90
- this.#sendPdu(MSG.FORMAT_LIST, 0, this.#formatListBody(
91
- text ? [CF_UNICODETEXT, CF_TEXT] : []
92
- ));
93
- if (this.pendingAck) this.pendingAck.resolve(false);
94
- return new Promise((resolve) => {
95
- let timer = 0;
96
- const settle = (acknowledged) => {
97
- clearTimeout(timer);
98
- if (this.pendingAck?.resolve === settle) this.pendingAck = null;
99
- resolve(acknowledged);
100
- };
101
- timer = setTimeout(() => {
102
- this.log("clipboard format list was not acknowledged");
103
- settle(false);
104
- }, FORMAT_LIST_ACK_TIMEOUT_MS);
105
- this.pendingAck = { resolve: settle };
106
- });
107
- }
108
- #formatListBody(formatIds) {
109
- const parts = formatIds.map((formatId) => {
110
- const writer = new Writer(this.longFormatNames ? 6 : 4 + SHORT_FORMAT_NAME_BYTES);
111
- writer.u32le(formatId);
112
- if (this.longFormatNames) writer.u16le(0);
113
- else writer.zeros(SHORT_FORMAT_NAME_BYTES);
114
- return writer.take();
115
- });
116
- return concatBytes(...parts);
117
- }
118
- // --- inbound -------------------------------------------------------------
119
- receive(bytes) {
120
- if (bytes.length < 8) return;
121
- const reader = new Reader(bytes);
122
- const msgType = reader.u16le();
123
- const msgFlags = reader.u16le();
124
- const dataLength = reader.u32le();
125
- const rest = reader.rest();
126
- const body = rest.subarray(0, Math.min(dataLength, rest.length));
127
- switch (msgType) {
128
- case MSG.CLIP_CAPS:
129
- this.#handleCapabilities(body);
130
- return;
131
- case MSG.MONITOR_READY:
132
- this.#handleMonitorReady();
133
- return;
134
- case MSG.FORMAT_LIST:
135
- this.#handleFormatList(body, msgFlags);
136
- return;
137
- case MSG.FORMAT_LIST_RESPONSE:
138
- this.pendingAck?.resolve((msgFlags & RESPONSE_FAIL) === 0);
139
- return;
140
- case MSG.FORMAT_DATA_REQUEST:
141
- this.#handleFormatDataRequest(body);
142
- return;
143
- case MSG.FORMAT_DATA_RESPONSE:
144
- this.#handleFormatDataResponse(body, msgFlags);
145
- return;
146
- default:
147
- return;
148
- }
149
- }
150
- #handleCapabilities(body) {
151
- const reader = new Reader(body);
152
- if (reader.remaining < 4) return;
153
- const setCount = reader.u16le();
154
- reader.u16le();
155
- for (let i = 0; i < setCount && reader.remaining >= 4; i++) {
156
- const type = reader.u16le();
157
- const length = reader.u16le();
158
- if (length < 4 || length - 4 > reader.remaining) break;
159
- const set = new Reader(reader.raw(length - 4));
160
- if (type === CAPSTYPE_GENERAL && set.remaining >= 8) {
161
- set.u32le();
162
- const flags = set.u32le();
163
- this.longFormatNames = (flags & USE_LONG_FORMAT_NAMES) !== 0;
164
- }
165
- }
166
- }
167
- /**
168
- * The host is ready. The client answers with its own capabilities and an
169
- * initial format list, which MS-RDPECLIP 1.3.2.1 requires even when there is
170
- * nothing to offer yet.
171
- */
172
- #handleMonitorReady() {
173
- const general = new Writer(12);
174
- general.u16le(CAPSTYPE_GENERAL).u16le(12);
175
- general.u32le(CAPS_VERSION_2);
176
- general.u32le(this.longFormatNames ? USE_LONG_FORMAT_NAMES : 0);
177
- const caps = new Writer(20);
178
- caps.u16le(1).u16le(0);
179
- caps.raw(general.take());
180
- this.#sendPdu(MSG.CLIP_CAPS, 0, caps.take());
181
- this.ready = true;
182
- this.#sendPdu(MSG.FORMAT_LIST, 0, this.#formatListBody(
183
- this.localText ? [CF_UNICODETEXT, CF_TEXT] : []
184
- ));
185
- this.log("clipboard channel ready", { longFormatNames: this.longFormatNames });
186
- }
187
- #handleFormatList(body, msgFlags) {
188
- const formats = this.#parseFormatList(body, msgFlags);
189
- this.#sendPdu(MSG.FORMAT_LIST_RESPONSE, RESPONSE_OK);
190
- const wanted = formats.includes(CF_UNICODETEXT) ? CF_UNICODETEXT : formats.includes(CF_TEXT) ? CF_TEXT : null;
191
- if (wanted === null) {
192
- this.log("host clipboard holds no text format", { formats });
193
- return;
194
- }
195
- const request = new Writer(4);
196
- request.u32le(wanted);
197
- this.#sendPdu(MSG.FORMAT_DATA_REQUEST, 0, request.take());
198
- }
199
- #parseFormatList(body, msgFlags) {
200
- const reader = new Reader(body);
201
- const formats = [];
202
- const short = !this.longFormatNames || (msgFlags & ASCII_NAMES) !== 0;
203
- while (reader.remaining >= 4) {
204
- formats.push(reader.u32le());
205
- if (short) {
206
- if (reader.remaining < SHORT_FORMAT_NAME_BYTES) break;
207
- reader.skip(SHORT_FORMAT_NAME_BYTES);
208
- continue;
209
- }
210
- while (reader.remaining >= 2 && reader.u16le() !== 0) {
211
- }
212
- }
213
- return formats;
214
- }
215
- #handleFormatDataRequest(body) {
216
- const reader = new Reader(body);
217
- const formatId = reader.remaining >= 4 ? reader.u32le() : 0;
218
- if (formatId !== CF_UNICODETEXT && formatId !== CF_TEXT) {
219
- this.#sendPdu(MSG.FORMAT_DATA_RESPONSE, RESPONSE_FAIL);
220
- return;
221
- }
222
- const data = formatId === CF_UNICODETEXT ? encodeUnicodeText(this.localText) : encodeAsciiText(this.localText);
223
- this.#sendPdu(MSG.FORMAT_DATA_RESPONSE, RESPONSE_OK, data);
224
- this.log("handed the host our clipboard", { formatId, characters: this.localText.length });
225
- }
226
- #handleFormatDataResponse(body, msgFlags) {
227
- if (msgFlags & RESPONSE_FAIL) {
228
- this.log("the host refused to hand over its clipboard");
229
- return;
230
- }
231
- const looksUnicode = body.length >= 2 && body.length % 2 === 0 && body[1] === 0;
232
- const text = looksUnicode ? decodeUnicodeText(body) : decodeAsciiText(body);
233
- if (!text) return;
234
- this.dispatchEvent(new CustomEvent("text", { detail: { text } }));
235
- }
236
- }
237
- export {
238
- ClipboardChannel
239
- };
@@ -1,34 +0,0 @@
1
- export declare class CredSspClient {
2
- #private;
3
- private readonly username;
4
- private readonly domain;
5
- private readonly password;
6
- private readonly serverPublicKey;
7
- private readonly send;
8
- private readonly log;
9
- private inbound;
10
- private waiters;
11
- constructor({ username, domain, password, serverPublicKey, send, log }: {
12
- domain: any;
13
- log: any;
14
- password: any;
15
- send: any;
16
- serverPublicKey: any;
17
- username: any;
18
- });
19
- /** Feed decrypted TLS application data. */
20
- receive(bytes: Uint8Array): void;
21
- authenticate(): Promise<{
22
- authenticated: boolean;
23
- reachedChallenge: boolean;
24
- hostVersion: any;
25
- targetName: string;
26
- note: string;
27
- } | {
28
- reachedChallenge?: undefined;
29
- note?: undefined;
30
- authenticated: boolean;
31
- hostVersion: any;
32
- targetName: string;
33
- }>;
34
- }
@@ -1,253 +0,0 @@
1
- import { concatBytes } from "./buffer.js";
2
- import { NtlmSealer, authenticateMessage, negotiateMessage, parseChallengeMessage } from "./ntlm.js";
3
- const CREDSSP_VERSION = 6;
4
- const CLIENT_TO_SERVER_MAGIC = "CredSSP Client-To-Server Binding Hash\0";
5
- const SERVER_TO_CLIENT_MAGIC = "CredSSP Server-To-Client Binding Hash\0";
6
- const NT_STATUS_MESSAGES = {
7
- 3221225581: "The host rejected the username or password (STATUS_LOGON_FAILURE).",
8
- 3221225582: "The account is not allowed to sign in right now (STATUS_ACCOUNT_RESTRICTION).",
9
- 3221225583: "The account is outside its permitted logon hours.",
10
- 3221225584: "The account is not allowed to sign in from this machine (STATUS_INVALID_WORKSTATION).",
11
- 3221225585: "The account password has expired.",
12
- 3221225586: "The account is disabled.",
13
- 3221225819: "The account is not allowed the Remote Desktop logon type -- add it to the Remote Desktop Users group.",
14
- 3221225875: "The account has expired.",
15
- 3221226020: "The account must change its password before signing in.",
16
- 3221226036: "The account is locked out.",
17
- 2148074252: "The host denied the logon (SEC_E_LOGON_DENIED).",
18
- 2148074248: "The host rejected the authentication token (SEC_E_INVALID_TOKEN).",
19
- 2148074242: "The host rejected the CredSSP exchange (SEC_E_UNSUPPORTED_FUNCTION) -- usually a public-key binding mismatch."
20
- };
21
- function derLength(length) {
22
- if (length < 128) return new Uint8Array([length]);
23
- if (length <= 255) return new Uint8Array([129, length]);
24
- if (length <= 65535) return new Uint8Array([130, length >> 8, length & 255]);
25
- return new Uint8Array([131, length >> 16 & 255, length >> 8 & 255, length & 255]);
26
- }
27
- const derTlv = (tag, content) => concatBytes(new Uint8Array([tag]), derLength(content.length), content);
28
- const derSequence = (...parts) => derTlv(48, concatBytes(...parts));
29
- const derOctetString = (bytes) => derTlv(4, bytes);
30
- const derContext = (index, content) => derTlv(160 | index, content);
31
- function derInteger(value) {
32
- const bytes = [];
33
- let remaining = value;
34
- do {
35
- bytes.unshift(remaining & 255);
36
- remaining >>>= 8;
37
- } while (remaining > 0);
38
- if (bytes[0] & 128) bytes.unshift(0);
39
- return derTlv(2, new Uint8Array(bytes));
40
- }
41
- function derRead(bytes, offset) {
42
- const tag = bytes[offset];
43
- let cursor = offset + 1;
44
- let length = bytes[cursor++];
45
- if (length & 128) {
46
- const count = length & 127;
47
- length = 0;
48
- for (let i = 0; i < count; i++) length = length << 8 | bytes[cursor++];
49
- }
50
- return { tag, content: bytes.subarray(cursor, cursor + length), end: cursor + length };
51
- }
52
- function derElementLength(bytes, offset = 0) {
53
- if (bytes.length - offset < 2) return 0;
54
- let cursor = offset + 1;
55
- let length = bytes[cursor++];
56
- if (length & 128) {
57
- const count = length & 127;
58
- if (bytes.length - cursor < count) return 0;
59
- length = 0;
60
- for (let i = 0; i < count; i++) length = length << 8 | bytes[cursor++];
61
- }
62
- const total = cursor - offset + length;
63
- return bytes.length - offset < total ? 0 : total;
64
- }
65
- function encodeTsRequest({ version, negoToken, authInfo, pubKeyAuth, clientNonce }) {
66
- const parts = [derContext(0, derInteger(version))];
67
- if (negoToken) {
68
- parts.push(derContext(1, derSequence(derSequence(derContext(0, derOctetString(negoToken))))));
69
- }
70
- if (authInfo) parts.push(derContext(2, derOctetString(authInfo)));
71
- if (pubKeyAuth) parts.push(derContext(3, derOctetString(pubKeyAuth)));
72
- if (clientNonce) parts.push(derContext(5, derOctetString(clientNonce)));
73
- return derSequence(...parts);
74
- }
75
- function decodeTsRequest(bytes) {
76
- const outer = derRead(bytes, 0);
77
- const result = { version: 0, negoToken: null, authInfo: null, pubKeyAuth: null, errorCode: 0 };
78
- let offset = 0;
79
- while (offset < outer.content.length) {
80
- const field = derRead(outer.content, offset);
81
- offset = field.end;
82
- const index = field.tag & 31;
83
- if (index === 0) {
84
- result.version = derRead(field.content, 0).content[0];
85
- continue;
86
- }
87
- if (index === 1) {
88
- const sequence = derRead(field.content, 0);
89
- const item = derRead(sequence.content, 0);
90
- const token = derRead(item.content, 0);
91
- result.negoToken = derRead(token.content, 0).content;
92
- continue;
93
- }
94
- if (index === 2) {
95
- result.authInfo = derRead(field.content, 0).content;
96
- continue;
97
- }
98
- if (index === 3) {
99
- result.pubKeyAuth = derRead(field.content, 0).content;
100
- continue;
101
- }
102
- if (index === 4) {
103
- const value = derRead(field.content, 0).content;
104
- let code = 0;
105
- for (const byte of value) code = (code << 8 | byte) >>> 0;
106
- result.errorCode = code;
107
- continue;
108
- }
109
- }
110
- return result;
111
- }
112
- function encodeCredentials(domain, username, password) {
113
- const utf16 = (text) => {
114
- const out = new Uint8Array(text.length * 2);
115
- const view = new DataView(out.buffer);
116
- for (let i = 0; i < text.length; i++) view.setUint16(i * 2, text.charCodeAt(i), true);
117
- return out;
118
- };
119
- const passwordCreds = derSequence(
120
- derContext(0, derOctetString(utf16(domain))),
121
- derContext(1, derOctetString(utf16(username))),
122
- derContext(2, derOctetString(utf16(password)))
123
- );
124
- return derSequence(
125
- derContext(0, derInteger(1)),
126
- // credType: password
127
- derContext(1, derOctetString(passwordCreds))
128
- );
129
- }
130
- class CredSspClient {
131
- username;
132
- domain;
133
- password;
134
- serverPublicKey;
135
- send;
136
- log;
137
- inbound;
138
- waiters;
139
- constructor({ username, domain, password, serverPublicKey, send, log }) {
140
- this.username = username || "";
141
- this.domain = domain || "";
142
- this.password = password || "";
143
- this.serverPublicKey = serverPublicKey;
144
- this.send = send;
145
- this.log = log || (() => {
146
- });
147
- this.inbound = new Uint8Array(0);
148
- this.waiters = [];
149
- }
150
- /** Feed decrypted TLS application data. */
151
- receive(bytes) {
152
- this.inbound = this.inbound.length === 0 ? bytes : concatBytes(this.inbound, bytes);
153
- for (; ; ) {
154
- const length = derElementLength(this.inbound);
155
- if (length === 0) return;
156
- const request = decodeTsRequest(this.inbound.subarray(0, length));
157
- this.inbound = this.inbound.subarray(length).slice();
158
- const waiter = this.waiters.shift();
159
- if (waiter) waiter.resolve(request);
160
- }
161
- }
162
- #next(timeoutMs = 15e3) {
163
- return new Promise((resolve, reject) => {
164
- const timer = setTimeout(
165
- () => reject(new Error("CredSSP: the host did not answer within 15s")),
166
- timeoutMs
167
- );
168
- this.waiters.push({
169
- resolve: (value) => {
170
- clearTimeout(timer);
171
- resolve(value);
172
- }
173
- });
174
- });
175
- }
176
- async #bindingHash(magic, nonce) {
177
- const digest = await crypto.subtle.digest(
178
- "SHA-256",
179
- concatBytes(new TextEncoder().encode(magic), nonce, this.serverPublicKey)
180
- );
181
- return new Uint8Array(digest);
182
- }
183
- async authenticate() {
184
- const negotiate = negotiateMessage();
185
- this.send(encodeTsRequest({ version: CREDSSP_VERSION, negoToken: negotiate }));
186
- this.log("CredSSP", "sent NTLM NEGOTIATE");
187
- const challengeRequest = await this.#next();
188
- if (challengeRequest.errorCode) throw new Error(this.#statusMessage(challengeRequest.errorCode));
189
- if (!challengeRequest.negoToken) throw new Error("CredSSP: the host sent no NTLM challenge");
190
- const challengeBytes = challengeRequest.negoToken.slice();
191
- const challenge = parseChallengeMessage(challengeBytes);
192
- const targetName = new TextDecoder("utf-16le").decode(challenge.targetName);
193
- this.log("CredSSP", `received NTLM CHALLENGE from "${targetName}" (host CredSSP version ${challengeRequest.version})`);
194
- if (!this.username) {
195
- return {
196
- authenticated: false,
197
- reachedChallenge: true,
198
- hostVersion: challengeRequest.version,
199
- targetName,
200
- note: "no credentials supplied, stopping after the challenge"
201
- };
202
- }
203
- const { message, keys } = authenticateMessage({
204
- username: this.username,
205
- domain: this.domain,
206
- password: this.password,
207
- challenge,
208
- negotiateBytes: negotiate,
209
- challengeBytes
210
- });
211
- const sealer = new NtlmSealer(keys);
212
- const clientNonce = new Uint8Array(32);
213
- crypto.getRandomValues(clientNonce);
214
- const legacy = challengeRequest.version < 5;
215
- const binding = legacy ? this.serverPublicKey : await this.#bindingHash(CLIENT_TO_SERVER_MAGIC, clientNonce);
216
- this.send(encodeTsRequest({
217
- version: CREDSSP_VERSION,
218
- negoToken: message,
219
- pubKeyAuth: sealer.seal(binding),
220
- clientNonce: legacy ? null : clientNonce
221
- }));
222
- this.log("CredSSP", `sent NTLM AUTHENTICATE with the ${legacy ? "legacy" : "hashed"} public-key binding`);
223
- const response = await this.#next();
224
- if (response.errorCode) throw new Error(this.#statusMessage(response.errorCode));
225
- if (!response.pubKeyAuth) throw new Error("CredSSP: the host returned no public-key confirmation");
226
- const confirmed = sealer.unseal(response.pubKeyAuth);
227
- const expected = legacy ? this.#incrementedPublicKey() : await this.#bindingHash(SERVER_TO_CLIENT_MAGIC, clientNonce);
228
- if (confirmed.length !== expected.length || !expected.every((byte, i) => byte === confirmed[i])) {
229
- throw new Error(
230
- "CredSSP: the host public-key confirmation did not match. The TLS session is not the one the host authenticated, which is what this check exists to catch."
231
- );
232
- }
233
- this.log("CredSSP", "host confirmed the public-key binding");
234
- this.send(encodeTsRequest({
235
- version: CREDSSP_VERSION,
236
- authInfo: sealer.seal(encodeCredentials(this.domain, this.username, this.password))
237
- }));
238
- this.log("CredSSP", "sent credentials");
239
- return { authenticated: true, hostVersion: challengeRequest.version, targetName };
240
- }
241
- /** Legacy confirmation is the public key with its first byte incremented. */
242
- #incrementedPublicKey() {
243
- const expected = this.serverPublicKey.slice();
244
- expected[0] = expected[0] + 1 & 255;
245
- return expected;
246
- }
247
- #statusMessage(errorCode) {
248
- return NT_STATUS_MESSAGES[errorCode] || `CredSSP: the host reported error 0x${errorCode.toString(16).padStart(8, "0")}.`;
249
- }
250
- }
251
- export {
252
- CredSspClient
253
- };
@@ -1,63 +0,0 @@
1
- export declare function md5(message: Uint8Array): Uint8Array<ArrayBuffer>;
2
- export declare function sha1(message: Uint8Array): Uint8Array<ArrayBuffer>;
3
- /** RC4 keystream position. RDP re-keys every 4096 PDUs, so this is mutable. */
4
- export interface Rc4State {
5
- s: Uint8Array;
6
- i: number;
7
- j: number;
8
- }
9
- /** The five values MS-RDPBCGR 5.3.5 derives from the two randoms. */
10
- export interface SessionKeys {
11
- macKey: Uint8Array;
12
- keyLength: number;
13
- is40Bit: boolean;
14
- initialDecryptKey: Uint8Array;
15
- initialEncryptKey: Uint8Array;
16
- decryptKey: Uint8Array;
17
- encryptKey: Uint8Array;
18
- }
19
- export declare function rc4Init(key: Uint8Array): Rc4State;
20
- /** RC4 is symmetric, so this both encrypts and decrypts. State advances. */
21
- export declare function rc4Crypt(state: any, data: Uint8Array): Uint8Array<ArrayBuffer>;
22
- /**
23
- * Textbook RSA over little-endian buffers, which is what RDP's Security
24
- * Exchange PDU carries: the client random is raised to the server's public
25
- * exponent with no padding scheme (MS-RDPBCGR 5.3.4.1).
26
- */
27
- export declare function rsaEncrypt(dataLe: Uint8Array, modulusLe: Uint8Array, exponentLe: Uint8Array): Uint8Array<ArrayBuffer>;
28
- /**
29
- * RSAES-PKCS1-v1_5 encryption over big-endian buffers, which is what TLS uses
30
- * for an RSA key exchange. WebCrypto only offers OAEP, so the padding is built
31
- * here and the modular exponentiation reuses the same BigInt path as RDP.
32
- */
33
- export declare function rsaEncryptPkcs1(message: Uint8Array, modulusBe: Uint8Array, exponentBe: Uint8Array): Uint8Array<ArrayBuffer>;
34
- export declare const ENCRYPTION_METHOD: {
35
- NONE: number;
36
- BIT_40: number;
37
- BIT_128: number;
38
- BIT_56: number;
39
- FIPS: number;
40
- };
41
- export declare function keyLengthForMethod(encryptionMethod: number): 8 | 16;
42
- /**
43
- * Derive the MAC key and both directional RC4 keys from the two random values
44
- * exchanged during connection.
45
- */
46
- export declare function deriveSessionKeys(clientRandom: Uint8Array, serverRandom: Uint8Array, encryptionMethod: number): {
47
- macKey: Uint8Array<ArrayBuffer>;
48
- keyLength: number;
49
- is40Bit: boolean;
50
- initialDecryptKey: Uint8Array<ArrayBuffer>;
51
- initialEncryptKey: Uint8Array<ArrayBuffer>;
52
- decryptKey: Uint8Array<ArrayBuffer>;
53
- encryptKey: Uint8Array<ArrayBuffer>;
54
- };
55
- /**
56
- * MACSignature = First64Bits(MD5(macKey + Pad2 + SHA1(macKey + Pad1 + len + data)))
57
- */
58
- export declare function macSignature(macKey: any, data: Uint8Array): Uint8Array<ArrayBuffer>;
59
- /**
60
- * Every 4096 PDUs the RC4 key is refreshed from the initial key
61
- * (MS-RDPBCGR 5.3.7.1). The refreshed key is salted with itself.
62
- */
63
- export declare function updateSessionKey(initialKey: any, currentKey: any, keyLength: any, is40Bit: any): Uint8Array<ArrayBuffer>;