ai-remote 0.4.0 → 0.4.2

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 (166) hide show
  1. package/{src/index.ts → dist/index.d.ts} +0 -1
  2. package/dist/index.js +0 -1
  3. package/dist/protocols/index.d.ts +31 -0
  4. package/dist/protocols/index.js +0 -1
  5. package/dist/protocols/rdp/buffer.d.ts +38 -0
  6. package/dist/protocols/rdp/buffer.js +0 -1
  7. package/dist/protocols/rdp/caps.d.ts +49 -0
  8. package/dist/protocols/rdp/caps.js +0 -1
  9. package/dist/protocols/rdp/cert.d.ts +18 -0
  10. package/dist/protocols/rdp/cert.js +0 -1
  11. package/dist/protocols/rdp/client.d.ts +56 -0
  12. package/dist/protocols/rdp/client.js +0 -1
  13. package/dist/protocols/rdp/cliprdr.d.ts +27 -0
  14. package/dist/protocols/rdp/cliprdr.js +0 -1
  15. package/dist/protocols/rdp/credssp.d.ts +34 -0
  16. package/dist/protocols/rdp/credssp.js +0 -1
  17. package/dist/protocols/rdp/crypto.d.ts +63 -0
  18. package/dist/protocols/rdp/crypto.js +0 -1
  19. package/dist/protocols/rdp/display.d.ts +38 -0
  20. package/dist/protocols/rdp/display.js +0 -1
  21. package/dist/protocols/rdp/gcc.d.ts +23 -0
  22. package/dist/protocols/rdp/gcc.js +0 -1
  23. package/dist/protocols/rdp/keymap.d.ts +9 -0
  24. package/dist/protocols/rdp/keymap.js +0 -1
  25. package/dist/protocols/rdp/mcs.d.ts +40 -0
  26. package/dist/protocols/rdp/mcs.js +0 -1
  27. package/dist/protocols/rdp/ntlm.d.ts +61 -0
  28. package/dist/protocols/rdp/ntlm.js +0 -1
  29. package/dist/protocols/rdp/pdu.d.ts +155 -0
  30. package/dist/protocols/rdp/pdu.js +0 -1
  31. package/dist/protocols/rdp/rail.d.ts +119 -0
  32. package/dist/protocols/rdp/rail.js +0 -1
  33. package/dist/protocols/rdp/rle.d.ts +13 -0
  34. package/dist/protocols/rdp/rle.js +0 -1
  35. package/dist/protocols/rdp/sec.d.ts +59 -0
  36. package/dist/protocols/rdp/sec.js +0 -1
  37. package/dist/protocols/rdp/session.d.ts +62 -0
  38. package/dist/protocols/rdp/session.js +0 -1
  39. package/dist/protocols/rdp/tls.d.ts +18 -0
  40. package/dist/protocols/rdp/tls.js +0 -1
  41. package/dist/protocols/rdp/vchannel.d.ts +28 -0
  42. package/dist/protocols/rdp/vchannel.js +0 -1
  43. package/dist/protocols/rdp/x224.d.ts +40 -0
  44. package/dist/protocols/rdp/x224.js +0 -1
  45. package/dist/protocols/ssh/kex.d.ts +97 -0
  46. package/dist/protocols/ssh/kex.js +0 -1
  47. package/dist/protocols/ssh/messages.d.ts +50 -0
  48. package/dist/protocols/ssh/messages.js +0 -1
  49. package/dist/protocols/ssh/session.d.ts +66 -0
  50. package/dist/protocols/ssh/session.js +0 -1
  51. package/dist/protocols/ssh/terminal.d.ts +46 -0
  52. package/dist/protocols/ssh/terminal.js +0 -1
  53. package/dist/protocols/ssh/transport.d.ts +62 -0
  54. package/dist/protocols/ssh/transport.js +0 -1
  55. package/dist/protocols/ssh/wire.d.ts +52 -0
  56. package/dist/protocols/ssh/wire.js +0 -1
  57. package/dist/protocols/types.d.ts +127 -0
  58. package/dist/protocols/types.js +0 -1
  59. package/dist/protocols/vnc/input.d.ts +5 -0
  60. package/dist/protocols/vnc/input.js +0 -1
  61. package/dist/protocols/vnc/session.d.ts +12 -0
  62. package/dist/protocols/vnc/session.js +0 -1
  63. package/dist/shared/connection.d.ts +81 -0
  64. package/dist/shared/connection.js +0 -1
  65. package/dist/shared/device.d.ts +26 -0
  66. package/dist/shared/device.js +0 -1
  67. package/dist/shared/hosts.d.ts +63 -0
  68. package/dist/shared/hosts.js +0 -1
  69. package/dist/shared/icons.d.ts +46 -0
  70. package/dist/shared/icons.js +0 -1
  71. package/dist/shared/protocol.d.ts +28 -0
  72. package/dist/shared/protocol.js +0 -1
  73. package/dist/shared/signals.d.ts +37 -0
  74. package/dist/shared/signals.js +0 -1
  75. package/package.json +10 -11
  76. package/dist/index.js.map +0 -7
  77. package/dist/protocols/index.js.map +0 -7
  78. package/dist/protocols/rdp/buffer.js.map +0 -7
  79. package/dist/protocols/rdp/caps.js.map +0 -7
  80. package/dist/protocols/rdp/cert.js.map +0 -7
  81. package/dist/protocols/rdp/client.js.map +0 -7
  82. package/dist/protocols/rdp/cliprdr.js.map +0 -7
  83. package/dist/protocols/rdp/credssp.js.map +0 -7
  84. package/dist/protocols/rdp/crypto.js.map +0 -7
  85. package/dist/protocols/rdp/display.js.map +0 -7
  86. package/dist/protocols/rdp/gcc.js.map +0 -7
  87. package/dist/protocols/rdp/keymap.js.map +0 -7
  88. package/dist/protocols/rdp/mcs.js.map +0 -7
  89. package/dist/protocols/rdp/ntlm.js.map +0 -7
  90. package/dist/protocols/rdp/pdu.js.map +0 -7
  91. package/dist/protocols/rdp/rail.js.map +0 -7
  92. package/dist/protocols/rdp/rle.js.map +0 -7
  93. package/dist/protocols/rdp/sec.js.map +0 -7
  94. package/dist/protocols/rdp/session.js.map +0 -7
  95. package/dist/protocols/rdp/tls.js.map +0 -7
  96. package/dist/protocols/rdp/vchannel.js.map +0 -7
  97. package/dist/protocols/rdp/x224.js.map +0 -7
  98. package/dist/protocols/ssh/kex.js.map +0 -7
  99. package/dist/protocols/ssh/messages.js.map +0 -7
  100. package/dist/protocols/ssh/session.js.map +0 -7
  101. package/dist/protocols/ssh/terminal.js.map +0 -7
  102. package/dist/protocols/ssh/transport.js.map +0 -7
  103. package/dist/protocols/ssh/wire.js.map +0 -7
  104. package/dist/protocols/types.js.map +0 -7
  105. package/dist/protocols/vnc/input.js.map +0 -7
  106. package/dist/protocols/vnc/session.js.map +0 -7
  107. package/dist/shared/connection.js.map +0 -7
  108. package/dist/shared/device.js.map +0 -7
  109. package/dist/shared/hosts.js.map +0 -7
  110. package/dist/shared/icons.js.map +0 -7
  111. package/dist/shared/protocol.js.map +0 -7
  112. package/dist/shared/signals.js.map +0 -7
  113. package/src/cli/cli.ts +0 -462
  114. package/src/cli/daemon.ts +0 -306
  115. package/src/cli/framebuffer.ts +0 -115
  116. package/src/cli/generated/viewer-bundle.ts +0 -5
  117. package/src/cli/ipc.ts +0 -78
  118. package/src/cli/native/window.swift +0 -151
  119. package/src/cli/paths.ts +0 -28
  120. package/src/cli/png.ts +0 -106
  121. package/src/cli/session.ts +0 -263
  122. package/src/cli/shell.ts +0 -184
  123. package/src/cli/transport.ts +0 -86
  124. package/src/cli/viewer-client/assets.d.ts +0 -4
  125. package/src/cli/viewer-client/main.ts +0 -323
  126. package/src/cli/viewer-page.ts +0 -138
  127. package/src/cli/viewer.ts +0 -296
  128. package/src/cli/window.ts +0 -140
  129. package/src/cli/wsserver.ts +0 -144
  130. package/src/protocols/index.ts +0 -72
  131. package/src/protocols/rdp/buffer.ts +0 -204
  132. package/src/protocols/rdp/caps.ts +0 -304
  133. package/src/protocols/rdp/cert.ts +0 -190
  134. package/src/protocols/rdp/client.ts +0 -1371
  135. package/src/protocols/rdp/cliprdr.ts +0 -344
  136. package/src/protocols/rdp/credssp.ts +0 -319
  137. package/src/protocols/rdp/crypto.ts +0 -416
  138. package/src/protocols/rdp/display.ts +0 -750
  139. package/src/protocols/rdp/gcc.ts +0 -162
  140. package/src/protocols/rdp/keymap.ts +0 -68
  141. package/src/protocols/rdp/mcs.ts +0 -283
  142. package/src/protocols/rdp/ntlm.ts +0 -380
  143. package/src/protocols/rdp/pdu.ts +0 -497
  144. package/src/protocols/rdp/rail.ts +0 -480
  145. package/src/protocols/rdp/rle.ts +0 -343
  146. package/src/protocols/rdp/sec.ts +0 -184
  147. package/src/protocols/rdp/session.ts +0 -393
  148. package/src/protocols/rdp/tls.ts +0 -452
  149. package/src/protocols/rdp/vchannel.ts +0 -89
  150. package/src/protocols/rdp/x224.ts +0 -139
  151. package/src/protocols/ssh/kex.ts +0 -227
  152. package/src/protocols/ssh/messages.ts +0 -54
  153. package/src/protocols/ssh/session.ts +0 -879
  154. package/src/protocols/ssh/terminal.ts +0 -242
  155. package/src/protocols/ssh/transport.ts +0 -745
  156. package/src/protocols/ssh/wire.ts +0 -220
  157. package/src/protocols/types.ts +0 -146
  158. package/src/protocols/vnc/input.ts +0 -13
  159. package/src/protocols/vnc/novnc.d.ts +0 -75
  160. package/src/protocols/vnc/session.ts +0 -367
  161. package/src/shared/connection.ts +0 -206
  162. package/src/shared/device.ts +0 -28
  163. package/src/shared/hosts.ts +0 -240
  164. package/src/shared/icons.ts +0 -49
  165. package/src/shared/protocol.ts +0 -55
  166. package/src/shared/signals.ts +0 -67
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/protocols/rdp/sec.ts"],
4
- "sourcesContent": ["// RDP Standard Security layer (MS-RDPBCGR 2.2.8.1.1.2, 5.3).\n//\n// When the host negotiates TLS this layer stays inert: the host reports no\n// encryption method and the transport is already protected. With the RDP\n// security layer selected it owns the per-PDU MAC, the RC4 streams in both\n// directions, and the key rotation that RDP requires every 4096 PDUs.\n\nimport { Reader, Writer, concatBytes } from './buffer';\nimport {\n ENCRYPTION_METHOD,\n deriveSessionKeys,\n macSignature,\n rc4Crypt,\n rc4Init,\n updateSessionKey,\n} from './crypto';\nimport { ENCRYPTION_LEVEL } from './gcc';\n\nexport const SEC_FLAG = {\n EXCHANGE_PKT: 0x0001,\n TRANSPORT_REQ: 0x0002,\n ENCRYPT: 0x0008,\n RESET_SEQNO: 0x0010,\n IGNORE_SEQNO: 0x0020,\n INFO_PKT: 0x0040,\n LICENSE_PKT: 0x0080,\n LICENSE_ENCRYPT_CS: 0x0200,\n REDIRECTION_PKT: 0x0400,\n SECURE_CHECKSUM: 0x0800,\n AUTODETECT_REQ: 0x1000,\n AUTODETECT_RSP: 0x2000,\n HEARTBEAT: 0x4000,\n};\n\nconst KEY_ROTATION_INTERVAL = 4096;\n\nexport class SecurityLayer {\n private encryptionMethod: number;\n private encryptionLevel: number;\n private keys: SessionKeys | null;\n private encryptState: Rc4State | null;\n private decryptState: Rc4State | null;\n private encryptedPdus: number;\n private decryptedPdus: number;\n\n constructor() {\n this.encryptionMethod = ENCRYPTION_METHOD.NONE;\n this.encryptionLevel = ENCRYPTION_LEVEL.NONE;\n this.keys = null;\n this.encryptState = null;\n this.decryptState = null;\n this.encryptedPdus = 0;\n this.decryptedPdus = 0;\n }\n\n /** True when data PDUs carry a basic security header at all. */\n get headerPresent() {\n return this.encryptionLevel !== ENCRYPTION_LEVEL.NONE || this.encryptionMethod !== ENCRYPTION_METHOD.NONE;\n }\n\n get encryptionEnabled() {\n return this.encryptionMethod !== ENCRYPTION_METHOD.NONE && this.keys !== null;\n }\n\n configure(encryptionMethod: number, encryptionLevel: number, clientRandom: Uint8Array, serverRandom: Uint8Array) {\n this.encryptionMethod = encryptionMethod;\n this.encryptionLevel = encryptionLevel;\n\n if (encryptionMethod === ENCRYPTION_METHOD.NONE) return;\n if (encryptionMethod & ENCRYPTION_METHOD.FIPS) {\n throw new Error('RDP: this host requires FIPS encryption, which this client does not implement');\n }\n\n this.keys = deriveSessionKeys(clientRandom, serverRandom, encryptionMethod);\n this.encryptState = rc4Init(this.keys.encryptKey);\n this.decryptState = rc4Init(this.keys.decryptKey);\n }\n\n #rotateEncryptKey() {\n this.keys.encryptKey = updateSessionKey(\n this.keys.initialEncryptKey,\n this.keys.encryptKey,\n this.keys.keyLength,\n this.keys.is40Bit\n );\n this.encryptState = rc4Init(this.keys.encryptKey);\n this.encryptedPdus = 0;\n }\n\n #rotateDecryptKey() {\n this.keys.decryptKey = updateSessionKey(\n this.keys.initialDecryptKey,\n this.keys.decryptKey,\n this.keys.keyLength,\n this.keys.is40Bit\n );\n this.decryptState = rc4Init(this.keys.decryptKey);\n this.decryptedPdus = 0;\n }\n\n /**\n * Prefix a payload with the basic security header, signing and encrypting it\n * when the negotiated level asks for that.\n *\n * `flags` carries the PDU-kind bits (SEC_INFO_PKT and friends). A payload with\n * no flags and no encryption needs no header at all.\n */\n wrap(payload: Uint8Array, flags = 0) {\n const encrypt = this.encryptionEnabled;\n if (!encrypt && flags === 0 && !this.headerPresent) return payload;\n\n const writer = new Writer(payload.length + 16);\n writer.u16le(flags | (encrypt ? SEC_FLAG.ENCRYPT : 0));\n writer.u16le(0); // flagsHi, reserved\n\n if (!encrypt) {\n writer.raw(payload);\n return writer.take();\n }\n\n // The MAC covers the plaintext, and is sent alongside the ciphertext.\n writer.raw(macSignature(this.keys.macKey, payload));\n writer.raw(rc4Crypt(this.encryptState, payload));\n\n if (++this.encryptedPdus === KEY_ROTATION_INTERVAL) this.#rotateEncryptKey();\n return writer.take();\n }\n\n /**\n * Strip (and if needed decrypt) the basic security header from a host PDU.\n * `headerMandatory` forces the header to be read even when the host reported\n * no encryption, which licensing PDUs require.\n */\n unwrap(bytes: Uint8Array, headerMandatory = false) {\n if (!this.headerPresent && !headerMandatory) return { flags: 0, payload: bytes };\n if (bytes.length < 4) return { flags: 0, payload: bytes };\n\n const reader = new Reader(bytes);\n const flags = reader.u16le();\n reader.u16le(); // flagsHi\n\n if (!(flags & SEC_FLAG.ENCRYPT)) {\n return { flags, payload: reader.rest() };\n }\n if (!this.encryptionEnabled) {\n throw new Error('RDP: the host encrypted a PDU before the session keys existed');\n }\n\n reader.skip(8); // dataSignature -- verified implicitly by the stream decrypting\n const payload = rc4Crypt(this.decryptState, reader.rest());\n\n if (++this.decryptedPdus === KEY_ROTATION_INTERVAL) this.#rotateDecryptKey();\n return { flags, payload };\n }\n\n /**\n * Fast-path output carries the same signature and RC4 stream as a slow-path\n * PDU, but without the basic security header in front of it.\n */\n decryptFastPath(payload: Uint8Array) {\n if (!this.encryptionEnabled) return payload;\n if (payload.length <= 8) return payload;\n\n const decrypted = rc4Crypt(this.decryptState, payload.subarray(8));\n if (++this.decryptedPdus === KEY_ROTATION_INTERVAL) this.#rotateDecryptKey();\n return decrypted;\n }\n\n /**\n * Security Exchange PDU (MS-RDPBCGR 2.2.1.10): the client random encrypted\n * with the host public key, followed by eight bytes of padding.\n */\n static securityExchange(encryptedClientRandom) {\n const writer = new Writer(encryptedClientRandom.length + 16);\n writer.u16le(SEC_FLAG.EXCHANGE_PKT);\n writer.u16le(0);\n writer.u32le(encryptedClientRandom.length + 8);\n writer.raw(encryptedClientRandom);\n writer.zeros(8);\n return writer.take();\n }\n}\n\nexport { concatBytes };\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/protocols/rdp/session.ts"],
4
- "sourcesContent": ["// An RDP session wearing the same shape as noVNC's RFB object.\n//\n// The toolbar, the on-screen keyboard, the byte counters and the stats rail in\n// app.js were all written against that interface. Presenting RDP through the\n// same surface keeps one UI for both protocols instead of two of everything.\n\nimport { RdpClient } from './client';\nimport { RdpDisplay } from './display';\nimport type { DialogCopy, DisconnectContext, ProtocolDriver } from '../types';\nimport type { ConnectionSettings } from '../../shared/connection';\nimport { DEFAULT_PORTS } from '../../shared/protocol';\n\n/** Wording for the one connect dialog, which is shared markup with VNC. */\nconst DIALOG: DialogCopy = {\n usernameLabel: 'Username',\n usernamePlaceholder: 'Windows username',\n usernameHelp: 'Use the Windows account name for this computer.',\n passwordLabel: 'Password',\n passwordPlaceholder: 'Windows password',\n passwordHelp: 'Use the Windows account password. It may be left empty when the host presents its own sign-in screen.',\n // Credentials may be left empty and typed on the remote sign-in screen.\n requiresPassword: false,\n};\n\n/**\n * RDP credentials never appear in the URL: the browser-side client puts them\n * straight into the protocol's own Client Info PDU, or into CredSSP for NLA.\n */\nfunction buildUrl(origin: string, settings: ConnectionSettings): string {\n const query = new URLSearchParams({\n host: settings.host,\n port: String(settings.port),\n security: settings.security,\n });\n return `${origin}/rdp-ws?${query}`;\n}\n\n/**\n * Returns null when nothing RDP-specific applies. The client's own message,\n * when there is one, is always more specific and wins before this is consulted.\n */\nfunction describeDisconnect({ code, reason, suffix }: DisconnectContext): string | null {\n if (/hybrid_required|requires network level|authenticates before/i.test(reason)) {\n return `The RDP host signs in before showing a desktop${suffix}. `\n + 'Enter the Windows username and password and connect again.';\n }\n if (code === 1011 || /cannot connect|unreachable|tcp error|tcp write/i.test(reason)) {\n return `The Worker could not keep a TCP connection to the RDP host${suffix}. `\n + 'Check that Remote Desktop is enabled and listening on this address and port.';\n }\n return null;\n}\n\nexport class RdpSession extends EventTarget {\n readonly protocol = 'rdp' as const;\n\n constructor(container, url: string, options = {}) {\n super();\n\n this.url = url;\n this.options = options;\n this.display = new RdpDisplay(container);\n this.display.viewOnly = Boolean(options.viewOnly);\n\n this._rfbConnectionState = 'connecting';\n this._scaleViewport = options.scaleViewport !== false;\n this.display.scaleViewport = this._scaleViewport;\n this.lastErrorMessage = '';\n this.hostLabel = options.hostLabel || 'Remote Desktop';\n\n this.#createClient(options.security || 'auto');\n\n // RemoteApp windows are sized inside a fixed negotiated RDP desktop. The\n // browser pane can still change when AI/SSH docks, closes, or the device\n // rotates, so publish the new work area and refit the application whenever\n // the pane itself changes size.\n this.remoteAppLayoutObserver = options.remoteApp?.program\n && typeof ResizeObserver === 'function'\n ? new ResizeObserver(() => this.fitToViewport())\n : null;\n this.remoteAppLayoutObserver?.observe(this.display.scrollHost);\n }\n\n #createClient(security) {\n this.security = security;\n this.client = new RdpClient(this.url, {\n width: this.options.width,\n height: this.options.height,\n username: this.options.credentials?.username || '',\n password: this.options.credentials?.password || '',\n domain: this.options.domain || '',\n security,\n keyboardLayout: this.options.keyboardLayout || 0x409,\n clientName: this.options.clientName || 'cf-gateway',\n remoteApp: this.options.remoteApp || null,\n });\n\n this.client.addEventListener('ready', (event) => {\n const { width, height } = event.detail;\n this.display.resize(width, height);\n this.display.attachInput(this.client);\n this.display.focus();\n this.fitToViewport();\n\n this._rfbConnectionState = 'connected';\n this.dispatchEvent(new CustomEvent('connect'));\n this.dispatchEvent(new CustomEvent('desktopname', {\n detail: { name: `${this.hostLabel} (${width}x${height})` },\n }));\n });\n\n // A host that re-runs capability exchange with a different desktop size.\n this.client.addEventListener('resize', (event) => {\n const { width, height } = event.detail;\n this.display.resize(width, height);\n this.dispatchEvent(new CustomEvent('desktopname', {\n detail: { name: `${this.hostLabel} (${width}x${height})` },\n }));\n this.fitToViewport();\n });\n\n // In RemoteApp mode the canvas follows the application's own window, so\n // the desktop around it -- which the host never paints -- stays off screen.\n this.client.addEventListener('remoteappviewport', (event) => {\n this.display.setViewport(event.detail);\n });\n\n this.client.addEventListener('bitmap', (event) => {\n this.display.drawRects(event.detail.rects, event.detail.palette);\n // The workspace keeps a pre-close RemoteApp thumbnail in memory. Emit\n // only after the rectangles have reached the canvas so its capture is a\n // real application frame rather than the black framebuffer allocated at\n // connection time.\n this.dispatchEvent(new CustomEvent('frame', {\n detail: { rects: event.detail.rects },\n }));\n });\n\n this.client.addEventListener('palette', (event) => {\n this.display.palette = event.detail;\n });\n\n this.client.addEventListener('pointer', (event) => {\n this.display.applyPointer(event.detail);\n });\n\n // Same event name and shape noVNC uses, so app.js needs no protocol branch.\n this.client.addEventListener('clipboard', (event) => {\n this.dispatchEvent(new CustomEvent('clipboard', { detail: { text: event.detail.text } }));\n });\n\n this.client.addEventListener('remoteappresult', (event) => {\n this.dispatchEvent(new CustomEvent('remoteappresult', { detail: event.detail }));\n });\n\n this.client.addEventListener('remoteappwindow', (event) => {\n this.dispatchEvent(new CustomEvent('remoteappwindow', { detail: event.detail }));\n });\n\n this.client.addEventListener('remoteappclosed', (event) => {\n this.dispatchEvent(new CustomEvent('remoteappclosed', { detail: event.detail }));\n });\n\n this.client.addEventListener('error', (event) => {\n this.lastErrorMessage = event.detail.message;\n });\n\n this.client.addEventListener('close', (event) => {\n this._rfbConnectionState = 'disconnected';\n this.remoteAppLayoutObserver?.disconnect();\n this.display.destroy();\n this.dispatchEvent(new CustomEvent('disconnect', {\n detail: {\n clean: Boolean(event.detail.clean),\n message: this.lastErrorMessage || event.detail.message || '',\n code: event.detail.code,\n phase: event.detail.phase,\n },\n }));\n });\n }\n\n /**\n * Ask the host to size the remote application's window to the room this\n * session has on the page. Full-desktop sessions have nothing to do here:\n * their size is fixed when the connection is negotiated.\n */\n fitToViewport() {\n if (!this.options.remoteApp?.program) return false;\n\n const bounds = this.display.container.getBoundingClientRect();\n const width = Math.floor(bounds.width || this.options.width || 0);\n const height = Math.floor(bounds.height || this.options.height || 0);\n if (width <= 0 || height <= 0) return false;\n\n return this.client.fitRemoteApp(width, height);\n }\n\n get connected(): boolean {\n return this._rfbConnectionState === 'connected';\n }\n\n get canvas(): HTMLCanvasElement | null {\n return this.display?.canvas ?? null;\n }\n\n /** The workspace instruments this socket for byte counters and keepalives. */\n get socket(): WebSocket | null {\n return this.client.socket ?? null;\n }\n\n get _sock() {\n return { _websocket: this.client.socket };\n }\n\n get scaleViewport() {\n return this._scaleViewport;\n }\n\n set scaleViewport(enabled) {\n this._scaleViewport = Boolean(enabled);\n this.display.setScaleViewport(this._scaleViewport);\n }\n\n get viewOnly() {\n return this.display.viewOnly;\n }\n\n set viewOnly(enabled) {\n this.display.viewOnly = Boolean(enabled);\n if (enabled) this.display.releaseAllKeys();\n }\n\n /**\n * The on-screen keyboard and the special-keys dialog call this with an X11\n * keysym and a DOM code. RDP wants a scancode, so the code decides; the\n * keysym is only a fallback for characters with no key of their own.\n */\n sendKey(keysym, code: number, pressed: boolean) {\n if (code && this.client.sendKeyByCode(code, pressed)) return;\n\n // Unicode input has no press/release pair to mirror, so send it once.\n if (!pressed || !keysym) return;\n if (keysym >= 0x20 && keysym <= 0x10ffff) {\n this.client.sendUnicode(String.fromCodePoint(keysym));\n }\n }\n\n sendCtrlAltDel() {\n this.client.sendCtrlAltDel();\n }\n\n // --- pointer, for the AI pane's screen-control tool ----------------------\n //\n // The display already turns browser events into these calls; exposing them\n // lets something other than a mouse drive the same path.\n\n /** Desktop pixels. Returns false when the session cannot accept input. */\n movePointer(x: number, y: number) {\n if (!this.client?.connected || this.display.viewOnly) return false;\n this.client.sendMouseMove(...this.#clampToDesktop(x, y));\n return true;\n }\n\n clickPointer(x: number, y: number, button = 0) {\n if (!this.client?.connected || this.display.viewOnly) return false;\n const [px, py] = this.#clampToDesktop(x, y);\n this.client.sendMouseMove(px, py);\n this.pointerButton(px, py, button, true);\n this.pointerButton(px, py, button, false);\n return true;\n }\n\n pointerButton(x: number, y: number, button = 0, pressed = true) {\n if (!this.client?.connected || this.display.viewOnly) return false;\n const [px, py] = this.#clampToDesktop(x, y);\n this.client.sendMouseButton(button, pressed, px, py);\n return true;\n }\n\n scrollPointer(x: number, y: number, delta: { x: number; y: number }) {\n if (!this.client?.connected || this.display.viewOnly) return false;\n const [px, py] = this.#clampToDesktop(x, y);\n for (let i = 0; i < Math.min(20, Math.abs(delta.y)); i++) {\n this.client.sendWheel(Math.sign(delta.y), px, py, false);\n }\n for (let i = 0; i < Math.min(20, Math.abs(delta.x)); i++) {\n this.client.sendWheel(Math.sign(delta.x), px, py, true);\n }\n return true;\n }\n\n /** Type a run of characters, which RDP carries as Unicode rather than keys. */\n typeText(text: string) {\n if (!this.client?.connected || this.display.viewOnly || !text) return false;\n let printable = '';\n const flushPrintable = () => {\n if (!printable) return;\n this.client.sendUnicode(printable);\n printable = '';\n };\n\n for (const character of text) {\n if (character === '\\n' || character === '\\r') {\n flushPrintable();\n this.client.sendKeyByCode('Enter', true);\n this.client.sendKeyByCode('Enter', false);\n } else if (character === '\\t') {\n flushPrintable();\n this.client.sendKeyByCode('Tab', true);\n this.client.sendKeyByCode('Tab', false);\n } else {\n printable += character;\n }\n }\n flushPrintable();\n return true;\n }\n\n #clampToDesktop(x: number, y: number) {\n return [\n Math.max(0, Math.min((this.display.width || 1) - 1, Math.round(x))),\n Math.max(0, Math.min((this.display.height || 1) - 1, Math.round(y))),\n ];\n }\n\n /** What the canvas is showing, for a screen capture or a coordinate check. */\n get screenGeometry() {\n return {\n width: this.display.width,\n height: this.display.height,\n viewport: { ...this.display.viewport },\n };\n }\n\n /**\n * Hand text to the host's clipboard, falling back to typing it when the host\n * declined the clipboard channel -- xrdp builds often do.\n */\n clipboardPasteFrom(text: string) {\n if (!text) return;\n\n const shared = this.client.setClipboardText(text);\n if (!shared || typeof shared.then !== 'function') {\n this.client.sendUnicode(text);\n return;\n }\n\n shared.then((acknowledged: boolean) => {\n if (!acknowledged) this.client.sendUnicode(text);\n });\n }\n\n /**\n * Deliver text into the session, which is what the clipboard drawer's button\n * has always visibly done. `clipboardPasteFrom` only loads the clipboard, the\n * way noVNC's method of that name does.\n */\n pasteText(text: string) {\n return text ? this.client.pasteToHost(text) : false;\n }\n\n releaseKeyboard(): void {\n // Anything held down has to go up on the host's side too, or the far\n // machine spends the rest of the session thinking Command is pressed.\n this.display.releaseAllKeys();\n this.display.blur();\n }\n\n /** True once the host has agreed to share its clipboard. */\n get clipboardShared() {\n return this.client.clipboardShared;\n }\n\n disconnect() {\n this.remoteAppLayoutObserver?.disconnect();\n this.client.disconnect();\n this.display.destroy();\n this._rfbConnectionState = 'disconnected';\n }\n}\n\nexport const rdpDriver: ProtocolDriver = {\n protocol: 'rdp',\n label: 'RDP Client',\n defaultPort: DEFAULT_PORTS.rdp,\n dialog: DIALOG,\n buildUrl,\n createSession: (container, url, options) => new RdpSession(container, url, options),\n // The RDP client logs its own progress, so there is nothing to add here.\n describeState: () => ({}),\n describeDisconnect,\n};\n"],
5
- "mappings": "AAMA,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAG3B,SAAS,qBAAqB;AAG9B,MAAM,SAAqB;AAAA,EACzB,eAAe;AAAA,EACf,qBAAqB;AAAA,EACrB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,qBAAqB;AAAA,EACrB,cAAc;AAAA;AAAA,EAEd,kBAAkB;AACpB;AAMA,SAAS,SAAS,QAAgB,UAAsC;AACtE,QAAM,QAAQ,IAAI,gBAAgB;AAAA,IAChC,MAAM,SAAS;AAAA,IACf,MAAM,OAAO,SAAS,IAAI;AAAA,IAC1B,UAAU,SAAS;AAAA,EACrB,CAAC;AACD,SAAO,GAAG,MAAM,WAAW,KAAK;AAClC;AAMA,SAAS,mBAAmB,EAAE,MAAM,QAAQ,OAAO,GAAqC;AACtF,MAAI,+DAA+D,KAAK,MAAM,GAAG;AAC/E,WAAO,iDAAiD,MAAM;AAAA,EAEhE;AACA,MAAI,SAAS,QAAQ,kDAAkD,KAAK,MAAM,GAAG;AACnF,WAAO,6DAA6D,MAAM;AAAA,EAE5E;AACA,SAAO;AACT;AAEO,MAAM,mBAAmB,YAAY;AAAA,EACjC,WAAW;AAAA,EAEpB,YAAY,WAAW,KAAa,UAAU,CAAC,GAAG;AAChD,UAAM;AAEN,SAAK,MAAM;AACX,SAAK,UAAU;AACf,SAAK,UAAU,IAAI,WAAW,SAAS;AACvC,SAAK,QAAQ,WAAW,QAAQ,QAAQ,QAAQ;AAEhD,SAAK,sBAAsB;AAC3B,SAAK,iBAAiB,QAAQ,kBAAkB;AAChD,SAAK,QAAQ,gBAAgB,KAAK;AAClC,SAAK,mBAAmB;AACxB,SAAK,YAAY,QAAQ,aAAa;AAEtC,SAAK,cAAc,QAAQ,YAAY,MAAM;AAM7C,SAAK,0BAA0B,QAAQ,WAAW,WAC7C,OAAO,mBAAmB,aAC3B,IAAI,eAAe,MAAM,KAAK,cAAc,CAAC,IAC7C;AACJ,SAAK,yBAAyB,QAAQ,KAAK,QAAQ,UAAU;AAAA,EAC/D;AAAA,EAEA,cAAc,UAAU;AACtB,SAAK,WAAW;AAChB,SAAK,SAAS,IAAI,UAAU,KAAK,KAAK;AAAA,MACpC,OAAO,KAAK,QAAQ;AAAA,MACpB,QAAQ,KAAK,QAAQ;AAAA,MACrB,UAAU,KAAK,QAAQ,aAAa,YAAY;AAAA,MAChD,UAAU,KAAK,QAAQ,aAAa,YAAY;AAAA,MAChD,QAAQ,KAAK,QAAQ,UAAU;AAAA,MAC/B;AAAA,MACA,gBAAgB,KAAK,QAAQ,kBAAkB;AAAA,MAC/C,YAAY,KAAK,QAAQ,cAAc;AAAA,MACvC,WAAW,KAAK,QAAQ,aAAa;AAAA,IACvC,CAAC;AAED,SAAK,OAAO,iBAAiB,SAAS,CAAC,UAAU;AAC/C,YAAM,EAAE,OAAO,OAAO,IAAI,MAAM;AAChC,WAAK,QAAQ,OAAO,OAAO,MAAM;AACjC,WAAK,QAAQ,YAAY,KAAK,MAAM;AACpC,WAAK,QAAQ,MAAM;AACnB,WAAK,cAAc;AAEnB,WAAK,sBAAsB;AAC3B,WAAK,cAAc,IAAI,YAAY,SAAS,CAAC;AAC7C,WAAK,cAAc,IAAI,YAAY,eAAe;AAAA,QAChD,QAAQ,EAAE,MAAM,GAAG,KAAK,SAAS,KAAK,KAAK,IAAI,MAAM,IAAI;AAAA,MAC3D,CAAC,CAAC;AAAA,IACJ,CAAC;AAGD,SAAK,OAAO,iBAAiB,UAAU,CAAC,UAAU;AAChD,YAAM,EAAE,OAAO,OAAO,IAAI,MAAM;AAChC,WAAK,QAAQ,OAAO,OAAO,MAAM;AACjC,WAAK,cAAc,IAAI,YAAY,eAAe;AAAA,QAChD,QAAQ,EAAE,MAAM,GAAG,KAAK,SAAS,KAAK,KAAK,IAAI,MAAM,IAAI;AAAA,MAC3D,CAAC,CAAC;AACF,WAAK,cAAc;AAAA,IACrB,CAAC;AAID,SAAK,OAAO,iBAAiB,qBAAqB,CAAC,UAAU;AAC3D,WAAK,QAAQ,YAAY,MAAM,MAAM;AAAA,IACvC,CAAC;AAED,SAAK,OAAO,iBAAiB,UAAU,CAAC,UAAU;AAChD,WAAK,QAAQ,UAAU,MAAM,OAAO,OAAO,MAAM,OAAO,OAAO;AAK/D,WAAK,cAAc,IAAI,YAAY,SAAS;AAAA,QAC1C,QAAQ,EAAE,OAAO,MAAM,OAAO,MAAM;AAAA,MACtC,CAAC,CAAC;AAAA,IACJ,CAAC;AAED,SAAK,OAAO,iBAAiB,WAAW,CAAC,UAAU;AACjD,WAAK,QAAQ,UAAU,MAAM;AAAA,IAC/B,CAAC;AAED,SAAK,OAAO,iBAAiB,WAAW,CAAC,UAAU;AACjD,WAAK,QAAQ,aAAa,MAAM,MAAM;AAAA,IACxC,CAAC;AAGD,SAAK,OAAO,iBAAiB,aAAa,CAAC,UAAU;AACnD,WAAK,cAAc,IAAI,YAAY,aAAa,EAAE,QAAQ,EAAE,MAAM,MAAM,OAAO,KAAK,EAAE,CAAC,CAAC;AAAA,IAC1F,CAAC;AAED,SAAK,OAAO,iBAAiB,mBAAmB,CAAC,UAAU;AACzD,WAAK,cAAc,IAAI,YAAY,mBAAmB,EAAE,QAAQ,MAAM,OAAO,CAAC,CAAC;AAAA,IACjF,CAAC;AAED,SAAK,OAAO,iBAAiB,mBAAmB,CAAC,UAAU;AACzD,WAAK,cAAc,IAAI,YAAY,mBAAmB,EAAE,QAAQ,MAAM,OAAO,CAAC,CAAC;AAAA,IACjF,CAAC;AAED,SAAK,OAAO,iBAAiB,mBAAmB,CAAC,UAAU;AACzD,WAAK,cAAc,IAAI,YAAY,mBAAmB,EAAE,QAAQ,MAAM,OAAO,CAAC,CAAC;AAAA,IACjF,CAAC;AAED,SAAK,OAAO,iBAAiB,SAAS,CAAC,UAAU;AAC/C,WAAK,mBAAmB,MAAM,OAAO;AAAA,IACvC,CAAC;AAED,SAAK,OAAO,iBAAiB,SAAS,CAAC,UAAU;AAC/C,WAAK,sBAAsB;AAC3B,WAAK,yBAAyB,WAAW;AACzC,WAAK,QAAQ,QAAQ;AACrB,WAAK,cAAc,IAAI,YAAY,cAAc;AAAA,QAC/C,QAAQ;AAAA,UACN,OAAO,QAAQ,MAAM,OAAO,KAAK;AAAA,UACjC,SAAS,KAAK,oBAAoB,MAAM,OAAO,WAAW;AAAA,UAC1D,MAAM,MAAM,OAAO;AAAA,UACnB,OAAO,MAAM,OAAO;AAAA,QACtB;AAAA,MACF,CAAC,CAAC;AAAA,IACJ,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAgB;AACd,QAAI,CAAC,KAAK,QAAQ,WAAW,QAAS,QAAO;AAE7C,UAAM,SAAS,KAAK,QAAQ,UAAU,sBAAsB;AAC5D,UAAM,QAAQ,KAAK,MAAM,OAAO,SAAS,KAAK,QAAQ,SAAS,CAAC;AAChE,UAAM,SAAS,KAAK,MAAM,OAAO,UAAU,KAAK,QAAQ,UAAU,CAAC;AACnE,QAAI,SAAS,KAAK,UAAU,EAAG,QAAO;AAEtC,WAAO,KAAK,OAAO,aAAa,OAAO,MAAM;AAAA,EAC/C;AAAA,EAEA,IAAI,YAAqB;AACvB,WAAO,KAAK,wBAAwB;AAAA,EACtC;AAAA,EAEA,IAAI,SAAmC;AACrC,WAAO,KAAK,SAAS,UAAU;AAAA,EACjC;AAAA;AAAA,EAGA,IAAI,SAA2B;AAC7B,WAAO,KAAK,OAAO,UAAU;AAAA,EAC/B;AAAA,EAEA,IAAI,QAAQ;AACV,WAAO,EAAE,YAAY,KAAK,OAAO,OAAO;AAAA,EAC1C;AAAA,EAEA,IAAI,gBAAgB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,cAAc,SAAS;AACzB,SAAK,iBAAiB,QAAQ,OAAO;AACrC,SAAK,QAAQ,iBAAiB,KAAK,cAAc;AAAA,EACnD;AAAA,EAEA,IAAI,WAAW;AACb,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EAEA,IAAI,SAAS,SAAS;AACpB,SAAK,QAAQ,WAAW,QAAQ,OAAO;AACvC,QAAI,QAAS,MAAK,QAAQ,eAAe;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,QAAQ,MAAc,SAAkB;AAC9C,QAAI,QAAQ,KAAK,OAAO,cAAc,MAAM,OAAO,EAAG;AAGtD,QAAI,CAAC,WAAW,CAAC,OAAQ;AACzB,QAAI,UAAU,MAAQ,UAAU,SAAU;AACxC,WAAK,OAAO,YAAY,OAAO,cAAc,MAAM,CAAC;AAAA,IACtD;AAAA,EACF;AAAA,EAEA,iBAAiB;AACf,SAAK,OAAO,eAAe;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,YAAY,GAAW,GAAW;AAChC,QAAI,CAAC,KAAK,QAAQ,aAAa,KAAK,QAAQ,SAAU,QAAO;AAC7D,SAAK,OAAO,cAAc,GAAG,KAAK,gBAAgB,GAAG,CAAC,CAAC;AACvD,WAAO;AAAA,EACT;AAAA,EAEA,aAAa,GAAW,GAAW,SAAS,GAAG;AAC7C,QAAI,CAAC,KAAK,QAAQ,aAAa,KAAK,QAAQ,SAAU,QAAO;AAC7D,UAAM,CAAC,IAAI,EAAE,IAAI,KAAK,gBAAgB,GAAG,CAAC;AAC1C,SAAK,OAAO,cAAc,IAAI,EAAE;AAChC,SAAK,cAAc,IAAI,IAAI,QAAQ,IAAI;AACvC,SAAK,cAAc,IAAI,IAAI,QAAQ,KAAK;AACxC,WAAO;AAAA,EACT;AAAA,EAEA,cAAc,GAAW,GAAW,SAAS,GAAG,UAAU,MAAM;AAC9D,QAAI,CAAC,KAAK,QAAQ,aAAa,KAAK,QAAQ,SAAU,QAAO;AAC7D,UAAM,CAAC,IAAI,EAAE,IAAI,KAAK,gBAAgB,GAAG,CAAC;AAC1C,SAAK,OAAO,gBAAgB,QAAQ,SAAS,IAAI,EAAE;AACnD,WAAO;AAAA,EACT;AAAA,EAEA,cAAc,GAAW,GAAW,OAAiC;AACnE,QAAI,CAAC,KAAK,QAAQ,aAAa,KAAK,QAAQ,SAAU,QAAO;AAC7D,UAAM,CAAC,IAAI,EAAE,IAAI,KAAK,gBAAgB,GAAG,CAAC;AAC1C,aAAS,IAAI,GAAG,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,MAAM,CAAC,CAAC,GAAG,KAAK;AACxD,WAAK,OAAO,UAAU,KAAK,KAAK,MAAM,CAAC,GAAG,IAAI,IAAI,KAAK;AAAA,IACzD;AACA,aAAS,IAAI,GAAG,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,MAAM,CAAC,CAAC,GAAG,KAAK;AACxD,WAAK,OAAO,UAAU,KAAK,KAAK,MAAM,CAAC,GAAG,IAAI,IAAI,IAAI;AAAA,IACxD;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,SAAS,MAAc;AACrB,QAAI,CAAC,KAAK,QAAQ,aAAa,KAAK,QAAQ,YAAY,CAAC,KAAM,QAAO;AACtE,QAAI,YAAY;AAChB,UAAM,iBAAiB,MAAM;AAC3B,UAAI,CAAC,UAAW;AAChB,WAAK,OAAO,YAAY,SAAS;AACjC,kBAAY;AAAA,IACd;AAEA,eAAW,aAAa,MAAM;AAC5B,UAAI,cAAc,QAAQ,cAAc,MAAM;AAC5C,uBAAe;AACf,aAAK,OAAO,cAAc,SAAS,IAAI;AACvC,aAAK,OAAO,cAAc,SAAS,KAAK;AAAA,MAC1C,WAAW,cAAc,KAAM;AAC7B,uBAAe;AACf,aAAK,OAAO,cAAc,OAAO,IAAI;AACrC,aAAK,OAAO,cAAc,OAAO,KAAK;AAAA,MACxC,OAAO;AACL,qBAAa;AAAA,MACf;AAAA,IACF;AACA,mBAAe;AACf,WAAO;AAAA,EACT;AAAA,EAEA,gBAAgB,GAAW,GAAW;AACpC,WAAO;AAAA,MACL,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,QAAQ,SAAS,KAAK,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC;AAAA,MAClE,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,QAAQ,UAAU,KAAK,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC;AAAA,IACrE;AAAA,EACF;AAAA;AAAA,EAGA,IAAI,iBAAiB;AACnB,WAAO;AAAA,MACL,OAAO,KAAK,QAAQ;AAAA,MACpB,QAAQ,KAAK,QAAQ;AAAA,MACrB,UAAU,EAAE,GAAG,KAAK,QAAQ,SAAS;AAAA,IACvC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAAmB,MAAc;AAC/B,QAAI,CAAC,KAAM;AAEX,UAAM,SAAS,KAAK,OAAO,iBAAiB,IAAI;AAChD,QAAI,CAAC,UAAU,OAAO,OAAO,SAAS,YAAY;AAChD,WAAK,OAAO,YAAY,IAAI;AAC5B;AAAA,IACF;AAEA,WAAO,KAAK,CAAC,iBAA0B;AACrC,UAAI,CAAC,aAAc,MAAK,OAAO,YAAY,IAAI;AAAA,IACjD,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAU,MAAc;AACtB,WAAO,OAAO,KAAK,OAAO,YAAY,IAAI,IAAI;AAAA,EAChD;AAAA,EAEA,kBAAwB;AAGtB,SAAK,QAAQ,eAAe;AAC5B,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA;AAAA,EAGA,IAAI,kBAAkB;AACpB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,aAAa;AACX,SAAK,yBAAyB,WAAW;AACzC,SAAK,OAAO,WAAW;AACvB,SAAK,QAAQ,QAAQ;AACrB,SAAK,sBAAsB;AAAA,EAC7B;AACF;AAEO,MAAM,YAA4B;AAAA,EACvC,UAAU;AAAA,EACV,OAAO;AAAA,EACP,aAAa,cAAc;AAAA,EAC3B,QAAQ;AAAA,EACR;AAAA,EACA,eAAe,CAAC,WAAW,KAAK,YAAY,IAAI,WAAW,WAAW,KAAK,OAAO;AAAA;AAAA,EAElF,eAAe,OAAO,CAAC;AAAA,EACvB;AACF;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/protocols/rdp/tls.ts"],
4
- "sourcesContent": ["// A single-purpose TLS 1.2 client, written because neither of the TLS stacks\n// available to this gateway can be used for NLA:\n//\n// * Cloudflare Workers' socket.startTls() validates the host certificate\n// against public CAs with no opt-out, and RDP hosts use self-signed\n// certificates.\n// * CredSSP has to encrypt the host certificate's public key to prove the\n// channel is not being relayed (MS-CSSP 3.1.5), and no runtime here exposes\n// the certificate of a TLS session it established for us.\n//\n// Doing the handshake in JavaScript solves both at once: we see the certificate\n// and we decide what to do about it. Only what RDP needs is implemented -- TLS\n// 1.2, RSA key transport, AES-GCM records -- so this is a few hundred lines\n// rather than a library. The certificate is deliberately NOT validated, exactly\n// as mstsc does not validate it by default; CredSSP's binding of that\n// certificate to a password-derived key is what actually stops a MITM here.\n\nimport { Reader, Writer, concatBytes } from './buffer';\nimport { parseX509PublicKey } from './cert';\nimport { rsaEncryptPkcs1 } from './crypto';\n\nconst TLS_1_2 = 0x0303;\n\nconst RECORD = {\n CHANGE_CIPHER_SPEC: 20,\n ALERT: 21,\n HANDSHAKE: 22,\n APPLICATION_DATA: 23,\n};\n\nconst HANDSHAKE = {\n CLIENT_HELLO: 1,\n SERVER_HELLO: 2,\n NEW_SESSION_TICKET: 4,\n CERTIFICATE: 11,\n SERVER_KEY_EXCHANGE: 12,\n CERTIFICATE_REQUEST: 13,\n SERVER_HELLO_DONE: 14,\n CLIENT_KEY_EXCHANGE: 16,\n FINISHED: 20,\n};\n\n/**\n * Both suites use RSA key transport, so there is no ServerKeyExchange to verify\n * and no ephemeral key agreement to implement. They differ only in PRF hash and\n * key size.\n */\nconst CIPHER_SUITES = {\n 0x009c: { name: 'TLS_RSA_WITH_AES_128_GCM_SHA256', keyLength: 16, hash: 'SHA-256' },\n 0x009d: { name: 'TLS_RSA_WITH_AES_256_GCM_SHA384', keyLength: 32, hash: 'SHA-384' },\n};\n\nconst ALERT_DESCRIPTIONS = {\n 0: 'close_notify',\n 10: 'unexpected_message',\n 20: 'bad_record_mac',\n 40: 'handshake_failure',\n 42: 'bad_certificate',\n 46: 'certificate_unknown',\n 47: 'illegal_parameter',\n 48: 'unknown_ca',\n 50: 'decode_error',\n 51: 'decrypt_error',\n 70: 'protocol_version',\n 71: 'insufficient_security',\n 80: 'internal_error',\n 90: 'user_canceled',\n 109: 'missing_extension',\n 112: 'unrecognized_name',\n};\n\nconst ascii = (text: string) => new TextEncoder().encode(text);\n\nfunction u16(value) {\n return new Uint8Array([(value >> 8) & 0xff, value & 0xff]);\n}\n\nfunction u24(value) {\n return new Uint8Array([(value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff]);\n}\n\nfunction u64(value) {\n const out = new Uint8Array(8);\n new DataView(out.buffer).setBigUint64(0, BigInt(value));\n return out;\n}\n\n// --- TLS 1.2 PRF (RFC 5246 5) ----------------------------------------------\n\nasync function pHash(hash: Uint8Array, secret: Uint8Array, seed: Uint8Array, length: number) {\n const key = await crypto.subtle.importKey('raw', secret, { name: 'HMAC', hash }, false, ['sign']);\n const hmac = async (data: Uint8Array) => new Uint8Array(await crypto.subtle.sign('HMAC', key, data));\n\n const blocks = [];\n let produced = 0;\n let a = await hmac(seed);\n\n while (produced < length) {\n const block = await hmac(concatBytes(a, seed));\n blocks.push(block);\n produced += block.length;\n a = await hmac(a);\n }\n\n return concatBytes(...blocks).subarray(0, length);\n}\n\nconst prf = (hash: Uint8Array, secret: Uint8Array, label: string, seed: Uint8Array, length: number) =>\n pHash(hash, secret, concatBytes(ascii(label), seed), length);\n\nexport class Tls12Client {\n /**\n * @param {(bytes: Uint8Array) => void} send write raw bytes to the transport\n * @param {(bytes: Uint8Array) => void} onData decrypted application data\n * @param {(error: Error) => void} onError fatal protocol failure\n */\n constructor({ send, onData, onError }) {\n this.sendRaw = send;\n this.onData = onData;\n this.onError = onError;\n\n this.inbound = new Uint8Array(0);\n this.transcript = [];\n this.established = false;\n this.serverPublicKey = null;\n this.suite = null;\n this.certificates = [];\n\n this.readSequence = 0n;\n this.writeSequence = 0n;\n this.readEncrypted = false;\n this.writeEncrypted = false;\n\n // Records must be processed strictly in order, and every step is async.\n this.queue = Promise.resolve();\n this.handshakeDone = new Promise((resolve, reject) => {\n this.resolveHandshake = resolve;\n this.rejectHandshake = reject;\n });\n }\n\n // --- handshake ------------------------------------------------------------\n\n async start() {\n this.clientRandom = new Uint8Array(32);\n crypto.getRandomValues(this.clientRandom);\n\n const extensions = concatBytes(\n // signature_algorithms: rsa with sha256, sha384, sha1\n u16(0x000d), u16(10), u16(8),\n new Uint8Array([0x04, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01]),\n // renegotiation_info: empty, which Schannel expects to see\n u16(0xff01), u16(1), new Uint8Array([0x00])\n );\n\n const body = concatBytes(\n u16(TLS_1_2),\n this.clientRandom,\n new Uint8Array([0]), // no session id\n u16(4), u16(0x009c), u16(0x009d), // cipher suites\n new Uint8Array([1, 0]), // compression: null\n u16(extensions.length), extensions\n );\n\n await this.#sendHandshake(HANDSHAKE.CLIENT_HELLO, body);\n return this.handshakeDone;\n }\n\n #record(type, payload: Uint8Array) {\n return concatBytes(new Uint8Array([type]), u16(TLS_1_2), u16(payload.length), payload);\n }\n\n async #sendHandshake(type, body: Uint8Array) {\n const message = concatBytes(new Uint8Array([type]), u24(body.length), body);\n this.transcript.push(message);\n await this.#sendRecord(RECORD.HANDSHAKE, message);\n }\n\n async #sendRecord(type, payload: Uint8Array) {\n if (!this.writeEncrypted) {\n this.sendRaw(this.#record(type, payload));\n return;\n }\n this.sendRaw(this.#record(type, await this.#seal(type, payload)));\n }\n\n /** AES-GCM record protection (RFC 5288): explicit nonce, then ciphertext. */\n async #seal(type, plaintext: Uint8Array) {\n const sequence = this.writeSequence++;\n const explicitNonce = u64(sequence);\n const iv = concatBytes(this.keys.clientIv, explicitNonce);\n const aad = concatBytes(u64(sequence), new Uint8Array([type]), u16(TLS_1_2), u16(plaintext.length));\n\n const sealed = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv, additionalData: aad, tagLength: 128 },\n this.keys.clientKey,\n plaintext\n );\n\n return concatBytes(explicitNonce, new Uint8Array(sealed));\n }\n\n async #open(type, fragment) {\n const sequence = this.readSequence++;\n const explicitNonce = fragment.subarray(0, 8);\n const ciphertext = fragment.subarray(8);\n const iv = concatBytes(this.keys.serverIv, explicitNonce);\n const aad = concatBytes(\n u64(sequence),\n new Uint8Array([type]),\n u16(TLS_1_2),\n u16(ciphertext.length - 16)\n );\n\n const opened = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv, additionalData: aad, tagLength: 128 },\n this.keys.serverKey,\n ciphertext\n );\n\n return new Uint8Array(opened);\n }\n\n async #deriveKeys(preMasterSecret) {\n const { hash, keyLength } = this.suite;\n\n this.masterSecret = await prf(\n hash,\n preMasterSecret,\n 'master secret',\n concatBytes(this.clientRandom, this.serverRandom),\n 48\n );\n\n // AEAD suites need two keys and two 4-byte salts, and no MAC keys.\n const keyBlock = await prf(\n hash,\n this.masterSecret,\n 'key expansion',\n concatBytes(this.serverRandom, this.clientRandom),\n keyLength * 2 + 8\n );\n\n const reader = new Reader(keyBlock);\n const clientKeyBytes = reader.raw(keyLength);\n const serverKeyBytes = reader.raw(keyLength);\n\n this.keys = {\n clientKey: await crypto.subtle.importKey('raw', clientKeyBytes, 'AES-GCM', false, ['encrypt']),\n serverKey: await crypto.subtle.importKey('raw', serverKeyBytes, 'AES-GCM', false, ['decrypt']),\n clientIv: reader.raw(4).slice(),\n serverIv: reader.raw(4).slice(),\n };\n }\n\n async #transcriptHash() {\n const digest = await crypto.subtle.digest(this.suite.hash, concatBytes(...this.transcript));\n return new Uint8Array(digest);\n }\n\n async #verifyData(label: string) {\n return prf(this.suite.hash, this.masterSecret, label, await this.#transcriptHash(), 12);\n }\n\n // --- inbound --------------------------------------------------------------\n\n /** Feed transport bytes. Calls are serialized, so order is preserved. */\n receive(bytes: Uint8Array) {\n this.queue = this.queue\n .then(() => this.#consume(bytes))\n .catch((error: unknown) => this.#fail(error));\n return this.queue;\n }\n\n #fail(error: unknown) {\n const failure = error instanceof Error ? error : new Error(String(error));\n this.rejectHandshake(failure);\n if (this.onError) this.onError(failure);\n }\n\n async #consume(bytes: Uint8Array) {\n this.inbound = this.inbound.length === 0 ? bytes : concatBytes(this.inbound, bytes);\n\n while (this.inbound.length >= 5) {\n const type = this.inbound[0];\n const length = (this.inbound[3] << 8) | this.inbound[4];\n if (this.inbound.length < 5 + length) return;\n\n const fragment = this.inbound.subarray(5, 5 + length);\n this.inbound = this.inbound.subarray(5 + length).slice();\n\n const payload = this.readEncrypted && type !== RECORD.CHANGE_CIPHER_SPEC\n ? await this.#open(type, fragment)\n : fragment;\n\n await this.#handleRecord(type, payload);\n }\n }\n\n async #handleRecord(type, payload: Uint8Array) {\n if (type === RECORD.ALERT) {\n const level = payload[0];\n const description = payload[1];\n const name = ALERT_DESCRIPTIONS[description] || `alert ${description}`;\n if (name === 'close_notify') {\n this.established = false;\n return;\n }\n throw new Error(`TLS: the host sent a fatal alert: ${name}${level === 1 ? ' (warning)' : ''}`);\n }\n\n if (type === RECORD.CHANGE_CIPHER_SPEC) {\n // Everything the host sends after this point is encrypted.\n this.readEncrypted = true;\n this.readSequence = 0n;\n return;\n }\n\n if (type === RECORD.APPLICATION_DATA) {\n if (this.onData && payload.length > 0) this.onData(payload);\n return;\n }\n\n if (type !== RECORD.HANDSHAKE) return;\n\n // One record can carry several handshake messages.\n let offset = 0;\n while (offset + 4 <= payload.length) {\n const messageType = payload[offset];\n const length = (payload[offset + 1] << 16) | (payload[offset + 2] << 8) | payload[offset + 3];\n const message = payload.subarray(offset, offset + 4 + length);\n const body = payload.subarray(offset + 4, offset + 4 + length);\n offset += 4 + length;\n\n // The Finished check covers every handshake message before it, so the\n // transcript must include this one before it is verified.\n if (messageType !== HANDSHAKE.FINISHED) this.transcript.push(message);\n await this.#handleHandshake(messageType, body, message);\n }\n }\n\n async #handleHandshake(type, body: Uint8Array, message: string) {\n switch (type) {\n case HANDSHAKE.SERVER_HELLO: {\n const reader = new Reader(body);\n const version = reader.u16be();\n if (version !== TLS_1_2) {\n throw new Error(`TLS: the host chose an unsupported version 0x${version.toString(16)}`);\n }\n this.serverRandom = reader.raw(32).slice();\n reader.raw(reader.u8()); // session id\n const suiteId = reader.u16be();\n this.suite = CIPHER_SUITES[suiteId];\n if (!this.suite) {\n throw new Error(`TLS: the host chose cipher suite 0x${suiteId.toString(16)}, which this client does not implement`);\n }\n reader.u8(); // compression method\n return;\n }\n\n case HANDSHAKE.CERTIFICATE: {\n const reader = new Reader(body);\n const totalLength = (reader.u8() << 16) | reader.u16be();\n const end = reader.offset + totalLength;\n while (reader.offset < end) {\n const certificateLength = (reader.u8() << 16) | reader.u16be();\n this.certificates.push(reader.raw(certificateLength).slice());\n }\n if (this.certificates.length === 0) throw new Error('TLS: the host sent no certificate');\n\n // The leaf comes first, and its key is what CredSSP will bind to.\n this.serverPublicKey = parseX509PublicKey(this.certificates[0]);\n return;\n }\n\n case HANDSHAKE.CERTIFICATE_REQUEST:\n this.certificateRequested = true;\n return;\n\n case HANDSHAKE.SERVER_KEY_EXCHANGE:\n throw new Error('TLS: the host chose an ephemeral key exchange, which this client does not implement');\n\n case HANDSHAKE.SERVER_HELLO_DONE:\n await this.#finishClientHandshake();\n return;\n\n case HANDSHAKE.NEW_SESSION_TICKET:\n return;\n\n case HANDSHAKE.FINISHED: {\n const expected = await this.#verifyData('server finished');\n const actual = body.subarray(0, 12);\n const matches = expected.length === actual.length && expected.every((byte, i) => byte === actual[i]);\n if (!matches) throw new Error('TLS: the host Finished message did not verify');\n\n this.transcript.push(message);\n this.established = true;\n this.resolveHandshake({\n cipherSuite: this.suite.name,\n certificates: this.certificates,\n publicKey: this.serverPublicKey,\n });\n return;\n }\n\n default:\n return;\n }\n }\n\n async #finishClientHandshake() {\n if (this.certificateRequested) {\n // An empty certificate list is the legal way to decline.\n await this.#sendHandshake(HANDSHAKE.CERTIFICATE, u24(0));\n }\n\n // The premaster secret starts with the version the client first offered,\n // which the host checks to detect a downgrade.\n const preMasterSecret = new Uint8Array(48);\n crypto.getRandomValues(preMasterSecret);\n preMasterSecret[0] = 0x03;\n preMasterSecret[1] = 0x03;\n\n const encrypted = rsaEncryptPkcs1(\n preMasterSecret,\n this.serverPublicKey.modulus,\n this.serverPublicKey.exponent\n );\n await this.#sendHandshake(HANDSHAKE.CLIENT_KEY_EXCHANGE, concatBytes(u16(encrypted.length), encrypted));\n\n await this.#deriveKeys(preMasterSecret);\n\n await this.#sendRecord(RECORD.CHANGE_CIPHER_SPEC, new Uint8Array([1]));\n this.writeEncrypted = true;\n this.writeSequence = 0n;\n\n await this.#sendHandshake(HANDSHAKE.FINISHED, await this.#verifyData('client finished'));\n }\n\n // --- outbound -------------------------------------------------------------\n\n /** Send application data once the handshake has completed. */\n write(plaintext: Uint8Array) {\n this.queue = this.queue\n .then(async () => {\n if (!this.established) throw new Error('TLS: write before the handshake finished');\n await this.#sendRecord(RECORD.APPLICATION_DATA, plaintext);\n })\n .catch((error: unknown) => this.#fail(error));\n return this.queue;\n }\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/protocols/rdp/vchannel.ts"],
4
- "sourcesContent": ["// Static virtual channel framing (MS-RDPBCGR 2.2.6.1).\n//\n// A virtual channel PDU is a CHANNEL_PDU_HEADER followed by data, split across\n// as many MCS send-data PDUs as the chunk size requires. The header repeats the\n// total length in every chunk, so the receiver knows how much to expect before\n// the last one arrives.\n\nimport { Reader, Writer, concatBytes } from './buffer';\n\n/** Channel option bits used when the channel is declared in Client Network Data. */\nexport const CHANNEL_OPTION = {\n INITIALIZED: 0x80000000,\n ENCRYPT_RDP: 0x40000000,\n COMPRESS_RDP: 0x00800000,\n SHOW_PROTOCOL: 0x00200000,\n};\n\nconst CHANNEL_FLAG_FIRST = 0x00000001;\nconst CHANNEL_FLAG_LAST = 0x00000002;\nexport const CHANNEL_FLAG_SHOW_PROTOCOL = 0x00000010;\n\n/** What RDP itself uses; a host may accept more, but never needs to. */\nexport const CHANNEL_CHUNK_LENGTH = 1600;\n\n/**\n * Split one logical channel message into wire chunks, each already carrying its\n * CHANNEL_PDU_HEADER. An empty message still produces one chunk, because the\n * header is what tells the host anything happened at all.\n */\nexport function chunkChannelData(data: Uint8Array, chunkSize = CHANNEL_CHUNK_LENGTH, extraFlags = 0) {\n const chunks = [];\n\n for (let offset = 0; offset < data.length || offset === 0; offset += chunkSize) {\n const slice = data.subarray(offset, offset + chunkSize);\n let flags = extraFlags;\n if (offset === 0) flags |= CHANNEL_FLAG_FIRST;\n if (offset + chunkSize >= data.length) flags |= CHANNEL_FLAG_LAST;\n\n const writer = new Writer(slice.length + 8);\n writer.u32le(data.length).u32le(flags).raw(slice);\n chunks.push(writer.take());\n\n if (data.length === 0) break;\n }\n\n return chunks;\n}\n\n/**\n * Reassemble inbound chunks into whole messages. A chunk that arrives without a\n * preceding FIRST is treated as its own message rather than dropped: some hosts\n * send small PDUs with both flags set and others with neither.\n */\nexport class ChannelReassembler {\n private parts: Uint8Array[];\n private expectedLength: number;\n\n constructor() {\n this.parts = [];\n this.expectedLength = 0;\n }\n\n /** Returns the completed message, or null while more chunks are pending. */\n push(bytes: Uint8Array) {\n if (bytes.length < 8) return null;\n\n const reader = new Reader(bytes);\n const totalLength = reader.u32le();\n const flags = reader.u32le();\n const data = reader.rest();\n\n if (flags & CHANNEL_FLAG_FIRST) {\n this.parts = [];\n this.expectedLength = totalLength;\n }\n\n this.parts.push(data.slice());\n\n const complete = (flags & CHANNEL_FLAG_LAST) !== 0;\n if (!complete) return null;\n\n const message = concatBytes(...this.parts);\n this.parts = [];\n // The declared length is authoritative: a final chunk may be padded.\n return this.expectedLength && this.expectedLength < message.length\n ? message.subarray(0, this.expectedLength)\n : message;\n }\n}\n"],
5
- "mappings": "AAOA,SAAS,QAAQ,QAAQ,mBAAmB;AAGrC,MAAM,iBAAiB;AAAA,EAC5B,aAAa;AAAA,EACb,aAAa;AAAA,EACb,cAAc;AAAA,EACd,eAAe;AACjB;AAEA,MAAM,qBAAqB;AAC3B,MAAM,oBAAoB;AACnB,MAAM,6BAA6B;AAGnC,MAAM,uBAAuB;AAO7B,SAAS,iBAAiB,MAAkB,YAAY,sBAAsB,aAAa,GAAG;AACnG,QAAM,SAAS,CAAC;AAEhB,WAAS,SAAS,GAAG,SAAS,KAAK,UAAU,WAAW,GAAG,UAAU,WAAW;AAC9E,UAAM,QAAQ,KAAK,SAAS,QAAQ,SAAS,SAAS;AACtD,QAAI,QAAQ;AACZ,QAAI,WAAW,EAAG,UAAS;AAC3B,QAAI,SAAS,aAAa,KAAK,OAAQ,UAAS;AAEhD,UAAM,SAAS,IAAI,OAAO,MAAM,SAAS,CAAC;AAC1C,WAAO,MAAM,KAAK,MAAM,EAAE,MAAM,KAAK,EAAE,IAAI,KAAK;AAChD,WAAO,KAAK,OAAO,KAAK,CAAC;AAEzB,QAAI,KAAK,WAAW,EAAG;AAAA,EACzB;AAEA,SAAO;AACT;AAOO,MAAM,mBAAmB;AAAA,EACtB;AAAA,EACA;AAAA,EAER,cAAc;AACZ,SAAK,QAAQ,CAAC;AACd,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA,EAGA,KAAK,OAAmB;AACtB,QAAI,MAAM,SAAS,EAAG,QAAO;AAE7B,UAAM,SAAS,IAAI,OAAO,KAAK;AAC/B,UAAM,cAAc,OAAO,MAAM;AACjC,UAAM,QAAQ,OAAO,MAAM;AAC3B,UAAM,OAAO,OAAO,KAAK;AAEzB,QAAI,QAAQ,oBAAoB;AAC9B,WAAK,QAAQ,CAAC;AACd,WAAK,iBAAiB;AAAA,IACxB;AAEA,SAAK,MAAM,KAAK,KAAK,MAAM,CAAC;AAE5B,UAAM,YAAY,QAAQ,uBAAuB;AACjD,QAAI,CAAC,SAAU,QAAO;AAEtB,UAAM,UAAU,YAAY,GAAG,KAAK,KAAK;AACzC,SAAK,QAAQ,CAAC;AAEd,WAAO,KAAK,kBAAkB,KAAK,iBAAiB,QAAQ,SACxD,QAAQ,SAAS,GAAG,KAAK,cAAc,IACvC;AAAA,EACN;AACF;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/protocols/rdp/x224.ts"],
4
- "sourcesContent": ["// TPKT (RFC 1006) and X.224 framing, plus the RDP security negotiation that\n// rides on the very first exchange (MS-RDPBCGR 2.2.1.1 / 2.2.1.2).\n\nimport { Reader, Writer } from './buffer';\n\nexport const TPKT_VERSION = 0x03;\nexport const TPKT_HEADER_LENGTH = 4;\n\nconst X224_CONNECTION_REQUEST = 0xe0;\nconst X224_CONNECTION_CONFIRM = 0xd0;\nconst X224_DATA = 0xf0;\n\nconst RDP_NEG_REQ = 0x01;\nconst RDP_NEG_RSP = 0x02;\nconst RDP_NEG_FAILURE = 0x03;\n\nexport const NEG_FAILURE = {\n SSL_REQUIRED: 0x00000001,\n SSL_NOT_ALLOWED: 0x00000002,\n SSL_CERT_NOT_ON_SERVER: 0x00000003,\n INCONSISTENT_FLAGS: 0x00000004,\n HYBRID_REQUIRED: 0x00000005,\n SSL_WITH_USER_AUTH_REQUIRED: 0x00000006,\n};\n\nexport const NEG_PROTOCOL = {\n RDP: 0x00000000,\n SSL: 0x00000001,\n HYBRID: 0x00000002,\n RDSTLS: 0x00000004,\n HYBRID_EX: 0x00000008,\n};\n\nconst NEG_FAILURE_MESSAGES = {\n 0x00000001: 'The host requires TLS. Choose the TLS security option for this target.',\n 0x00000002: 'The host does not allow TLS. Choose the \"RDP (standard)\" security option.',\n 0x00000003: 'The host has no certificate installed, so it cannot negotiate TLS.',\n 0x00000004: 'The host rejected the requested protocol flags as inconsistent.',\n 0x00000005: 'The host requires Network Level Authentication (NLA/CredSSP). Turn NLA off on the host, or set its security layer to \"RDP\".',\n 0x00000006: 'The host requires TLS with early user authentication, which this gateway does not support.',\n};\n\n/** X.224 Connection Request, carrying the protocols this client will accept. */\nexport function connectionRequest(requestedProtocols: number) {\n const writer = new Writer(32);\n writer.u8(TPKT_VERSION).u8(0).u16be(19);\n writer.u8(14).u8(X224_CONNECTION_REQUEST).u16be(0).u16be(0).u8(0);\n writer.u8(RDP_NEG_REQ).u8(0).u16le(8).u32le(requestedProtocols);\n return writer.take();\n}\n\n/**\n * Read the X.224 Connection Confirm. Throws with the host's own explanation\n * when it refuses the requested protocol.\n */\nexport function parseConnectionConfirm(frame: Uint8Array) {\n const reader = new Reader(frame);\n reader.skip(TPKT_HEADER_LENGTH);\n reader.u8();\n const code = reader.u8();\n if (code !== X224_CONNECTION_CONFIRM) {\n throw new Error(`RDP: expected an X.224 connection confirm, got TPDU code 0x${code.toString(16)}`);\n }\n reader.skip(5); // dst-ref, src-ref, class\n\n // Servers that predate the negotiation extension simply stop here, which\n // means \"plain RDP security, as asked\".\n if (reader.remaining < 8) return { selectedProtocol: NEG_PROTOCOL.RDP, negotiated: false };\n\n const type = reader.u8();\n reader.u8();\n reader.u16le();\n\n if (type === RDP_NEG_FAILURE) {\n const failureCode = reader.u32le();\n const error = new Error(\n NEG_FAILURE_MESSAGES[failureCode] ||\n `The host refused the RDP connection (negotiation failure 0x${failureCode.toString(16)}).`\n );\n // A client negotiating automatically needs the code, not just the sentence.\n error.negotiationFailure = failureCode;\n throw error;\n }\n if (type !== RDP_NEG_RSP) {\n return { selectedProtocol: NEG_PROTOCOL.RDP, negotiated: false };\n }\n\n return { selectedProtocol: reader.u32le(), negotiated: true };\n}\n\n/** Wrap a payload in TPKT + X.224 Data. */\nexport function dataFrame(payload: Uint8Array) {\n const writer = new Writer(payload.length + 7);\n writer.u8(TPKT_VERSION).u8(0).u16be(payload.length + 7);\n writer.u8(2).u8(X224_DATA).u8(0x80); // li, DT code, EOT\n writer.raw(payload);\n return writer.take();\n}\n\n/** Strip TPKT + X.224 Data headers from a complete frame. */\nexport function dataPayload(frame: Uint8Array) {\n if (frame.length < 7) throw new Error('RDP: X.224 data frame is too short');\n return frame.subarray(7);\n}\n\n/**\n * Split a byte stream into whole frames. RDP interleaves two framings on one\n * connection: TPKT (first byte 0x03) and fast-path output (any other value),\n * each with its own length encoding.\n */\nexport function splitFrames(buffer: Uint8Array) {\n const frames = [];\n let offset = 0;\n\n while (buffer.length - offset >= 4) {\n let frameLength;\n let fastPath = false;\n\n if (buffer[offset] === TPKT_VERSION) {\n frameLength = (buffer[offset + 2] << 8) | buffer[offset + 3];\n if (frameLength < TPKT_HEADER_LENGTH) {\n throw new Error('RDP: malformed TPKT length');\n }\n } else {\n fastPath = true;\n // Fast-path length is 1 or 2 bytes; the high bit of the first marks which.\n frameLength = buffer[offset + 1] & 0x80\n ? ((buffer[offset + 1] & 0x7f) << 8) | buffer[offset + 2]\n : buffer[offset + 1];\n if (frameLength < 2) throw new Error('RDP: malformed fast-path length');\n }\n\n if (buffer.length - offset < frameLength) break;\n frames.push({ fastPath, frame: buffer.subarray(offset, offset + frameLength) });\n offset += frameLength;\n }\n\n return { frames, rest: buffer.subarray(offset) };\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/protocols/ssh/kex.ts"],
4
- "sourcesContent": ["// Key exchange and host-key verification.\n//\n// Every algorithm here is one the Web Crypto API implements, because the whole\n// SSH client runs in the browser: X25519 and NIST P-256 for the exchange, and\n// Ed25519, RSA or ECDSA to check the host's signature over it. Nothing is\n// hand-rolled, and nothing that cannot be verified is offered.\n\nimport { SshReader, SshWriter, concatBytes, padStart, toBase64, toBase64Url } from './wire';\n\nconst subtle = globalThis.crypto?.subtle;\n\n/** Key exchange methods, best first. All of them hash with SHA-256. */\nexport const KEX_ALGORITHMS = {\n 'curve25519-sha256': { type: 'x25519', hash: 'SHA-256' },\n 'curve25519-sha256@libssh.org': { type: 'x25519', hash: 'SHA-256' },\n 'ecdh-sha2-nistp256': { type: 'ecdh', curve: 'P-256', hash: 'SHA-256' },\n};\n\n/**\n * Host key signature algorithms. `keyType` is what appears inside the key blob;\n * a modern RSA key is signed with SHA-256 or SHA-512 but still calls itself\n * ssh-rsa, so the two names differ there.\n */\nexport const HOST_KEY_ALGORITHMS = {\n 'ssh-ed25519': { keyType: 'ssh-ed25519', kind: 'ed25519' },\n 'rsa-sha2-512': { keyType: 'ssh-rsa', kind: 'rsa', hash: 'SHA-512' },\n 'rsa-sha2-256': { keyType: 'ssh-rsa', kind: 'rsa', hash: 'SHA-256' },\n 'ecdsa-sha2-nistp256': { keyType: 'ecdsa-sha2-nistp256', kind: 'ecdsa', curve: 'P-256', hash: 'SHA-256' },\n};\n\nasync function supports(algorithm, usages) {\n if (!subtle) return false;\n try {\n await subtle.generateKey(algorithm, false, usages);\n return true;\n } catch {\n return false;\n }\n}\n\nlet cachedOffer = null;\n\n/**\n * What this browser can actually do. X25519 and Ed25519 are recent additions to\n * Web Crypto, so they are probed rather than assumed; the NIST curve and RSA\n * have been there all along and always keep the session possible.\n */\nexport async function negotiableAlgorithms() {\n if (cachedOffer) return cachedOffer;\n\n const [x25519, ed25519] = await Promise.all([\n supports({ name: 'X25519' }, ['deriveBits']),\n supports({ name: 'Ed25519' }, ['sign', 'verify']),\n ]);\n\n const kex = [];\n if (x25519) kex.push('curve25519-sha256', 'curve25519-sha256@libssh.org');\n kex.push('ecdh-sha2-nistp256');\n\n const hostKey = [];\n if (ed25519) hostKey.push('ssh-ed25519');\n hostKey.push('rsa-sha2-512', 'rsa-sha2-256', 'ecdsa-sha2-nistp256');\n\n cachedOffer = { kex, hostKey, x25519, ed25519 };\n return cachedOffer;\n}\n\n/** The client half of one key exchange. */\nexport async function createKexClient(algorithmName: string) {\n const algorithm = KEX_ALGORITHMS[algorithmName];\n if (!algorithm) throw new Error(`SSH: unsupported key exchange ${algorithmName}`);\n\n if (algorithm.type === 'x25519') {\n const pair = await subtle.generateKey({ name: 'X25519' }, false, ['deriveBits']);\n const publicKey = new Uint8Array(await subtle.exportKey('raw', pair.publicKey));\n return {\n hash: algorithm.hash,\n publicKey,\n async sharedSecret(serverPublicKey) {\n if (serverPublicKey.length !== 32) throw new Error('SSH: the host sent a malformed X25519 key.');\n const peer = await subtle.importKey('raw', serverPublicKey, { name: 'X25519' }, false, []);\n const secret = new Uint8Array(await subtle.deriveBits({ name: 'X25519', public: peer }, pair.privateKey, 256));\n if (secret.every((byte) => byte === 0)) {\n throw new Error('SSH: the host sent a degenerate X25519 key.');\n }\n return secret;\n },\n };\n }\n\n const pair = await subtle.generateKey({ name: 'ECDH', namedCurve: algorithm.curve }, false, ['deriveBits']);\n const publicKey = new Uint8Array(await subtle.exportKey('raw', pair.publicKey));\n return {\n hash: algorithm.hash,\n publicKey,\n async sharedSecret(serverPublicKey) {\n const peer = await subtle.importKey(\n 'raw', serverPublicKey, { name: 'ECDH', namedCurve: algorithm.curve }, false, []\n );\n return new Uint8Array(await subtle.deriveBits({ name: 'ECDH', public: peer }, pair.privateKey, 256));\n },\n };\n}\n\n/** The key type named inside a host key blob, which the algorithm must match. */\nexport function hostKeyType(keyBlob: Uint8Array) {\n return new SshReader(keyBlob).string();\n}\n\n/** The SHA256:\u2026 fingerprint OpenSSH prints, so a user can compare it. */\nexport async function hostKeyFingerprint(keyBlob: Uint8Array) {\n const digest = new Uint8Array(await subtle.digest('SHA-256', keyBlob));\n return `SHA256:${toBase64(digest).replace(/=+$/, '')}`;\n}\n\nasync function importHostKey(algorithmName: string, keyBlob: Uint8Array) {\n const algorithm = HOST_KEY_ALGORITHMS[algorithmName];\n if (!algorithm) throw new Error(`SSH: unsupported host key algorithm ${algorithmName}`);\n\n const reader = new SshReader(keyBlob);\n const keyType = reader.string();\n if (keyType !== algorithm.keyType) {\n throw new Error(`SSH: the host key is a ${keyType} but was announced as ${algorithmName}.`);\n }\n\n if (algorithm.kind === 'ed25519') {\n return subtle.importKey('raw', reader.stringBytes(), { name: 'Ed25519' }, false, ['verify']);\n }\n\n if (algorithm.kind === 'rsa') {\n const exponent = reader.mpint();\n const modulus = reader.mpint();\n return subtle.importKey(\n 'jwk',\n { kty: 'RSA', n: toBase64Url(modulus), e: toBase64Url(exponent), ext: true },\n { name: 'RSASSA-PKCS1-v1_5', hash: algorithm.hash },\n false,\n ['verify']\n );\n }\n\n reader.string(); // curve name, already implied by the algorithm\n return subtle.importKey(\n 'raw', reader.stringBytes(), { name: 'ECDSA', namedCurve: algorithm.curve }, false, ['verify']\n );\n}\n\n/** Unwrap the signature blob, which names its own algorithm again. */\nfunction signatureBytes(algorithmName: string, signatureBlob: Uint8Array) {\n const reader = new SshReader(signatureBlob);\n const named = reader.string();\n if (named !== algorithmName) {\n throw new Error(`SSH: the host signed with ${named} after agreeing on ${algorithmName}.`);\n }\n\n const raw = reader.stringBytes();\n if (HOST_KEY_ALGORITHMS[algorithmName]?.kind !== 'ecdsa') return raw;\n\n // ECDSA signatures arrive as two mpints and Web Crypto wants them as a fixed\n // width pair.\n const parts = new SshReader(raw);\n const r = parts.mpint();\n const s = parts.mpint();\n return concatBytes(padStart(r, 32), padStart(s, 32));\n}\n\n/**\n * Check that the host owns the key it presented, by verifying its signature\n * over the exchange hash. A failure here is fatal: it is what stands between\n * the session and a machine in the middle of it.\n */\nexport async function verifyHostKeySignature({ algorithm, keyBlob, signatureBlob, exchangeHash }) {\n const { kind, hash } = HOST_KEY_ALGORITHMS[algorithm];\n const key = await importHostKey(algorithm, keyBlob);\n const signature = signatureBytes(algorithm, signatureBlob);\n\n // Only ECDSA needs the digest named at verification time; the other two\n // carry it on the key itself.\n const parameters = kind === 'ecdsa'\n ? { name: 'ECDSA', hash }\n : kind === 'rsa' ? { name: 'RSASSA-PKCS1-v1_5' } : { name: 'Ed25519' };\n\n return subtle.verify(parameters, key, signature, exchangeHash);\n}\n\n/**\n * The exchange hash H (RFC 4253 section 8), which both sides compute from the\n * same inputs and the host signs.\n */\nexport async function exchangeHash({\n hash,\n clientVersion,\n serverVersion,\n clientKexInit,\n serverKexInit,\n hostKeyBlob,\n clientPublicKey,\n serverPublicKey,\n sharedSecret,\n}) {\n const writer = new SshWriter(1024);\n writer.string(clientVersion);\n writer.string(serverVersion);\n writer.string(clientKexInit);\n writer.string(serverKexInit);\n writer.string(hostKeyBlob);\n writer.string(clientPublicKey);\n writer.string(serverPublicKey);\n writer.mpint(sharedSecret);\n return new Uint8Array(await subtle.digest(hash, writer.take()));\n}\n\n/**\n * Derive one key from the exchange (RFC 4253 section 7.2), extending it with\n * further hash rounds when the cipher wants more bytes than one digest.\n */\nexport async function deriveKey({ hash, sharedSecret, exchangeHash: h, letter, sessionId, length }) {\n const prefix = new SshWriter(64 + h.length).mpint(sharedSecret).take();\n const seed = concatBytes(prefix, h, new Uint8Array([letter.charCodeAt(0)]), sessionId);\n\n let material = new Uint8Array(await subtle.digest(hash, seed));\n while (material.length < length) {\n const extension = new Uint8Array(await subtle.digest(hash, concatBytes(prefix, h, material)));\n material = concatBytes(material, extension);\n }\n return material.subarray(0, length);\n}\n"],
5
- "mappings": "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",
6
- "names": ["pair", "publicKey", "exchangeHash"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/protocols/ssh/messages.ts"],
4
- "sourcesContent": ["// SSH message numbers and the human wording for a disconnect (RFC 4250, 4253).\n\nexport const MSG = {\n DISCONNECT: 1,\n IGNORE: 2,\n UNIMPLEMENTED: 3,\n DEBUG: 4,\n SERVICE_REQUEST: 5,\n SERVICE_ACCEPT: 6,\n EXT_INFO: 7,\n KEXINIT: 20,\n NEWKEYS: 21,\n KEX_ECDH_INIT: 30,\n KEX_ECDH_REPLY: 31,\n USERAUTH_REQUEST: 50,\n USERAUTH_FAILURE: 51,\n USERAUTH_SUCCESS: 52,\n USERAUTH_BANNER: 53,\n USERAUTH_INFO_REQUEST: 60,\n USERAUTH_INFO_RESPONSE: 61,\n GLOBAL_REQUEST: 80,\n REQUEST_SUCCESS: 81,\n REQUEST_FAILURE: 82,\n CHANNEL_OPEN: 90,\n CHANNEL_OPEN_CONFIRMATION: 91,\n CHANNEL_OPEN_FAILURE: 92,\n CHANNEL_WINDOW_ADJUST: 93,\n CHANNEL_DATA: 94,\n CHANNEL_EXTENDED_DATA: 95,\n CHANNEL_EOF: 96,\n CHANNEL_CLOSE: 97,\n CHANNEL_REQUEST: 98,\n CHANNEL_SUCCESS: 99,\n CHANNEL_FAILURE: 100,\n};\n\n/** SSH_EXTENDED_DATA_STDERR. */\nexport const EXTENDED_DATA_STDERR = 1;\n\nexport const DISCONNECT_REASON = {\n 1: 'the host reported a protocol error',\n 2: 'the host could not agree on a key exchange method',\n 3: 'the key exchange failed',\n 5: 'the host ran out of resources',\n 6: 'the connection was lost below SSH',\n 7: 'the host application ended the connection',\n 8: 'too many connections',\n 9: 'authentication was cancelled by the user',\n 10: 'no more authentication methods are available',\n 11: 'the host closed the connection',\n 12: 'authentication was cancelled by the host',\n 14: 'the host refused the authentication attempt',\n 15: 'the host closed the connection while authenticating',\n};\n"],
5
- "mappings": "AAEO,MAAM,MAAM;AAAA,EACjB,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,OAAO;AAAA,EACP,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,uBAAuB;AAAA,EACvB,wBAAwB;AAAA,EACxB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,2BAA2B;AAAA,EAC3B,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,cAAc;AAAA,EACd,uBAAuB;AAAA,EACvB,aAAa;AAAA,EACb,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AACnB;AAGO,MAAM,uBAAuB;AAE7B,MAAM,oBAAoB;AAAA,EAC/B,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/protocols/ssh/session.ts"],
4
- "sourcesContent": ["// An SSH session: authentication, one interactive shell channel, and the\n// events a terminal needs to render it.\n//\n// The transport below this owns the cryptography. This module owns the\n// conversation: proving who the user is, asking for a pseudo-terminal, and\n// moving bytes between the shell and the screen.\n\nimport { MSG, EXTENDED_DATA_STDERR } from './messages';\nimport { SshReader, SshWriter, decodeUtf8, encodeUtf8, toBase64 } from './wire';\nimport { SshTransport } from './transport';\n\nexport const DEFAULT_PORT = 22;\n\nconst SERVICE = 'ssh-connection';\nconst TERMINAL_TYPE = 'xterm-256color';\n\n// A shell is not a bulk transfer, so a modest window costs nothing and is\n// topped up long before it runs out.\nconst INITIAL_WINDOW = 2 * 1024 * 1024;\nconst MAX_PACKET = 32 * 1024;\nconst WINDOW_REFILL_THRESHOLD = INITIAL_WINDOW / 2;\n\n/** Terminal modes: only the terminator, so the host keeps its own defaults. */\nconst PTY_MODES = new Uint8Array([0]);\n\n/**\n * How patient `runCommand` is, in three different senses.\n *\n * A single deadline cannot tell \"the shell ignored me\" apart from \"the\n * installer is three minutes into a download\", and a short one turns every\n * real piece of work -- a package install, a build, a large copy -- into a\n * false failure that also leaves the command running on the far side.\n *\n * So the wait is judged by what the shell is doing. It has START_MS to\n * acknowledge the command at all; after that it may take as long as it likes\n * as long as it keeps printing, and only silence longer than IDLE_MS, or a run\n * longer than MAX_MS, is treated as stuck.\n */\nexport const COMMAND_START_MS = 20_000;\nexport const COMMAND_IDLE_MS = 5 * 60_000;\nexport const COMMAND_MAX_MS = 60 * 60_000;\n\n/** Nothing for the terminal to draw, while the framing is being hidden. */\nconst NO_BYTES = new Uint8Array(0);\n\n/**\n * One shell word holding `value` literally.\n *\n * Single quotes suspend every kind of expansion the shell has, so the only\n * character that needs handling is the closing quote itself: the string is left\n * for a moment, an escaped quote is added, and it starts again.\n */\nfunction singleQuote(value) {\n return `'${String(value).replace(/'/g, `'\\\\''`)}'`;\n}\n\n/**\n * The shell a machine answers with, guessed from its prompt.\n *\n * Windows OpenSSH hands out cmd.exe by default, and a POSIX-framed command\n * there does not fail loudly -- it prints `'printf' is not recognized` and\n * leaves the caller waiting for markers that will never come.\n */\nfunction shellFromPrompt(text: string) {\n const tail = text.slice(-400);\n if (/(?:^|\\n)PS [A-Za-z]:\\\\[^\\n]*>\\s*$/.test(tail)) return 'powershell';\n if (/(?:^|\\n)[^\\n]*[A-Za-z]:\\\\[^\\n]*>\\s*$/.test(tail)) return 'cmd';\n return '';\n}\n\n/** A shell saying it has never heard of the command that frames the output. */\nconst FRAMING_REJECTED = /is not recognized as an internal or external command|command not found|not recognized as the name of a cmdlet|CommandNotFoundException/i;\n\n/**\n * The one line that runs `command` and marks where its output starts and ends.\n *\n * Each shell gets its own, because none of the three agree on how to print a\n * string, run a command and read back its exit status.\n */\nexport function frameCommand(family, command: string, id) {\n if (family === 'cmd') {\n // `&` chains unconditionally, and the delayed-expansion problem is dodged\n // by reading ERRORLEVEL through `call`, which re-expands its arguments.\n return `echo.&echo CCB${id}&${command}&call echo.CCE${id} %^ERRORLEVEL%\\r`;\n }\n\n if (family === 'powershell') {\n // A base64 payload keeps a trailing comment, newline or semicolon from\n // swallowing the closing marker. Dot-sourcing the resulting script block\n // also keeps Set-Location and environment changes in this same shell.\n //\n // $LASTEXITCODE is read before $?, because the two answer different\n // questions and only the first one is about the command. $? says whether\n // the dot-source raised an error record, and launching a program that goes\n // on to fail is not an error record: a failing `git`, `msiexec` or nested\n // `powershell.exe` leaves $? True and its own status in $LASTEXITCODE, so\n // asking $? first reports a failed command as exit 0. $LASTEXITCODE is\n // cleared just above, so a non-null value can only have come from a\n // program this command ran, and $? still decides for a payload that ended\n // in a cmdlet and never set it.\n const encoded = toBase64(encodeUtf8(command));\n return `Write-Output \"\"; Write-Output (\"CCB\" + \"${id}\"); `\n + `$__ccCode = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encoded}')); `\n + `$global:LASTEXITCODE = $null; . ([ScriptBlock]::Create($__ccCode)); $__ccOk = $?; `\n + `$__cc = if ($LASTEXITCODE -ne $null) { $LASTEXITCODE } elseif ($__ccOk) { 0 } else { 1 }; `\n + `Write-Output (\"CCE\" + \"${id} \" + $__cc)\\r`;\n }\n\n // `printf '%s%s'` joins the marker inside the shell; `__cc` holds the status\n // so the command's own exit code survives the second printf.\n //\n // It all goes on one line, because an interactive shell echoes each line as\n // it reads it: a second line would be echoed after the command had already\n // printed, landing inside the captured output rather than in front of the\n // opening marker where it can be discarded.\n //\n // The command travels through `eval` as a single quoted word rather than\n // being spliced between the semicolons directly. Spliced in, three ordinary\n // commands break the framing: one ending in `&` puts `&` next to `;`, which\n // is a syntax error; one ending in a newline leaves a bare `;`, likewise; and\n // one ending in `# a comment` swallows the closing marker, so the call only\n // returns when it times out. Inside `eval` the command is a string, so none\n // of it can reach the outer line. `eval` runs in this same shell, so the\n // working directory, the environment and `$?` all still behave as if the\n // user had typed it.\n return `printf '\\\\n%s%s\\\\n' 'CCB' '${id}'; eval ${singleQuote(command)}; `\n + `__cc=$?; printf '\\\\n%s%s %s\\\\n' 'CCE' '${id}' \"$__cc\"\\r`;\n}\n\n/**\n * Plain text from a shell transcript. An interactive shell dresses its output\n * with colour, cursor moves and title changes, none of which mean anything to\n * a model reading the result.\n */\nfunction stripTerminalCodes(text: string) {\n return text\n // OSC sequences (window titles, and iTerm/VS Code shell integration)\n .replace(/\\u001b\\][^\\u0007\\u001b]*(?:\\u0007|\\u001b\\\\)/g, '')\n // CSI sequences: colour, cursor movement, erase\n .replace(/\\u001b\\[[0-9;?]*[ -/]*[@-~]/g, '')\n // remaining two-character escapes\n .replace(/\\u001b[@-Z\\\\-_]/g, '')\n // carriage returns a terminal would have overwritten in place\n .replace(/\\r\\n/g, '\\n')\n .replace(/\\r/g, '');\n}\n\n/**\n * The same bytes, minus the scaffolding around a tool command.\n *\n * An interactive shell echoes whatever is typed into it, and what\n * `frameCommand` types is a `printf`-and-`eval` line -- or, in PowerShell, a\n * base64 blob. Left alone it buries the command's own output in the\n * scrollback the user is trying to read. The joined markers are safe to cut\n * on for the same reason `runCommand` finds them: the echoed line contains\n * their halves, never the joined marker itself.\n *\n * Only display is filtered. `detail.bytes` stays exactly what the host sent,\n * because that is what `runCommand` reads the result out of.\n */\nfunction filterDisplay(session, bytes: Uint8Array) {\n const filter = session.displayFilter;\n if (!filter) return bytes;\n\n filter.pending += decodeUtf8(bytes);\n let shown = '';\n\n if (filter.stage === 'before') {\n const at = filter.pending.indexOf(filter.begin);\n if (at === -1) return NO_BYTES; // still inside the echoed wrapper\n filter.pending = filter.pending.slice(at + filter.begin.length).replace(/^\\r?\\n/, '');\n filter.stage = 'inside';\n }\n\n if (filter.stage === 'inside') {\n const at = filter.pending.indexOf(filter.end);\n if (at === -1) {\n shown = filter.pending;\n filter.pending = '';\n return shown ? encodeUtf8(shown) : NO_BYTES;\n }\n shown = filter.pending.slice(0, at);\n filter.pending = filter.pending.slice(at);\n filter.stage = 'closing';\n }\n\n // The closing marker carries the exit status on its own line. Drop that\n // line and hand back everything after it: the prompt the shell draws next\n // belongs to the user again.\n const newline = filter.pending.indexOf('\\n');\n if (newline !== -1) {\n shown += filter.pending.slice(newline + 1);\n session.displayFilter = null;\n }\n return shown ? encodeUtf8(shown) : NO_BYTES;\n}\n\n/**\n * Stop filtering, showing anything held back on the way out.\n *\n * Text held before the opening marker is normally the echoed wrapper, but if\n * the command never started it is the shell explaining why -- the one case\n * where the held text is the whole answer, so it must not be swallowed.\n */\nfunction releaseDisplayFilter(session) {\n const filter = session.displayFilter;\n session.displayFilter = null;\n if (!filter || filter.stage !== 'before' || !filter.pending) return;\n session.dispatchEvent(new CustomEvent('data', {\n detail: { bytes: NO_BYTES, display: encodeUtf8(filter.pending), isStderr: false },\n }));\n}\n\nexport class SshSession extends EventTarget {\n /**\n * @param {string} url gateway WebSocket URL\n * @param {object} options\n * @param {string} options.username\n * @param {string} [options.password]\n * @param {'powershell'} [options.preferredShell]\n * @param {(info: object) => Promise<boolean>} [options.verifyHost]\n * @param {(prompt: object) => Promise<string>} [options.requestInput]\n */\n constructor(url: string, options = {}) {\n super();\n\n this.options = options;\n this.username = options.username || '';\n this.password = options.password || '';\n this.requestInput = options.requestInput || (async () => '');\n this.columns = options.columns || 80;\n this.rows = options.rows || 24;\n\n this.channelId = 0;\n this.remoteChannelId = null;\n this.remoteWindow = 0;\n this.remoteMaxPacket = MAX_PACKET;\n this.localWindow = INITIAL_WINDOW;\n this.pendingWrites = [];\n this.shellOpen = false;\n this.commandInFlight = false;\n /** Set while a tool command runs, to keep its framing off the screen. */\n this.displayFilter = null;\n /** True only after the requested interactive shell is ready for commands. */\n this.readySignaled = false;\n this.powerShellAttempted = false;\n this.powerShellTimer = null;\n /** '', 'posix', 'cmd' or 'powershell'; learned from the prompt. */\n this.shellFamily = '';\n this.promptTail = '';\n this.exitStatus = null;\n this.authMethods = [];\n this.closed = false;\n this.waiters = new Map();\n\n this.transport = new SshTransport(url, {\n verifyHost: options.verifyHost || (async () => true),\n openTransport: options.openTransport || null,\n log: (step: number, detail) => console.info(`[SSH] ${step}`, detail),\n });\n\n this.transport.addEventListener('packet', (event) => {\n this.#handlePacket(event.detail.type, event.detail.payload);\n });\n this.transport.addEventListener('greeting', (event) => {\n for (const line of event.detail.lines) this.#emitBanner(`${line}\\r\\n`);\n });\n this.transport.addEventListener('banner', (event) => this.#emitBanner(event.detail.text));\n this.transport.addEventListener('keys', (event) => {\n this.fingerprint = event.detail.fingerprint;\n this.#status('secured', `Host key ${event.detail.fingerprint} verified.`);\n this.dispatchEvent(new CustomEvent('secure', { detail: event.detail }));\n if (event.detail.firstTime) void this.#authenticate();\n });\n this.transport.addEventListener('hostdisconnect', (event) => {\n this.lastMessage = event.detail.reason;\n });\n this.transport.addEventListener('error', (event) => {\n this.lastMessage = event.detail.message;\n this.dispatchEvent(new CustomEvent('error', { detail: event.detail }));\n });\n this.transport.addEventListener('close', (event) => {\n if (this.closed) return;\n this.closed = true;\n clearTimeout(this.powerShellTimer);\n this.#abandonWaiters(event.detail.message || 'The SSH connection closed.');\n this.dispatchEvent(new CustomEvent('close', {\n detail: {\n ...event.detail,\n message: event.detail.message || this.lastMessage || '',\n exitStatus: this.exitStatus,\n },\n }));\n });\n }\n\n connect() {\n this.#status('connecting', 'Opening the SSH transport\u2026');\n this.transport.connect();\n }\n\n disconnect() {\n if (this.closed) return;\n clearTimeout(this.powerShellTimer);\n if (this.shellOpen && this.remoteChannelId !== null) {\n this.transport.send(new SshWriter(8).u8(MSG.CHANNEL_CLOSE).u32(this.remoteChannelId).take());\n }\n this.transport.disconnect('Closed by the user');\n }\n\n /** Send keystrokes, or anything else the terminal produces, to the shell. */\n write(data: Uint8Array) {\n const bytes = typeof data === 'string' ? encodeUtf8(data) : data;\n if (!bytes.length) return;\n this.pendingWrites.push(bytes);\n this.#flushWrites();\n }\n\n /**\n * Run one command in this shell and return what it printed.\n *\n * Deliberately the *same* shell the user is looking at, not a second channel:\n * a command the assistant runs inherits the working directory, the\n * environment and the login the user already has, and it appears in their\n * terminal as it happens. An agent acting on a machine invisibly is worse\n * than an agent that is slower to read.\n *\n * The output is framed by markers rather than measured, because an\n * interactive shell echoes its input and prints a prompt around it. The\n * markers are assembled by the shell from two pieces, so the echoed command\n * line -- which contains the pieces but not the joined marker -- never looks\n * like the real boundary.\n *\n * A `command` event brackets the run so the terminal can label it, and the\n * three deadlines above decide when a command is stuck rather than slow.\n */\n runCommand(command: string, {\n startMs = COMMAND_START_MS,\n idleMs = COMMAND_IDLE_MS,\n maxMs = COMMAND_MAX_MS,\n } = {}) {\n const text = String(command || '').trim();\n if (!text) return Promise.reject(new Error('No command was given.'));\n if (!this.shellOpen || this.closed) {\n return Promise.reject(new Error('The SSH shell is not open.'));\n }\n if (this.readySignaled === false) {\n return Promise.reject(new Error('The SSH shell is still starting.'));\n }\n if (this.commandInFlight) {\n return Promise.reject(new Error('Another command is still running in this shell.'));\n }\n\n const id = Array.from(crypto.getRandomValues(new Uint8Array(6)))\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n const begin = `CCB${id}`;\n const end = `CCE${id}`;\n // What the prompt showed, or POSIX until something says otherwise.\n const family = this.shellFamily || 'posix';\n\n this.commandInFlight = true;\n const startedAt = Date.now();\n\n // The wrapper is machinery, not something the user asked to read, so the\n // terminal is shown the command's own output instead of the line that\n // frames it. The announcement below is what marks it in the scrollback.\n this.displayFilter = { begin, end, stage: 'before', pending: '' };\n this.dispatchEvent(new CustomEvent('command', {\n detail: { phase: 'start', id, command: text },\n }));\n\n return new Promise((resolve, reject) => {\n let transcript = '';\n let started = false;\n let startTimer = null;\n let idleTimer = null;\n let maxTimer = null;\n\n const finish = (settle) => {\n clearTimeout(startTimer);\n clearTimeout(idleTimer);\n clearTimeout(maxTimer);\n this.removeEventListener('data', onData);\n this.removeEventListener('close', onClose);\n this.commandInFlight = false;\n releaseDisplayFilter(this);\n settle();\n };\n\n /** End the call, and tell the terminal how it went. */\n const done = (result) => finish(() => {\n this.dispatchEvent(new CustomEvent('command', {\n detail: {\n phase: 'end',\n id,\n command: text,\n exitStatus: result.exitStatus,\n timedOut: result.timedOut,\n started: result.started !== false,\n reason: result.reason,\n durationMs: result.durationMs,\n },\n }));\n resolve(result);\n });\n\n const fail = (error) => finish(() => {\n this.dispatchEvent(new CustomEvent('command', {\n detail: {\n phase: 'end',\n id,\n command: text,\n exitStatus: null,\n timedOut: false,\n started,\n durationMs: Date.now() - startedAt,\n error: error.message,\n },\n }));\n reject(error);\n });\n\n /**\n * What the command has printed so far. Without the opening marker the\n * shell never started it, so the transcript is its echo of the wrapper\n * -- noise, not output.\n */\n const partialOutput = () => {\n const at = transcript.indexOf(begin);\n return at === -1\n ? ''\n : stripTerminalCodes(transcript.slice(at + begin.length)).replace(/^\\r?\\n/, '').replace(/\\s+$/, '');\n };\n\n /** Interrupt whatever is still running, so the shell is usable again. */\n const giveUp = (reason) => {\n this.write('\\u0003');\n done({\n output: partialOutput(),\n exitStatus: null,\n timedOut: true,\n started,\n reason,\n durationMs: Date.now() - startedAt,\n });\n };\n\n const onData = (event) => {\n transcript += decodeUtf8(event.detail.bytes);\n\n if (!started && transcript.indexOf(begin) !== -1) {\n started = true;\n clearTimeout(startTimer);\n startTimer = null;\n }\n // A command that is still printing is still working, however long it\n // has been at it, so every chunk buys another idle window.\n if (started) {\n clearTimeout(idleTimer);\n idleTimer = setTimeout(() => giveUp('idle'), idleMs);\n }\n\n const endAt = transcript.indexOf(`${end} `);\n if (endAt === -1) {\n // The shell has rejected the wrapper itself, so the closing marker is\n // never coming. Say so now instead of holding the caller until the\n // deadline with a transcript of the wrapper it could not read.\n if (!started && FRAMING_REJECTED.test(stripTerminalCodes(transcript))) {\n const guessed = shellFromPrompt(stripTerminalCodes(transcript)) || 'cmd';\n const wrongGuess = family !== guessed;\n // Learn from it, so the next command is framed the shell's way.\n this.shellFamily = guessed;\n fail(new Error(\n wrongGuess\n ? `This shell is ${guessed === 'powershell' ? 'PowerShell' : 'Windows cmd'}, not a POSIX shell, `\n + 'and it rejected the wrapper used to capture output. The shell has been noted; '\n + 'run the command again and it will be framed correctly.'\n : `The shell rejected the wrapper used to capture output: ${\n stripTerminalCodes(transcript).split('\\n').filter(Boolean).pop() || 'no detail'\n }`\n ));\n }\n return;\n }\n\n const tail = transcript.slice(endAt);\n const status = parseInt(tail.slice(end.length + 1).trim(), 10);\n // Everything the shell printed between the two markers, with the\n // marker lines themselves and any terminal control codes removed.\n const beginAt = transcript.indexOf(begin);\n const body = beginAt === -1\n ? transcript.slice(0, endAt)\n : transcript.slice(beginAt + begin.length, endAt);\n\n done({\n output: stripTerminalCodes(body).replace(/^\\r?\\n/, '').replace(/\\s+$/, ''),\n exitStatus: Number.isInteger(status) ? status : null,\n timedOut: false,\n started: true,\n durationMs: Date.now() - startedAt,\n });\n };\n\n const onClose = () => fail(new Error('The SSH session closed while the command was running.'));\n\n this.addEventListener('data', onData);\n this.addEventListener('close', onClose);\n\n // Three deadlines, none of which is \"how long may this command take\".\n startTimer = setTimeout(() => giveUp('start'), startMs);\n idleTimer = setTimeout(() => giveUp('idle'), idleMs);\n maxTimer = setTimeout(() => giveUp('max'), maxMs);\n\n this.write(frameCommand(family, text, id));\n });\n }\n\n /** Tell the shell its window changed, so full-screen programs redraw. */\n resize(columns: number, rows: number) {\n const nextColumns = Math.max(1, Math.floor(columns));\n const nextRows = Math.max(1, Math.floor(rows));\n if (nextColumns === this.columns && nextRows === this.rows) return;\n\n this.columns = nextColumns;\n this.rows = nextRows;\n if (!this.shellOpen || this.remoteChannelId === null) return;\n\n this.transport.send(new SshWriter(64)\n .u8(MSG.CHANNEL_REQUEST).u32(this.remoteChannelId).string('window-change').boolean(false)\n .u32(nextColumns).u32(nextRows).u32(0).u32(0)\n .take());\n }\n\n // --- plumbing ------------------------------------------------------------\n\n #status(phase, message: string) {\n this.dispatchEvent(new CustomEvent('status', { detail: { phase, message } }));\n }\n\n #emitBanner(text: string) {\n if (text) this.dispatchEvent(new CustomEvent('banner', { detail: { text } }));\n }\n\n /** Wait for one of a few message types: the handshakes read linearly that way. */\n #expect(...types) {\n return new Promise((resolve, reject) => {\n for (const type of types) this.waiters.set(type, { resolve, reject });\n });\n }\n\n #settle(type, payload: Uint8Array) {\n const waiter = this.waiters.get(type);\n if (!waiter) return false;\n this.waiters.clear();\n waiter.resolve({ type, payload });\n return true;\n }\n\n /** A closed transport must not leave a handshake waiting forever. */\n #abandonWaiters(message: string) {\n if (this.waiters.size === 0) return;\n const pending = [...new Set(this.waiters.values())];\n this.waiters.clear();\n for (const waiter of pending) waiter.reject(new Error(message));\n }\n\n #handlePacket(type, payload: Uint8Array) {\n if (this.#settle(type, payload)) return;\n\n switch (type) {\n case MSG.USERAUTH_BANNER:\n this.#emitBanner(new SshReader(payload, 1).string().replaceAll('\\n', '\\r\\n'));\n return;\n case MSG.CHANNEL_DATA: {\n const reader = new SshReader(payload, 1);\n reader.u32();\n this.#consume(reader.stringBytes());\n return;\n }\n case MSG.CHANNEL_EXTENDED_DATA: {\n const reader = new SshReader(payload, 1);\n reader.u32();\n const dataType = reader.u32();\n const bytes = reader.stringBytes();\n this.#consume(bytes, dataType === EXTENDED_DATA_STDERR);\n return;\n }\n case MSG.CHANNEL_WINDOW_ADJUST: {\n const reader = new SshReader(payload, 1);\n reader.u32();\n this.remoteWindow += reader.u32();\n this.#flushWrites();\n return;\n }\n case MSG.CHANNEL_REQUEST: {\n const reader = new SshReader(payload, 1);\n reader.u32();\n const request = reader.string();\n const wantReply = reader.boolean();\n if (request === 'exit-status') this.exitStatus = reader.u32();\n if (request === 'exit-signal') this.exitSignal = reader.string();\n if (wantReply && this.remoteChannelId !== null) {\n this.transport.send(new SshWriter(8).u8(MSG.CHANNEL_SUCCESS).u32(this.remoteChannelId).take());\n }\n return;\n }\n case MSG.CHANNEL_EOF:\n return;\n case MSG.CHANNEL_CLOSE:\n this.shellOpen = false;\n this.#status('closed', 'The remote shell ended.');\n this.transport.disconnect('Shell closed');\n return;\n default:\n return;\n }\n }\n\n /**\n * Watch the prompt for which shell this machine answers with, so the first\n * command is framed correctly rather than discovering it by failing.\n */\n #noteShellFamily(bytes: Uint8Array) {\n this.promptTail = (this.promptTail + stripTerminalCodes(decodeUtf8(bytes))).slice(-400);\n const family = shellFromPrompt(this.promptTail);\n if (!family) return;\n\n if (family === 'cmd' && !this.powerShellAttempted) {\n this.#startPowerShell();\n return;\n }\n\n // While PowerShell is starting, the old cmd prompt may still be the last\n // prompt in the transcript. Wait for PowerShell's own prompt rather than\n // briefly advertising cmd as ready and racing an AI tool call into it.\n if (family === 'cmd' && this.powerShellAttempted && !this.readySignaled) return;\n\n this.shellFamily = family;\n if (family === 'powershell' && !this.readySignaled) {\n this.#markReady('Connected \u00B7 PowerShell');\n }\n }\n\n /** Replace Windows OpenSSH's usual cmd.exe with an interactive PowerShell. */\n #startPowerShell() {\n if (this.powerShellAttempted || !this.shellOpen) return;\n this.powerShellAttempted = true;\n this.readySignaled = false;\n this.shellFamily = '';\n this.#status('opening', 'Starting PowerShell\u2026');\n this.write('powershell.exe -NoLogo\\r');\n\n // A customized prompt may not begin with \"PS\". Keep the terminal usable\n // in that case; the command was already sent and PowerShell is still the\n // overwhelmingly likely interpreter after this short settling period.\n this.powerShellTimer = setTimeout(() => {\n if (this.readySignaled || this.closed) return;\n this.shellFamily = 'powershell';\n this.#markReady('Connected \u00B7 PowerShell');\n }, 2_000);\n }\n\n #markReady(message = 'Connected.') {\n clearTimeout(this.powerShellTimer);\n this.powerShellTimer = null;\n this.readySignaled = true;\n this.#status('ready', message);\n this.dispatchEvent(new CustomEvent('ready', {\n detail: { fingerprint: this.fingerprint, shellFamily: this.shellFamily || 'posix' },\n }));\n }\n\n /** Shell output, with the receive window topped up before it runs dry. */\n #consume(bytes: Uint8Array, isStderr = false) {\n if (!bytes.length) return;\n this.#noteShellFamily(bytes);\n this.dispatchEvent(new CustomEvent('data', {\n detail: { bytes, display: filterDisplay(this, bytes), isStderr },\n }));\n\n this.localWindow -= bytes.length;\n if (this.localWindow > WINDOW_REFILL_THRESHOLD || this.remoteChannelId === null) return;\n\n const increment = INITIAL_WINDOW - this.localWindow;\n this.localWindow = INITIAL_WINDOW;\n this.transport.send(new SshWriter(16)\n .u8(MSG.CHANNEL_WINDOW_ADJUST).u32(this.remoteChannelId).u32(increment).take());\n }\n\n #flushWrites() {\n if (!this.shellOpen || this.remoteChannelId === null) return;\n\n while (this.pendingWrites.length > 0) {\n const next = this.pendingWrites[0];\n const room = Math.min(this.remoteWindow, this.remoteMaxPacket - 64);\n if (room <= 0) return;\n\n const chunk = next.length <= room ? next : next.subarray(0, room);\n if (chunk.length === next.length) this.pendingWrites.shift();\n else this.pendingWrites[0] = next.subarray(chunk.length);\n\n this.remoteWindow -= chunk.length;\n this.transport.send(new SshWriter(chunk.length + 16)\n .u8(MSG.CHANNEL_DATA).u32(this.remoteChannelId).string(chunk).take());\n }\n }\n\n // --- authentication ------------------------------------------------------\n\n async #authenticate() {\n try {\n this.#status('authenticating', `Authenticating as ${this.username}\u2026`);\n this.transport.send(new SshWriter(32).u8(MSG.SERVICE_REQUEST).string('ssh-userauth').take());\n await this.#expect(MSG.SERVICE_ACCEPT);\n\n // \"none\" is how a client learns which methods a host will accept, and a\n // host with no authentication at all answers it with success.\n if (await this.#tryNone()) return void await this.#openShell();\n\n if (this.authMethods.includes('password') && this.password) {\n if (await this.#tryPassword(this.password)) return void await this.#openShell();\n }\n\n if (this.authMethods.includes('keyboard-interactive')) {\n if (await this.#tryKeyboardInteractive()) return void await this.#openShell();\n\n // The stored password was not what the host wanted -- a one-time code,\n // or simply the wrong one. Ask, the way the command-line client does.\n if (this.authMethods.includes('keyboard-interactive')) {\n if (await this.#tryKeyboardInteractive({ usePassword: false })) {\n return void await this.#openShell();\n }\n }\n }\n\n // A host that only advertises password auth still needs an answer when\n // the panel was opened without one.\n if (this.authMethods.includes('password')) {\n const typed = await this.requestInput({\n prompt: `${this.username}@${this.options.hostLabel || 'host'}'s password: `,\n echo: false,\n });\n if (typed && await this.#tryPassword(typed)) return void await this.#openShell();\n }\n\n throw new Error(this.authMethods.length\n ? `SSH: authentication failed. The host accepts ${this.authMethods.join(', ')}.`\n : 'SSH: the host rejected the connection before any authentication method was offered.');\n } catch (error) {\n if (!this.closed) this.transport.fail(error);\n }\n }\n\n async #tryNone() {\n this.transport.send(new SshWriter(64)\n .u8(MSG.USERAUTH_REQUEST).string(this.username).string(SERVICE).string('none').take());\n return this.#authResult();\n }\n\n async #tryPassword(password: string) {\n this.transport.send(new SshWriter(128)\n .u8(MSG.USERAUTH_REQUEST).string(this.username).string(SERVICE).string('password')\n .boolean(false).string(password)\n .take());\n return this.#authResult();\n }\n\n /**\n * keyboard-interactive is how most Linux hosts actually ask for a password,\n * and the only way a one-time code can be entered. Prompts are answered from\n * the password when one is obviously being asked for, and by the user\n * otherwise.\n */\n async #tryKeyboardInteractive({ usePassword = true } = {}) {\n this.transport.send(new SshWriter(96)\n .u8(MSG.USERAUTH_REQUEST).string(this.username).string(SERVICE).string('keyboard-interactive')\n .string('').string('')\n .take());\n\n let usedPassword = !usePassword;\n while (true) {\n const { type, payload } = await this.#expect(\n MSG.USERAUTH_SUCCESS, MSG.USERAUTH_FAILURE, MSG.USERAUTH_INFO_REQUEST\n );\n if (type === MSG.USERAUTH_SUCCESS) return true;\n if (type === MSG.USERAUTH_FAILURE) {\n this.#readFailure(payload);\n return false;\n }\n\n const reader = new SshReader(payload, 1);\n const name = reader.string();\n const instruction = reader.string();\n reader.string(); // language\n const promptCount = reader.u32();\n\n if (name) this.#emitBanner(`${name.replaceAll('\\n', '\\r\\n')}\\r\\n`);\n if (instruction) this.#emitBanner(`${instruction.replaceAll('\\n', '\\r\\n')}\\r\\n`);\n\n const answers = [];\n for (let index = 0; index < promptCount; index++) {\n const prompt = reader.string();\n const echo = reader.boolean();\n const looksLikePassword = !echo && /pass(word|phrase)/i.test(prompt);\n\n if (looksLikePassword && this.password && !usedPassword) {\n usedPassword = true;\n answers.push(this.password);\n continue;\n }\n answers.push(await this.requestInput({ prompt, echo }));\n }\n\n const response = new SshWriter(256).u8(MSG.USERAUTH_INFO_RESPONSE).u32(answers.length);\n for (const answer of answers) response.string(answer);\n this.transport.send(response.take());\n }\n }\n\n async #authResult() {\n const { type, payload } = await this.#expect(MSG.USERAUTH_SUCCESS, MSG.USERAUTH_FAILURE);\n if (type === MSG.USERAUTH_SUCCESS) return true;\n this.#readFailure(payload);\n return false;\n }\n\n #readFailure(payload: Uint8Array) {\n const reader = new SshReader(payload, 1);\n this.authMethods = reader.nameList();\n const partial = reader.boolean();\n if (partial) this.#emitBanner('\\r\\nOne factor accepted; the host wants another.\\r\\n');\n }\n\n // --- the shell -----------------------------------------------------------\n\n async #openShell() {\n this.#status('opening', 'Starting the remote shell\u2026');\n\n this.transport.send(new SshWriter(64)\n .u8(MSG.CHANNEL_OPEN).string('session').u32(this.channelId).u32(INITIAL_WINDOW).u32(MAX_PACKET)\n .take());\n\n const opened = await this.#expect(MSG.CHANNEL_OPEN_CONFIRMATION, MSG.CHANNEL_OPEN_FAILURE);\n if (opened.type === MSG.CHANNEL_OPEN_FAILURE) {\n const reader = new SshReader(opened.payload, 1);\n reader.u32();\n reader.u32();\n throw new Error(`SSH: the host refused to open a session channel (${reader.string()}).`);\n }\n\n const reader = new SshReader(opened.payload, 1);\n reader.u32(); // our channel id, echoed back\n this.remoteChannelId = reader.u32();\n this.remoteWindow = reader.u32();\n this.remoteMaxPacket = Math.max(1024, Math.min(reader.u32(), MAX_PACKET));\n\n this.transport.send(new SshWriter(128)\n .u8(MSG.CHANNEL_REQUEST).u32(this.remoteChannelId).string('pty-req').boolean(true)\n .string(TERMINAL_TYPE).u32(this.columns).u32(this.rows).u32(0).u32(0).string(PTY_MODES)\n .take());\n const pty = await this.#expect(MSG.CHANNEL_SUCCESS, MSG.CHANNEL_FAILURE);\n if (pty.type === MSG.CHANNEL_FAILURE) {\n throw new Error('SSH: the host refused a pseudo-terminal, so there is no interactive shell to show.');\n }\n\n this.transport.send(new SshWriter(32)\n .u8(MSG.CHANNEL_REQUEST).u32(this.remoteChannelId).string('shell').boolean(true).take());\n const shell = await this.#expect(MSG.CHANNEL_SUCCESS, MSG.CHANNEL_FAILURE);\n if (shell.type === MSG.CHANNEL_FAILURE) {\n throw new Error('SSH: the host refused to start a shell for this account.');\n }\n\n this.shellOpen = true;\n this.#flushWrites();\n if (this.options.preferredShell === 'powershell') this.#startPowerShell();\n else this.#markReady();\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["reader"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/protocols/ssh/terminal.ts"],
4
- "sourcesContent": ["// The terminal surface: xterm.js, themed to match the gateway, wired to an\n// SSH session's bytes in and keystrokes out.\n\nimport { Terminal } from '@xterm/xterm';\nimport { FitAddon } from '@xterm/addon-fit';\nimport xtermStyles from '@xterm/xterm/css/xterm.css';\n\nconst STYLE_ELEMENT_ID = 'xterm-styles';\n\n/**\n * The width almost every command's output is laid out for.\n *\n * A terminal narrower than this does not merely look cramped, it garbles: `ls\n * -l`, `Get-ChildItem`, `ps` and `top` all place columns by character position,\n * so the last column wraps one letter per line and the table stops being a\n * table. Fitting the font to the panel rather than the panel to the font is\n * what keeps that from happening in a docked panel or on a phone.\n */\nconst MIN_COLUMNS = 80;\n\n/** Comfortable on a wide panel, and the size a roomy one keeps. */\nconst BASE_FONT_SIZE = 13;\n\n/**\n * The floor. On a small mobile screen this yields 80 columns without wrapping\n * or cutting off table outputs.\n */\nconst MIN_FONT_SIZE = 7;\n\n/** xterm ships its own stylesheet; the bundle carries it instead of a CDN. */\nfunction installStyles() {\n if (document.getElementById(STYLE_ELEMENT_ID)) return;\n const style = document.createElement('style');\n style.id = STYLE_ELEMENT_ID;\n style.textContent = xtermStyles;\n document.head.appendChild(style);\n}\n\n/** The gateway's palette, so the terminal belongs to the same application. */\nconst THEME = {\n background: '#0b0d12',\n foreground: '#dfe4ec',\n cursor: '#f38020',\n cursorAccent: '#0b0d12',\n selectionBackground: 'rgba(243, 128, 32, 0.32)',\n black: '#1b1f27',\n red: '#ff6b6b',\n green: '#4ade80',\n yellow: '#fbbf24',\n blue: '#60a5fa',\n magenta: '#c084fc',\n cyan: '#34d3ee',\n white: '#cbd3e1',\n brightBlack: '#63708a',\n brightRed: '#ff8787',\n brightGreen: '#86efac',\n brightYellow: '#fcd34d',\n brightBlue: '#93c5fd',\n brightMagenta: '#d8b4fe',\n brightCyan: '#67e8f9',\n brightWhite: '#f5f7fa',\n};\n\nexport class SshTerminal {\n constructor(container) {\n installStyles();\n\n this.container = container;\n this.terminal = new Terminal({\n allowProposedApi: true,\n convertEol: false,\n cursorBlink: true,\n cursorStyle: 'bar',\n drawBoldTextInBrightColors: true,\n fontFamily: 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace',\n fontSize: BASE_FONT_SIZE,\n lineHeight: 1.25,\n letterSpacing: 0,\n macOptionIsMeta: true,\n minimumContrastRatio: 1.5,\n scrollback: 10_000,\n theme: THEME,\n });\n\n this.fitAddon = new FitAddon();\n this.terminal.loadAddon(this.fitAddon);\n this.terminal.open(container);\n this.fit();\n\n // The panel is resizable and the browser window is not the only thing that\n // changes its size, so follow the element rather than the window.\n this.observer = new ResizeObserver(() => this.fit());\n this.observer.observe(container);\n\n this.dataHandler = null;\n this.terminal.onData((data: Uint8Array) => this.dataHandler?.(data));\n }\n\n get columns() {\n return this.terminal.cols;\n }\n\n get rows() {\n return this.terminal.rows;\n }\n\n onData(handler) {\n this.dataHandler = handler;\n }\n\n onResize(handler) {\n this.terminal.onResize(({ cols, rows }) => handler(cols, rows));\n }\n\n write(data: Uint8Array) {\n this.terminal.write(data);\n }\n\n /** Gateway-side notices, kept visually apart from what the host prints. */\n notice(text: string, tone = 'info') {\n const color = { info: '\\x1b[38;5;245m', good: '\\x1b[38;5;114m', bad: '\\x1b[38;5;203m' }[tone] || '';\n this.terminal.write(`${color}${text}\\x1b[0m\\r\\n`);\n }\n\n clear() {\n this.terminal.clear();\n }\n\n focus() {\n this.terminal.focus();\n }\n\n /**\n * Size the terminal to the panel, choosing the font so MIN_COLUMNS fit.\n *\n * The columns matter more than the type size here: the commands people run in\n * a side panel -- `ls -l`, `top`, anything in PowerShell that prints a table\n * -- are laid out for 80 columns, and a narrower grid wraps every row into an\n * unreadable stack.\n *\n * `proposeDimensions()` reports the grid the current font size yields, and a\n * monospace cell's width scales with its font size, so `fontSize * cols /\n * MIN_COLUMNS` lands close to the size that gives MIN_COLUMNS. It is measured\n * rather than assumed because the cell-width-to-font-size ratio belongs to\n * whichever font the platform picked. A second pass settles the rounding.\n * Changing the font resizes the element, which the ResizeObserver sees, so\n * the guard keeps that from recursing.\n */\n fit() {\n if (this.fitting) return;\n if (this.container.clientWidth <= 0 || this.container.clientHeight <= 0) return;\n\n this.fitting = true;\n try {\n for (let pass = 0; pass < 2; pass++) {\n const proposed = this.fitAddon.proposeDimensions();\n if (!proposed || !(proposed.cols > 0)) break;\n\n const current = this.terminal.options.fontSize || BASE_FONT_SIZE;\n const wanted = Math.min(\n BASE_FONT_SIZE,\n Math.max(MIN_FONT_SIZE, Math.floor((current * proposed.cols) / MIN_COLUMNS))\n );\n if (wanted === current) break;\n this.terminal.options.fontSize = wanted;\n }\n\n this.fitAddon.fit();\n\n // A panel narrow enough to be short of MIN_COLUMNS even at the smallest\n // type keeps the columns and scrolls sideways instead, because wrapped\n // output is harder to read than output that has to be scrolled to.\n const short = this.terminal.cols < MIN_COLUMNS;\n this.container.classList.toggle('is-narrow', short);\n if (short) this.terminal.resize(MIN_COLUMNS, this.terminal.rows);\n } catch {\n // A hidden panel has no size to fit to; the next resize will catch it.\n } finally {\n this.fitting = false;\n }\n }\n\n /** The grid the host is being told about, for a status line or a log. */\n get geometry() {\n return {\n columns: this.terminal.cols,\n rows: this.terminal.rows,\n fontSize: this.terminal.options.fontSize,\n };\n }\n\n get selection() {\n return this.terminal.getSelection();\n }\n\n /**\n * Read one line from the user, for a password or a one-time code the host\n * asked for during authentication. The shell is not open yet, so the\n * terminal is free to be a prompt.\n */\n readLine({ prompt = '', echo = false } = {}) {\n return new Promise((resolve) => {\n const previous = this.dataHandler;\n let answer = '';\n\n this.terminal.write(prompt.replaceAll('\\n', '\\r\\n'));\n this.dataHandler = (data: Uint8Array) => {\n for (const character of data) {\n const code = character.charCodeAt(0);\n\n if (code === 0x0d || code === 0x0a) { // Enter, or a pasted line break\n this.terminal.write('\\r\\n');\n this.dataHandler = previous;\n resolve(answer);\n return;\n }\n if (code === 0x7f || code === 0x08) { // Backspace\n if (answer.length === 0) continue;\n answer = answer.slice(0, -1);\n if (echo) this.terminal.write('\\b \\b');\n continue;\n }\n if (code === 0x03) { // Ctrl-C\n this.terminal.write('^C\\r\\n');\n this.dataHandler = previous;\n resolve('');\n return;\n }\n if (code < 0x20) continue;\n\n answer += character;\n if (echo) this.terminal.write(character);\n }\n };\n });\n }\n\n destroy() {\n this.observer?.disconnect();\n this.terminal.dispose();\n }\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/protocols/ssh/transport.ts"],
4
- "sourcesContent": ["// The SSH transport layer (RFC 4253): version exchange, the binary packet\n// protocol, key exchange, and the AES-GCM cipher that protects everything\n// after it.\n//\n// Like the RDP client, this runs entirely in the browser. The gateway's\n// /ssh-ws endpoint is a byte relay and never sees a decrypted packet or a\n// password; the keys derived here exist only in this tab.\n\nimport { MSG, DISCONNECT_REASON } from './messages';\nimport {\n SshReader,\n SshWriter,\n concatBytes,\n encodeUtf8,\n} from './wire';\nimport {\n createKexClient,\n deriveKey,\n exchangeHash,\n hostKeyFingerprint,\n hostKeyType,\n negotiableAlgorithms,\n verifyHostKeySignature,\n} from './kex';\nimport {\n GATEWAY_KEEPALIVE_SIGNAL,\n GATEWAY_READY_SIGNAL,\n KEEPALIVE_INTERVAL_MS,\n} from '../../shared/signals';\n\nexport const CLIENT_VERSION = 'SSH-2.0-CloudflareGateway_1.0';\n\n/** Only AEAD ciphers are offered, so the MAC list is never actually used. */\nconst CIPHERS = {\n 'aes256-gcm@openssh.com': { keyLength: 32 },\n 'aes128-gcm@openssh.com': { keyLength: 16 },\n};\nconst CIPHER_NAMES = Object.keys(CIPHERS);\nconst MAC_NAMES = ['hmac-sha2-256', 'hmac-sha2-512'];\nconst COMPRESSION_NAMES = ['none'];\n\nconst GCM_BLOCK = 16;\nconst GCM_TAG_BYTES = 16;\nconst GCM_IV_BYTES = 12;\nconst PLAIN_BLOCK = 8;\nconst MIN_PADDING = 4;\nconst MAX_PACKET_BYTES = 262_144;\nconst VERSION_LINE_LIMIT = 8_192;\n\nconst subtle = globalThis.crypto?.subtle;\n\n/** Pick the first client preference the host also offered (RFC 4253 7.1). */\nexport function negotiate(clientNames, serverNames) {\n return clientNames.find((name: string) => serverNames.includes(name)) || null;\n}\n\n/** An AES-GCM direction: one key, and an invocation counter that never repeats. */\nclass GcmCipher {\n key: CryptoKey;\n fixed: Uint8Array;\n counter: Uint8Array;\n\n constructor(key: CryptoKey, iv: Uint8Array) {\n this.key = key;\n this.fixed = iv.slice(0, 4);\n this.counter = iv.slice(4, 12);\n }\n\n static async create(keyBytes: Uint8Array, iv: Uint8Array) {\n const key = await subtle.importKey('raw', keyBytes as unknown as BufferSource, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']);\n return new GcmCipher(key, iv);\n }\n\n /** The 12-byte nonce for this packet, then step the 64-bit counter. */\n nextIv() {\n const iv = concatBytes(this.fixed, this.counter);\n for (let index = this.counter.length - 1; index >= 0; index--) {\n this.counter[index] = (this.counter[index] + 1) & 0xff;\n if (this.counter[index] !== 0) break;\n }\n return iv;\n }\n}\n\nexport class SshTransport extends EventTarget {\n url: string;\n verifyHost: (info: any) => Promise<boolean> | boolean;\n log: (...args: any[]) => void;\n socket: WebSocket | null;\n openTransport: ((url: string) => any) | null;\n closed: boolean;\n buffer: Uint8Array;\n serverVersion: string;\n greeting: string[];\n sendSequence: number;\n receiveSequence: number;\n outgoing: any;\n incoming: any;\n sessionId: Uint8Array | null;\n kex: any;\n kexQueue: Uint8Array[];\n keepaliveTimer: any;\n bytesSent: number;\n bytesReceived: number;\n encryptChain?: Promise<void>;\n newKeysBarrier?: Promise<void> | null;\n releaseNewKeys?: (() => void) | null;\n pendingServerKexInit?: Uint8Array | null;\n established?: boolean;\n\n /**\n * @param {string} url gateway WebSocket URL\n * @param {object} options\n * @param {(info: object) => Promise<boolean>} options.verifyHost decides\n * whether an unknown or changed host key may be used.\n */\n constructor(\n url: string,\n { verifyHost, log, openTransport }: {\n verifyHost?: (info: any) => Promise<boolean> | boolean;\n log?: (...args: any[]) => void;\n /** Returns a WebSocket-shaped transport for `url`; default is a real one. */\n openTransport?: ((url: string) => any) | null;\n } = {}\n ) {\n super();\n\n this.url = url;\n this.verifyHost = verifyHost || (() => true);\n this.log = log || (() => {});\n this.openTransport = openTransport || null;\n\n this.socket = null;\n this.closed = false;\n this.buffer = new Uint8Array(0);\n this.serverVersion = '';\n this.greeting = [];\n\n this.sendSequence = 0;\n this.receiveSequence = 0;\n this.outgoing = null;\n this.incoming = null;\n this.sessionId = null;\n\n this.kex = null;\n this.kexQueue = [];\n this.keepaliveTimer = null;\n this.bytesSent = 0;\n this.bytesReceived = 0;\n }\n\n // --- connection ----------------------------------------------------------\n\n connect() {\n // Injected so this client can run anywhere: a WebSocket to the gateway in\n // a browser, a TCP socket straight at the host in the CLI. The key\n // exchange and everything above it is identical either way.\n this.socket = this.openTransport ? this.openTransport(this.url) : new WebSocket(this.url);\n this.socket.binaryType = 'arraybuffer';\n\n this.socket.addEventListener('open', () => {\n // The relay holds the host's first bytes until the browser says it is\n // listening, exactly as the RDP client does.\n this.socket.send(GATEWAY_READY_SIGNAL);\n this.#write(encodeUtf8(`${CLIENT_VERSION}\\r\\n`));\n this.keepaliveTimer = setInterval(() => {\n if (this.socket?.readyState !== WebSocket.OPEN) return;\n\n // Keeps the browser-to-gateway WebSocket alive. The gateway absorbs\n // this frame, so it never reaches the host.\n this.socket.send(GATEWAY_KEEPALIVE_SIGNAL);\n\n // Which leaves the TCP leg silent whenever nobody is typing, and an\n // idle TCP connection through a tunnel is a TCP connection about to be\n // dropped. This is the request OpenSSH itself uses to keep one warm:\n // servers answer it with a failure, which is a perfectly good answer --\n // the point is the bytes, not the reply.\n if (this.sessionId) {\n try {\n this.#sendPacket(new SshWriter(64)\n .u8(MSG.GLOBAL_REQUEST)\n .string('keepalive@openssh.com')\n .u8(1)\n .take());\n } catch {\n // A keepalive is not worth breaking a working session over.\n }\n }\n }, KEEPALIVE_INTERVAL_MS);\n });\n\n this.socket.addEventListener('message', (event) => {\n if (typeof event.data === 'string') return; // gateway control frame\n this.#receive(new Uint8Array(event.data));\n });\n\n this.socket.addEventListener('close', (event) => {\n this.#finish({\n clean: event.code === 1000,\n code: event.code,\n message: event.reason || (\n event.code === 1006\n ? 'The gateway rejected or could not reach this SSH target. Confirm that SSH is published for this host.'\n : ''\n ),\n });\n });\n\n // A failed WebSocket upgrade emits `error` immediately followed by\n // `close`. The error event has no HTTP status or response body, while a\n // relay that connected successfully can put the useful TCP failure in the\n // close reason. Waiting for `close` preserves that explanation instead of\n // replacing it with an opaque generic error.\n this.socket.addEventListener('error', () => {\n this.log('The SSH gateway WebSocket reported a transport error; waiting for its close reason.');\n });\n }\n\n disconnect(message = 'Client disconnected') {\n if (this.closed) return;\n try {\n const payload = new SshWriter(64)\n .u8(MSG.DISCONNECT).u32(11).string(message).string('')\n .take();\n this.#sendPacket(payload);\n } catch {}\n // The packet may still be encrypting, and closing the socket under it\n // would drop the goodbye the host is waiting for.\n const socket = this.socket;\n void Promise.resolve(this.encryptChain).finally(() => {\n try { socket?.close(1000, 'Client disconnected'); } catch {}\n });\n this.#finish({ clean: true, code: 1000, message: '' });\n }\n\n #finish(detail) {\n if (this.closed) return;\n this.closed = true;\n clearInterval(this.keepaliveTimer);\n this.keepaliveTimer = null;\n this.dispatchEvent(new CustomEvent('close', { detail }));\n }\n\n fail(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n if (this.closed) return;\n this.dispatchEvent(new CustomEvent('error', { detail: { message } }));\n this.#finish({ clean: false, code: 4000, message });\n try { this.socket?.close(4000, message.slice(0, 100)); } catch {}\n }\n\n #write(bytes: Uint8Array) {\n if (this.socket?.readyState !== WebSocket.OPEN) return;\n this.bytesSent += bytes.length;\n this.socket.send(bytes);\n }\n\n // --- framing -------------------------------------------------------------\n\n #receive(chunk: Uint8Array) {\n this.bytesReceived += chunk.length;\n this.buffer = this.buffer.length ? concatBytes(this.buffer, chunk) : chunk;\n\n // Decryption is asynchronous, so the reader is a single chain rather than\n // a loop: two overlapping drains could deliver packets out of order, and\n // an out-of-order packet is a broken session.\n this.drainChain = (this.drainChain || Promise.resolve())\n .then(() => this.#drain())\n .catch((error: unknown) => this.fail(error));\n }\n\n async #drain() {\n if (this.closed) return;\n if (!this.serverVersion && !this.#readVersion()) return;\n\n while (!this.closed) {\n // The host's NEWKEYS is the last packet under the old cipher. Nothing\n // after it can be read until the new one is installed.\n if (this.newKeysBarrier) {\n const barrier = this.newKeysBarrier;\n this.newKeysBarrier = null;\n await barrier;\n continue;\n }\n\n if (!this.incoming) {\n const payload = this.#readPlainPacket();\n if (!payload) return;\n this.#dispatchPacket(payload);\n continue;\n }\n\n const frame = this.#takeEncryptedFrame();\n if (!frame) return;\n this.#dispatchPacket(await this.#decrypt(frame));\n }\n }\n\n /**\n * The host may print any number of lines before its version line, and RFC\n * 4253 says to show them: this is where a \"banned by fail2ban\" notice or a\n * legal banner arrives.\n */\n #readVersion() {\n while (true) {\n const newline = this.buffer.indexOf(0x0a);\n if (newline === -1) {\n if (this.buffer.length > VERSION_LINE_LIMIT) {\n throw new Error('SSH: the host sent no version line.');\n }\n return false;\n }\n\n let end = newline;\n if (end > 0 && this.buffer[end - 1] === 0x0d) end--;\n const line = new TextDecoder().decode(this.buffer.subarray(0, end));\n this.buffer = this.buffer.subarray(newline + 1);\n\n if (!line.startsWith('SSH-')) {\n if (line.trim()) this.greeting.push(line);\n continue;\n }\n if (!line.startsWith('SSH-2.0-') && !line.startsWith('SSH-1.99-')) {\n throw new Error(`SSH: this host speaks ${line.split('-').slice(0, 2).join('-')}, which is too old.`);\n }\n\n this.serverVersion = line;\n this.log('version', { server: line, greeting: this.greeting });\n this.dispatchEvent(new CustomEvent('greeting', { detail: { lines: this.greeting.slice() } }));\n void this.#startKex();\n return true;\n }\n }\n\n /** A packet from before the keys exist. Returns null when more bytes are due. */\n #readPlainPacket() {\n if (this.buffer.length < 5) return null;\n const view = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);\n const packetLength = view.getUint32(0, false);\n if (packetLength < 8 || packetLength > MAX_PACKET_BYTES) {\n throw new Error('SSH: the host sent a packet of an impossible size.');\n }\n if (this.buffer.length < 4 + packetLength) return null;\n\n const paddingLength = this.buffer[4];\n if (paddingLength + 1 > packetLength) throw new Error('SSH: a packet claimed more padding than it had.');\n const payload = this.buffer.slice(5, 4 + packetLength - paddingLength);\n this.buffer = this.buffer.subarray(4 + packetLength);\n this.receiveSequence = (this.receiveSequence + 1) >>> 0;\n return payload;\n }\n\n /**\n * With AES-GCM the length is authenticated but not encrypted, so the frame\n * can be measured before anything is decrypted (RFC 5647).\n */\n #takeEncryptedFrame() {\n if (this.buffer.length < 4) return null;\n const view = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);\n const packetLength = view.getUint32(0, false);\n if (packetLength < GCM_BLOCK || packetLength % GCM_BLOCK !== 0 || packetLength > MAX_PACKET_BYTES) {\n throw new Error('SSH: the host sent a packet of an impossible size.');\n }\n\n const total = 4 + packetLength + GCM_TAG_BYTES;\n if (this.buffer.length < total) return null;\n\n const frame = this.buffer.slice(0, total);\n this.buffer = this.buffer.subarray(total);\n return frame;\n }\n\n async #decrypt(frame: Uint8Array) {\n let plain;\n try {\n plain = new Uint8Array(await subtle.decrypt(\n {\n name: 'AES-GCM',\n iv: this.incoming.nextIv(),\n additionalData: frame.subarray(0, 4),\n tagLength: GCM_TAG_BYTES * 8,\n },\n this.incoming.key,\n frame.subarray(4)\n ));\n } catch {\n throw new Error('SSH: a packet failed its integrity check, so the session was dropped.');\n }\n\n const paddingLength = plain[0];\n if (paddingLength + 1 > plain.length) throw new Error('SSH: a packet claimed more padding than it had.');\n this.receiveSequence = (this.receiveSequence + 1) >>> 0;\n return plain.slice(1, plain.length - paddingLength);\n }\n\n #dispatchPacket(payload: Uint8Array) {\n if (payload.length === 0) return;\n const type = payload[0];\n\n switch (type) {\n case MSG.DISCONNECT: {\n const reader = new SshReader(payload, 1);\n const code = reader.u32();\n const description = reader.string();\n const reason = description || DISCONNECT_REASON[code] || `reason ${code}`;\n this.dispatchEvent(new CustomEvent('hostdisconnect', { detail: { code, reason } }));\n this.#finish({ clean: code === 11, code: 1000, message: reason });\n try { this.socket?.close(1000, 'Host disconnected'); } catch {}\n return;\n }\n case MSG.IGNORE:\n case MSG.UNIMPLEMENTED:\n case MSG.EXT_INFO:\n return;\n case MSG.DEBUG: {\n const reader = new SshReader(payload, 1);\n const alwaysDisplay = reader.boolean();\n const message = reader.string();\n this.log('debug', { message, alwaysDisplay });\n if (alwaysDisplay) {\n this.dispatchEvent(new CustomEvent('banner', { detail: { text: `${message}\\r\\n` } }));\n }\n return;\n }\n case MSG.KEXINIT:\n void this.#onKexInit(payload);\n return;\n case MSG.NEWKEYS:\n this.#onNewKeys();\n return;\n case MSG.GLOBAL_REQUEST: {\n const reader = new SshReader(payload, 1);\n reader.string();\n if (reader.boolean()) this.#sendPacket(new Uint8Array([MSG.REQUEST_FAILURE]));\n return;\n }\n // The answer to the keepalive global request. Either one means the host\n // is still there, which is the whole point of having asked.\n case MSG.REQUEST_SUCCESS:\n case MSG.REQUEST_FAILURE:\n return;\n default:\n break;\n }\n\n // A key exchange packet can only be answered by the exchange that is\n // running, and everything else belongs to the layers above.\n if (this.kex && type >= 30 && type <= 49) {\n this.#deliverKexPacket(payload);\n return;\n }\n this.dispatchEvent(new CustomEvent('packet', { detail: { type, payload } }));\n }\n\n // --- sending -------------------------------------------------------------\n\n /** Application packets wait while a key exchange is in flight (RFC 4253 7.1). */\n send(payload: Uint8Array) {\n if (this.closed) return false;\n if (this.kex?.running) {\n this.kexQueue.push(payload);\n return true;\n }\n this.#sendPacket(payload);\n return true;\n }\n\n #sendPacket(payload: Uint8Array) {\n if (this.closed || this.socket?.readyState !== WebSocket.OPEN) return;\n if (!this.outgoing) {\n this.#write(framePlain(payload));\n this.sendSequence = (this.sendSequence + 1) >>> 0;\n return;\n }\n void this.#encrypt(payload);\n }\n\n async #encrypt(payload: Uint8Array) {\n // Packets must reach the host in the order they were built, and encryption\n // is asynchronous, so each one waits for the one before it.\n const previous = this.encryptChain || Promise.resolve();\n this.encryptChain = previous.then(async () => {\n if (this.closed || !this.outgoing) return;\n\n const paddingLength = gcmPaddingLength(payload.length);\n const plain = new Uint8Array(1 + payload.length + paddingLength);\n plain[0] = paddingLength;\n plain.set(payload, 1);\n crypto.getRandomValues(plain.subarray(1 + payload.length));\n\n const header = new SshWriter(4).u32(plain.length).take();\n const sealed = new Uint8Array(await subtle.encrypt(\n { name: 'AES-GCM', iv: this.outgoing.nextIv(), additionalData: header, tagLength: GCM_TAG_BYTES * 8 },\n this.outgoing.key,\n plain\n ));\n\n this.#write(concatBytes(header, sealed));\n this.sendSequence = (this.sendSequence + 1) >>> 0;\n }).catch((error: unknown) => this.fail(error));\n\n return this.encryptChain;\n }\n\n // --- key exchange --------------------------------------------------------\n\n async #startKex() {\n if (this.kex?.running) return;\n\n // Claimed before the first await: the host's own KEXINIT is usually already\n // on the wire, and two exchanges starting at once would send two of ours.\n this.kex = { running: true, clientKexInit: null, pending: null, inbox: [] };\n\n try {\n const offer = await negotiableAlgorithms();\n const clientKexInit = buildKexInit(offer);\n this.kex.clientKexInit = clientKexInit;\n this.#sendPacket(clientKexInit);\n\n const serverKexInit = this.pendingServerKexInit || await this.#nextKexPacket(MSG.KEXINIT);\n this.pendingServerKexInit = null;\n await this.#runKex(offer, clientKexInit, serverKexInit);\n } catch (error) {\n this.fail(error);\n }\n }\n\n /** The host may ask for new keys at any point in a session. */\n async #onKexInit(payload: Uint8Array) {\n if (this.kex?.running) {\n this.#deliverKexPacket(payload);\n return;\n }\n\n this.log('rekey', { reason: 'the host asked for new keys' });\n this.pendingServerKexInit = payload;\n await this.#startKex();\n }\n\n /**\n * Key exchange packets can arrive before the step that wants them: the host\n * sends its reply and NEWKEYS back to back, while this side is still\n * deriving a secret or waiting for the user to accept a host key. They are\n * queued rather than dropped.\n */\n #deliverKexPacket(payload: Uint8Array) {\n if (!this.kex) return;\n if (this.kex.pending) {\n const pending = this.kex.pending;\n this.kex.pending = null;\n pending(payload);\n return;\n }\n this.kex.inbox.push(payload);\n }\n\n #nextKexPacket(expectedType) {\n return new Promise((resolve, reject) => {\n const check = (payload: Uint8Array) => {\n if (expectedType !== undefined && payload[0] !== expectedType) {\n reject(new Error(`SSH: expected message ${expectedType} during key exchange but got ${payload[0]}.`));\n return;\n }\n resolve(payload);\n };\n\n const queued = this.kex.inbox.shift();\n if (queued) {\n check(queued);\n return;\n }\n this.kex.pending = (payload: Uint8Array) => {\n this.kex.pending = null;\n check(payload);\n };\n });\n }\n\n async #runKex(offer, clientKexInit: Uint8Array, serverKexInit: Uint8Array) {\n const server = parseKexInit(serverKexInit);\n const kexAlgorithm = negotiate(offer.kex, server.kex);\n const hostKeyAlgorithm = negotiate(offer.hostKey, server.hostKey);\n const cipherToServer = negotiate(CIPHER_NAMES, server.cipherClientToServer);\n const cipherToClient = negotiate(CIPHER_NAMES, server.cipherServerToClient);\n\n if (!kexAlgorithm) throw new Error(`SSH: no shared key exchange method. The host offers ${server.kex.join(', ')}.`);\n if (!hostKeyAlgorithm) throw new Error(`SSH: no host key type this browser can verify. The host offers ${server.hostKey.join(', ')}.`);\n if (!cipherToServer || !cipherToClient) {\n throw new Error(`SSH: the host does not offer AES-GCM, which is the only cipher this client can use. It offers ${server.cipherServerToClient.join(', ')}.`);\n }\n\n this.log('negotiated', {\n kex: kexAlgorithm,\n hostKey: hostKeyAlgorithm,\n cipher: cipherToClient,\n rekey: Boolean(this.sessionId),\n });\n\n const client = await createKexClient(kexAlgorithm);\n this.#sendPacket(new SshWriter(128).u8(MSG.KEX_ECDH_INIT).string(client.publicKey).take());\n\n const reply = new SshReader(await this.#nextKexPacket(MSG.KEX_ECDH_REPLY), 1);\n const hostKeyBlob = reply.stringBytes();\n const serverPublicKey = reply.stringBytes();\n const signatureBlob = reply.stringBytes();\n\n const sharedSecret = await client.sharedSecret(serverPublicKey);\n const hash = await exchangeHash({\n hash: client.hash,\n clientVersion: CLIENT_VERSION,\n serverVersion: this.serverVersion,\n clientKexInit,\n serverKexInit,\n hostKeyBlob,\n clientPublicKey: client.publicKey,\n serverPublicKey,\n sharedSecret,\n });\n\n const signatureValid = await verifyHostKeySignature({\n algorithm: hostKeyAlgorithm,\n keyBlob: hostKeyBlob,\n signatureBlob,\n exchangeHash: hash,\n });\n if (!signatureValid) {\n throw new Error('SSH: the host key signature is invalid, so this is not the machine it claims to be.');\n }\n\n const fingerprint = await hostKeyFingerprint(hostKeyBlob);\n if (!this.sessionId) {\n // Trust is decided once per connection, against the key that has just\n // proved it owns this exchange.\n const accepted = await this.verifyHost({\n fingerprint,\n keyType: hostKeyType(hostKeyBlob),\n algorithm: hostKeyAlgorithm,\n });\n if (!accepted) throw new Error('SSH: the host key was not accepted, so the connection was stopped.');\n this.sessionId = hash;\n }\n\n this.#sendPacket(new Uint8Array([MSG.NEWKEYS]));\n await this.#nextKexPacket(MSG.NEWKEYS);\n\n const parameters = {\n hash: client.hash,\n sharedSecret,\n exchangeHash: hash,\n sessionId: this.sessionId,\n };\n const [ivToServer, ivToClient, keyToServer, keyToClient] = await Promise.all([\n deriveKey({ ...parameters, letter: 'A', length: GCM_IV_BYTES }),\n deriveKey({ ...parameters, letter: 'B', length: GCM_IV_BYTES }),\n deriveKey({ ...parameters, letter: 'C', length: CIPHERS[cipherToServer].keyLength }),\n deriveKey({ ...parameters, letter: 'D', length: CIPHERS[cipherToClient].keyLength }),\n ]);\n\n this.outgoing = await GcmCipher.create(keyToServer, ivToServer);\n this.incoming = await GcmCipher.create(keyToClient, ivToClient);\n this.kex.running = false;\n\n // Both ciphers exist now, so the reader may go on.\n this.releaseNewKeys?.();\n this.releaseNewKeys = null;\n\n const queued = this.kexQueue;\n this.kexQueue = [];\n for (const payload of queued) this.#sendPacket(payload);\n\n const firstTime = !this.established;\n this.established = true;\n this.dispatchEvent(new CustomEvent('keys', {\n detail: { fingerprint, kexAlgorithm, hostKeyAlgorithm, cipher: cipherToClient, firstTime },\n }));\n }\n\n #onNewKeys() {\n if (!this.kex) return;\n\n // Hold the reader here: the host's very next packet is already encrypted\n // with keys this side has not derived yet.\n this.newKeysBarrier = new Promise((resolve) => { this.releaseNewKeys = resolve; });\n this.#deliverKexPacket(new Uint8Array([MSG.NEWKEYS]));\n }\n}\n\n/** Padding for a GCM packet: only the encrypted part is block aligned. */\nexport function gcmPaddingLength(payloadLength) {\n const unpadded = 1 + payloadLength;\n let padding = GCM_BLOCK - (unpadded % GCM_BLOCK);\n while (padding < MIN_PADDING) padding += GCM_BLOCK;\n return padding;\n}\n\n/** A packet before any keys exist: the whole thing is block aligned. */\nexport function framePlain(payload: Uint8Array) {\n const unpadded = 5 + payload.length;\n let padding = PLAIN_BLOCK - (unpadded % PLAIN_BLOCK);\n if (padding < MIN_PADDING) padding += PLAIN_BLOCK;\n\n const writer = new SshWriter(unpadded + padding);\n writer.u32(1 + payload.length + padding);\n writer.u8(padding);\n writer.raw(payload);\n const frame = writer.take();\n const filler = new Uint8Array(padding);\n crypto.getRandomValues(filler);\n return concatBytes(frame, filler);\n}\n\nexport function buildKexInit(offer) {\n const cookie = new Uint8Array(16);\n crypto.getRandomValues(cookie);\n\n const writer = new SshWriter(512);\n writer.u8(MSG.KEXINIT).raw(cookie);\n writer.nameList(offer.kex);\n writer.nameList(offer.hostKey);\n writer.nameList(CIPHER_NAMES); // client to server\n writer.nameList(CIPHER_NAMES); // server to client\n writer.nameList(MAC_NAMES);\n writer.nameList(MAC_NAMES);\n writer.nameList(COMPRESSION_NAMES);\n writer.nameList(COMPRESSION_NAMES);\n writer.nameList([]); // languages\n writer.nameList([]);\n writer.boolean(false); // no guessed key exchange packet\n writer.u32(0); // reserved\n return writer.take();\n}\n\nexport function parseKexInit(payload: Uint8Array) {\n const reader = new SshReader(payload, 17); // message number and cookie\n return {\n kex: reader.nameList(),\n hostKey: reader.nameList(),\n cipherClientToServer: reader.nameList(),\n cipherServerToClient: reader.nameList(),\n macClientToServer: reader.nameList(),\n macServerToClient: reader.nameList(),\n compressionClientToServer: reader.nameList(),\n compressionServerToClient: reader.nameList(),\n };\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/protocols/ssh/wire.ts"],
4
- "sourcesContent": ["// SSH wire types (RFC 4251 section 5).\n//\n// Everything in SSH is big endian, so unlike the RDP reader and writer the byte\n// order is not part of each call. The four types that matter here are the\n// 32-bit length-prefixed string, the multiple-precision integer, the name-list\n// and the byte.\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\nexport function encodeUtf8(text: string): Uint8Array {\n return textEncoder.encode(text);\n}\n\nexport function decodeUtf8(bytes: Uint8Array): string {\n return textDecoder.decode(bytes);\n}\n\nexport function concatBytes(...parts: readonly Uint8Array[]): Uint8Array {\n const total = parts.reduce((sum, part) => sum + part.length, 0);\n const out = new Uint8Array(total);\n let offset = 0;\n for (const part of parts) {\n out.set(part, offset);\n offset += part.length;\n }\n return out;\n}\n\n/** Constant-time-ish equality. Used for authentication tags and fingerprints. */\nexport function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let difference = 0;\n for (let index = 0; index < a.length; index++) difference |= (a[index] ?? 0) ^ (b[index] ?? 0);\n return difference === 0;\n}\n\nexport function toBase64(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary);\n}\n\nexport function fromBase64(text: string): Uint8Array {\n const binary = atob(text);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index++) bytes[index] = binary.charCodeAt(index);\n return bytes;\n}\n\n/** Base64url without padding, which is what a JSON Web Key wants. */\nexport function toBase64Url(bytes: Uint8Array): string {\n return toBase64(bytes).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');\n}\n\nexport class SshWriter {\n private bytes: Uint8Array<ArrayBuffer>;\n private view: DataView;\n private offset = 0;\n\n constructor(capacity = 256) {\n this.bytes = new Uint8Array(new ArrayBuffer(capacity));\n this.view = new DataView(this.bytes.buffer);\n }\n\n #ensure(extra: number): void {\n if (this.offset + extra <= this.bytes.length) return;\n let capacity = Math.max(this.bytes.length * 2, 64);\n while (capacity < this.offset + extra) capacity *= 2;\n const grown = new Uint8Array(new ArrayBuffer(capacity));\n grown.set(this.bytes.subarray(0, this.offset));\n this.bytes = grown;\n this.view = new DataView(grown.buffer);\n }\n\n u8(value: number): this {\n this.#ensure(1);\n this.view.setUint8(this.offset, value);\n this.offset += 1;\n return this;\n }\n\n boolean(value: boolean): this {\n return this.u8(value ? 1 : 0);\n }\n\n u32(value: number): this {\n this.#ensure(4);\n this.view.setUint32(this.offset, value >>> 0, false);\n this.offset += 4;\n return this;\n }\n\n raw(bytes: Uint8Array): this {\n this.#ensure(bytes.length);\n this.bytes.set(bytes, this.offset);\n this.offset += bytes.length;\n return this;\n }\n\n /** A length-prefixed string. Text is encoded as UTF-8. */\n string(value: string | Uint8Array): this {\n const bytes = typeof value === 'string' ? encodeUtf8(value) : value;\n return this.u32(bytes.length).raw(bytes);\n }\n\n nameList(names: readonly string[]): this {\n return this.string(names.join(','));\n }\n\n /**\n * An mpint: two's complement, big endian, with no leading zero bytes except\n * the one that keeps a positive number from looking negative.\n */\n mpint(bytes: Uint8Array): this {\n let start = 0;\n while (start < bytes.length && bytes[start] === 0) start++;\n const magnitude = bytes.subarray(start);\n if (magnitude.length === 0) return this.u32(0);\n if ((magnitude[0] ?? 0) & 0x80) {\n this.u32(magnitude.length + 1).u8(0);\n return this.raw(magnitude);\n }\n return this.u32(magnitude.length).raw(magnitude);\n }\n\n get length(): number {\n return this.offset;\n }\n\n take(): Uint8Array {\n return this.bytes.slice(0, this.offset);\n }\n}\n\nexport class SshReader {\n private readonly bytes: Uint8Array;\n private readonly view: DataView;\n offset: number;\n\n constructor(bytes: Uint8Array, offset = 0) {\n this.bytes = bytes;\n this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n this.offset = offset;\n }\n\n get remaining(): number {\n return this.bytes.length - this.offset;\n }\n\n #need(count: number): void {\n if (count < 0 || this.remaining < count) {\n throw new Error(`SSH: truncated packet (needed ${count} bytes, ${this.remaining} left)`);\n }\n }\n\n u8(): number {\n this.#need(1);\n return this.view.getUint8(this.offset++);\n }\n\n boolean(): boolean {\n return this.u8() !== 0;\n }\n\n u32(): number {\n this.#need(4);\n const value = this.view.getUint32(this.offset, false);\n this.offset += 4;\n return value;\n }\n\n raw(count: number): Uint8Array {\n this.#need(count);\n const slice = this.bytes.subarray(this.offset, this.offset + count);\n this.offset += count;\n return slice;\n }\n\n /** The bytes of a length-prefixed string. */\n stringBytes(): Uint8Array {\n return this.raw(this.u32());\n }\n\n string(): string {\n return decodeUtf8(this.stringBytes());\n }\n\n nameList(): string[] {\n const value = this.string();\n return value ? value.split(',') : [];\n }\n\n /** An mpint's magnitude, with the sign byte removed. */\n mpint(): Uint8Array {\n const bytes = this.stringBytes();\n let start = 0;\n while (start < bytes.length && bytes[start] === 0) start++;\n return bytes.subarray(start);\n }\n\n skip(count: number): this {\n this.#need(count);\n this.offset += count;\n return this;\n }\n\n rest(): Uint8Array {\n return this.bytes.subarray(this.offset);\n }\n}\n\n/** Pad a magnitude out to a fixed width, which raw curve points need. */\nexport function padStart(bytes: Uint8Array, width: number): Uint8Array {\n if (bytes.length === width) return bytes;\n if (bytes.length > width) return bytes.subarray(bytes.length - width);\n const out = new Uint8Array(width);\n out.set(bytes, width - bytes.length);\n return out;\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": [],
4
- "sourcesContent": [],
5
- "mappings": "",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/protocols/vnc/input.ts"],
4
- "sourcesContent": ["/** RFB wheel buttons for a bounded two-axis scroll, in wire order. */\nexport function vncWheelMasks(delta: { x: number; y: number }): number[] {\n const masks: number[] = [];\n const wheels = [\n { amount: delta.y, negativeMask: 1 << 3, positiveMask: 1 << 4 },\n { amount: delta.x, negativeMask: 1 << 5, positiveMask: 1 << 6 },\n ];\n for (const { amount, negativeMask, positiveMask } of wheels) {\n const mask = amount < 0 ? negativeMask : positiveMask;\n for (let step = 0; step < Math.min(20, Math.abs(amount)); step++) masks.push(mask);\n }\n return masks;\n}\n"],
5
- "mappings": "AACO,SAAS,cAAc,OAA2C;AACvE,QAAM,QAAkB,CAAC;AACzB,QAAM,SAAS;AAAA,IACb,EAAE,QAAQ,MAAM,GAAG,cAAc,KAAK,GAAG,cAAc,KAAK,EAAE;AAAA,IAC9D,EAAE,QAAQ,MAAM,GAAG,cAAc,KAAK,GAAG,cAAc,KAAK,EAAE;AAAA,EAChE;AACA,aAAW,EAAE,QAAQ,cAAc,aAAa,KAAK,QAAQ;AAC3D,UAAM,OAAO,SAAS,IAAI,eAAe;AACzC,aAAS,OAAO,GAAG,OAAO,KAAK,IAAI,IAAI,KAAK,IAAI,MAAM,CAAC,GAAG,OAAQ,OAAM,KAAK,IAAI;AAAA,EACnF;AACA,SAAO;AACT;",
6
- "names": []
7
- }