diff --git a/hive-ag3nt/assets/screen.html b/hive-ag3nt/assets/screen.html index 810759c..8f076c8 100644 --- a/hive-ag3nt/assets/screen.html +++ b/hive-ag3nt/assets/screen.html @@ -167,21 +167,68 @@ canvas { display: block; cursor: default; } if (n === 0) { state = 'error'; return false; } const types = drainTo(n); if (!types) { chunks.unshift(b); totalBytes += 1; return false; } - // Prefer type 1 (None), else use first offered - const prefer = types.indexOf(1) !== -1 ? 1 : types[0]; + // Prefer type 1 (None), then type 19 (VeNCrypt — used by neatvnc/weston + // even with --disable-transport-layer-security), else first offered. + let prefer; + if (types.indexOf(1) !== -1) prefer = 1; // plain None + else if (types.indexOf(19) !== -1) prefer = 19; // VeNCrypt + else prefer = types[0]; send(new Uint8Array([prefer])); - state = prefer === 1 ? 'security-result' : 'security-vnc-challenge'; + if (prefer === 1) state = 'security-result'; + else if (prefer === 19) state = 'vencrypt-version'; + else state = 'security-vnc-challenge'; return true; } case 'security-vnc-challenge': { - // VNC auth: skip challenge bytes, respond with zeros (will fail, - // but we're in plain-RFB mode for hyperhive — see weston-vnc.nix) + // VNC auth (type 2): we don't have the password, so send zeros. + // This will fail for password-protected servers; fine for our + // weston VNC which uses None via VeNCrypt. const b = drainTo(16); if (!b) return false; send(new Uint8Array(16)); state = 'security-result'; return true; } + // ── VeNCrypt (type 19) sub-handshake ─────────────────────────────── + // neatvnc (weston VNC backend) uses VeNCrypt as the outer type even + // with --disable-transport-layer-security, offering sub-type 1 (None). + case 'vencrypt-version': { + // Server sends: major (u8), minor (u8) — e.g. 0, 2 + const b = drainTo(2); + if (!b) return false; + // Echo same version back + send(new Uint8Array([b[0], b[1]])); + state = 'vencrypt-subtypes'; + return true; + } + case 'vencrypt-subtypes': { + // Server sends: nSubtypes (u8), then nSubtypes × u32 sub-type ids + const nb = drainTo(1); + if (!nb) return false; + const nSub = nb[0]; + const raw = drainTo(nSub * 4); + if (!raw) { chunks.unshift(nb); totalBytes += 1; return false; } + // Build sub-type array from big-endian u32s + const subs = []; + for (let i = 0; i < nSub; i++) subs.push(u32be(raw, i * 4)); + // Prefer sub-type 1 (VeNCrypt None) — no TLS, no password. + // Fall back to first offered. + const sub = subs.includes(1) ? 1 : subs[0]; + // Send chosen sub-type as big-endian u32 + send(new Uint8Array([sub>>>24, (sub>>>16)&0xff, (sub>>>8)&0xff, sub&0xff])); + state = 'vencrypt-accept'; + return true; + } + case 'vencrypt-accept': { + // Server sends 1 byte: 1=accepted, 0=refused + const b = drainTo(1); + if (!b) return false; + if (b[0] !== 1) { setStatus('VeNCrypt sub-type refused', 'error'); return false; } + // Sub-type 1 (None): proceed to SecurityResult + state = 'security-result'; + return true; + } + // ────────────────────────────────────────────────────────────────── case 'security-result': { const b = drainTo(4); if (!b) return false;