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;