v0.9.2
- wtvsec: use initial key for ticket signing - update: initial work for future ability to update wtv-ticket to client - update: wtv-flashrom: use zefie server only if file does not exist locally (allows both zefie server roms and local roms) - update: wtv-flashrom:/get-lc2-path: experimental 'Cancel Update' button - fix: wtv-tricks:/blastcache: return_to was not unescaped - wtvsec: use initial key for tickets
This commit is contained in:
@@ -30,7 +30,7 @@ Let us use the URL `wtv-1800:/preregister` as an example. This is what the serve
|
||||
- Access Asynchronous mode by setting `request_is_async = true;`
|
||||
- Client request headers are available as an Array in variable `request_headers`, query arguments are also an Array, in `request_headers.query`
|
||||
- In Asynchronous mode, you are expected to call `sendToClient(socket,headers,data)` yourself, `socket` is already defined by the time your script runs, so you can just pass it through.
|
||||
- `./ServiceVault/wtv-1800/preregister.html` \[ [Example](zefie_wtvp_minisrv/ServiceVault/wtv-home/zefie.html) \]
|
||||
- `./ServiceVault/wtv-1800/preregister.html` \[ [Example](zefie_wtvp_minisrv/ServiceVault/wtv-music/demo/index.html) \]
|
||||
- HTML match (*HTML mode*)
|
||||
- Like Direct File Mode, but you don't need to append `.html`.
|
||||
- You do not need to do anything special with this format.
|
||||
|
||||
5
zefie_wtvp_minisrv/.gitignore
vendored
5
zefie_wtvp_minisrv/.gitignore
vendored
@@ -8,9 +8,8 @@ ServiceLogPost/*_*
|
||||
|
||||
# Large files not pertaining to the service code
|
||||
UserServiceVault/*-*
|
||||
ServiceVault/wtv-flashrom/content/*
|
||||
ServiceVault/wtv-music/content/*
|
||||
ServiceVault/wtv-music/midi/*
|
||||
package-lock.json
|
||||
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
if (socket.ssid != null) {
|
||||
if (ssid_sessions[socket.ssid].get("wtvsec_login")) ssid_sessions[socket.ssid].delete("wtvsec_login");
|
||||
var wtvsec_login = new WTVSec();
|
||||
wtvsec_login.IssueChallenge();
|
||||
wtvsec_login.set_incarnation(request_headers["wtv-incarnation"]);
|
||||
ssid_sessions[socket.ssid].set("wtvsec_login", wtvsec_login);
|
||||
if (!ssid_sessions[socket.ssid].data_store.wtvsec_login) {
|
||||
ssid_sessions[socket.ssid].data_store.wtvsec_login = new WTVSec();
|
||||
ssid_sessions[socket.ssid].data_store.wtvsec_login.IssueChallenge();
|
||||
ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(request_headers["wtv-incarnation"]);
|
||||
}
|
||||
} else {
|
||||
var errpage = doErrorCode(400);
|
||||
headers = errpage[0];
|
||||
data = errpage[1];
|
||||
}
|
||||
|
||||
if (wtvsec_login) {
|
||||
if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
|
||||
var prereg_contype = "text/html";
|
||||
|
||||
// if relogin, skip tellyscript
|
||||
if (request_headers.query.relogin) { // skip tellyscript
|
||||
wtvsec_login.ticket_b64 = null; // clear old ticket
|
||||
ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64 = null; // clear old ticket
|
||||
}
|
||||
|
||||
// if relogin, skip tellyscript
|
||||
@@ -34,7 +38,7 @@ if (wtvsec_login) {
|
||||
|
||||
headers = `200 OK
|
||||
Connection: Keep-Alive
|
||||
wtv-initial-key: ` + wtvsec_login.challenge_key.toString(CryptoJS.enc.Base64) + `
|
||||
wtv-initial-key: ` + ssid_sessions[socket.ssid].data_store.wtvsec_login.challenge_key.toString(CryptoJS.enc.Base64) + `
|
||||
Content-Type: `+ prereg_contype + `
|
||||
wtv-service: reset
|
||||
` + getServiceString('wtv-1800') + `
|
||||
|
||||
@@ -3,10 +3,31 @@ request_is_async = true;
|
||||
var request_path = unescape(request_headers.query.path);
|
||||
headers = "200 OK\n"
|
||||
|
||||
function doLocalFlashROM(flashrom_file_path) {
|
||||
// use local flashrom files;
|
||||
try {
|
||||
fs.readFile(flashrom_file_path, null, function (err, data) {
|
||||
if (err) {
|
||||
errpage = doErrorPage(400)
|
||||
headers = errpage[0];
|
||||
data = err.toString();
|
||||
}
|
||||
sendToClient(socket, headers, data);
|
||||
});
|
||||
} catch (e) {
|
||||
var errpage = doErrorPage(404, "The service could not find the requested ROM.")
|
||||
headers = errpage[0];
|
||||
data = errpage[1];
|
||||
sendToClient(socket, headers, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (request_headers.query.raw) {
|
||||
if ((/\.brom$/).test(request_path)) headers += "Content-Type: binary/x-wtv-bootrom"; // maybe?
|
||||
else headers += "Content-Type: binary/x-wtv-flashblock";
|
||||
if (minisrv_config.services[service_name].use_zefie_server) {
|
||||
var flashrom_file_path = service_dir + '/' + request_path;
|
||||
if (minisrv_config.services[service_name].use_zefie_server && !fs.existsSync(flashrom_file_path)) {
|
||||
// get flashrom files from archive.midnightchannel.net
|
||||
var options = {
|
||||
host: "archive.midnightchannel.net",
|
||||
@@ -40,23 +61,7 @@ if (request_headers.query.raw) {
|
||||
});
|
||||
req.end();
|
||||
} else {
|
||||
// use local flashrom files);
|
||||
var flashrom_file_path = service_dir + '/' + request_path;
|
||||
try {
|
||||
fs.readFile(flashrom_file_path, null, function (err, data) {
|
||||
if (err) {
|
||||
errpage = doErrorPage(400)
|
||||
headers = errpage[0];
|
||||
data = err.toString();
|
||||
}
|
||||
sendToClient(socket, headers, data);
|
||||
});
|
||||
} catch (e) {
|
||||
var errpage = doErrorPage(404, "The service could not find the requested ROM.")
|
||||
headers = errpage[0];
|
||||
data = errpage[1];
|
||||
sendToClient(socket, headers, data);
|
||||
}
|
||||
doLocalFlashROM(flashrom_file_path);
|
||||
}
|
||||
} else {
|
||||
// no support for bf0app yet, but here we send the client to initiate-lc2-download
|
||||
|
||||
@@ -4,13 +4,30 @@
|
||||
// - handle failures
|
||||
request_is_async = true;
|
||||
|
||||
function doLocalFlashROM() {
|
||||
fs.readFile(flashrom_file_path, null, function (err, data) {
|
||||
try {
|
||||
var data_128 = new Buffer.alloc(128);
|
||||
data.copy(data_128, 0, 0, 128);
|
||||
var flashrom_message = new Buffer.from(data_128.toString('hex').substring(36 * 2, 68 * 2), 'hex').toString('ascii').replace(/[^0-9a-z\ \.\-]/gi, "");
|
||||
processLC2DownloadPage(request_headers.query.path, flashrom_message, (request_headers.query.numparts || null));
|
||||
} catch (e) {
|
||||
var errpage = doErrorPage(404, "The service could not find the requested ROM.")
|
||||
headers = errpage[0];
|
||||
data = errpage[1];
|
||||
sendToClient(socket, headers, data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!request_headers.query.path) {
|
||||
var errpage = doErrorPage(400);
|
||||
headers = errpage[0];
|
||||
data = errpage[1];
|
||||
} else {
|
||||
var request_path = unescape(request_headers.query.path);
|
||||
if (minisrv_config.services[service_name].use_zefie_server) {
|
||||
var flashrom_file_path = service_dir + '/' + request_path;
|
||||
if (minisrv_config.services[service_name].use_zefie_server && !fs.existsSync(flashrom_file_path)) {
|
||||
// read first 256 bytes of flashrom file from archive.midnightchannel.net
|
||||
// to get `flashrom_message` and `numparts` if missing
|
||||
var options = {
|
||||
@@ -60,21 +77,8 @@ if (!request_headers.query.path) {
|
||||
});
|
||||
req.end();
|
||||
} else {
|
||||
// use local flashrom files
|
||||
var flashrom_file_path = service_dir + '/' + request_path;
|
||||
fs.readFile(flashrom_file_path, null, function (err, data) {
|
||||
try {
|
||||
var data_128 = new Buffer.alloc(128);
|
||||
data.copy(data_128, 0, 0, 128);
|
||||
var flashrom_message = new Buffer.from(data_128.toString('hex').substring(36 * 2, 68 * 2), 'hex').toString('ascii').replace(/[^0-9a-z\ \.\-]/gi, "");
|
||||
processLC2DownloadPage(request_headers.query.path, flashrom_message, (request_headers.query.numparts || null));
|
||||
} catch (e) {
|
||||
var errpage = doErrorPage(404, "The service could not find the requested ROM.")
|
||||
headers = errpage[0];
|
||||
data = errpage[1];
|
||||
sendToClient(socket, headers, data);
|
||||
}
|
||||
});
|
||||
// use local flashrom files
|
||||
doLocalFlashROM(flashrom_file_path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +199,10 @@ ${flashrom_message}
|
||||
<tr>
|
||||
<td width=104 valign=middle align=center>
|
||||
<td width=20 valign=middle align=center>
|
||||
<td colspan=9 width=416 valign=top align=left>
|
||||
<td colspan=9 width=416 valign=top align=right>
|
||||
<form action="client:gohome">
|
||||
<input type="submit" value="Cancel Update" text="#CCCCCC" borderimage="file://ROM/Borders/ButtonBorder2.bif">
|
||||
</form>
|
||||
<table cellspacing=0 cellpadding=0>
|
||||
<tr>
|
||||
<td width=306 valign=top align=left>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
headers =`200 OK
|
||||
Connection: Keep-Alive
|
||||
wtv-expire-all: wtv-home:/splash
|
||||
wtv-expire-all: wtv-flashrom:
|
||||
Content-type: text/html`
|
||||
|
||||
if (ssid_sessions[socket.ssid].get('box-does-psuedo-encryption')) {
|
||||
|
||||
@@ -6,7 +6,7 @@ Content-type: text/html`
|
||||
var visit_url = null;
|
||||
|
||||
if (request_headers.Referer) visit_url = request_headers.Referer;
|
||||
else if (request_headers.query.return_to) visit_url = request_headers.query.return_to;
|
||||
else if (request_headers.query.return_to) visit_url = unescape(request_headers.query.return_to);
|
||||
else visit_url = "client:goback";
|
||||
|
||||
data = `<html>
|
||||
|
||||
@@ -453,6 +453,17 @@ async function sendToClient(socket, headers_obj, data) {
|
||||
headers_obj["Content-Length"] = data.byteLength;
|
||||
}
|
||||
|
||||
if (ssid_sessions[socket.ssid]) {
|
||||
if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
|
||||
if (ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64) {
|
||||
if (ssid_sessions[socket.ssid].data_store.update_ticket) {
|
||||
headers_obj["wtv-ticket"] = ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64;
|
||||
headers_obj = moveObjectElement("wtv-ticket", "Connection", headers_obj);
|
||||
ssid_sessions[socket.ssid].data_store.update_ticket = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// header object to string
|
||||
if (zshowheaders) console.log(" * Outgoing headers on socket ID", socket.id, (await filterSSID(headers_obj)));
|
||||
@@ -604,6 +615,24 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
|
||||
});
|
||||
}
|
||||
|
||||
if (ssid_sessions[socket.ssid]) {
|
||||
if (headers["wtv-ticket"]) {
|
||||
if (!ssid_sessions[socket.ssid].data_store.wtvsec_login) {
|
||||
ssid_sessions[socket.ssid].data_store.wtvsec_login = new WTVSec();
|
||||
ssid_sessions[socket.ssid].data_store.wtvsec_login.IssueChallenge();
|
||||
ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(headers["wtv-incarnation"]);
|
||||
ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64 = headers["wtv-ticket"];
|
||||
ssid_sessions[socket.ssid].data_store.wtvsec_login.DecodeTicket(ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64);
|
||||
} else {
|
||||
if (ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64 != headers["wtv-ticket"]) {
|
||||
if (zdebug) console.log(" # New ticket from client");
|
||||
ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64 = headers["wtv-ticket"];
|
||||
ssid_sessions[socket.ssid].data_store.wtvsec_login.DecodeTicket(ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (returnHeadersBeforeSecure) {
|
||||
headers = await checkForPostData(socket, headers, data, data_hex);
|
||||
return headers;
|
||||
@@ -789,10 +818,9 @@ async function handleSocket(socket) {
|
||||
|
||||
function integrateConfig(main, user) {
|
||||
Object.keys(user).forEach(function (k) {
|
||||
if (typeof (user[k]) == 'object') {
|
||||
if (typeof (user[k]) == 'object' && user[k] != null) {
|
||||
// new entry
|
||||
if (!main[k]) main[k] = new Array();
|
||||
|
||||
// go down the rabbit hole
|
||||
main[k] = integrateConfig(main[k], user[k]);
|
||||
} else {
|
||||
|
||||
@@ -55,16 +55,16 @@
|
||||
"http": {
|
||||
"port": 1650,
|
||||
"connections": 3,
|
||||
"use_external_proxy": true,
|
||||
"external_proxy_is_socks": true,
|
||||
"use_external_proxy": false,
|
||||
"external_proxy_is_socks": false,
|
||||
"external_proxy_host": "127.0.0.1",
|
||||
"external_proxy_port": 1080
|
||||
},
|
||||
"https": {
|
||||
"port": 1650,
|
||||
"connections": 3,
|
||||
"use_external_proxy": true,
|
||||
"external_proxy_is_socks": true,
|
||||
"use_external_proxy": false,
|
||||
"external_proxy_is_socks": false,
|
||||
"external_proxy_host": "127.0.0.1",
|
||||
"external_proxy_port": 1080
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "zefie_wtvp_minisrv",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"description": "WebTV Service (WTVP) Emulation Server",
|
||||
"main": "app.js",
|
||||
"homepage": "https://github.com/zefie/zefie_wtvp_minisrv",
|
||||
|
||||
@@ -51,14 +51,13 @@ class WTVSec {
|
||||
// store last challenge response in ticket
|
||||
var ticket_data = this.challenge_raw;
|
||||
try {
|
||||
var ticket_data_enc = CryptoJS.DES.encrypt(ticket_data, this.current_shared_key, {
|
||||
var ticket_data_enc = CryptoJS.DES.encrypt(ticket_data, this.initial_shared_key, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.NoPadding
|
||||
});
|
||||
// create a copy of WordArray since concat modifies the original
|
||||
var current_shared_key = this.DuplicateWordArray(this.current_shared_key);
|
||||
var challenge_signed_key = this.DuplicateWordArray(this.challenge_signed_key);
|
||||
this.ticket_b64 = current_shared_key.concat(challenge_signed_key.concat(ticket_data_enc.ciphertext)).toString(CryptoJS.enc.Base64);
|
||||
this.ticket_b64 = challenge_signed_key.concat(ticket_data_enc.ciphertext).toString(CryptoJS.enc.Base64);
|
||||
} catch (e) {
|
||||
console.log("Error encrypting ticket: " + e.toString());
|
||||
return null;
|
||||
@@ -68,14 +67,13 @@ class WTVSec {
|
||||
|
||||
DecodeTicket(ticket_b64) {
|
||||
var ticket_hex = CryptoJS.enc.Base64.parse(ticket_b64).toString(CryptoJS.enc.Hex);
|
||||
var ticket_key = CryptoJS.enc.Hex.parse(ticket_hex.substring(0,16));
|
||||
var challenge_key = CryptoJS.enc.Hex.parse(ticket_hex.substring(16, 32));
|
||||
var challenge_enc = CryptoJS.enc.Hex.parse(ticket_hex.substring(32));
|
||||
var challenge_key = CryptoJS.enc.Hex.parse(ticket_hex.substring(0, 16));
|
||||
var challenge_enc = CryptoJS.enc.Hex.parse(ticket_hex.substring(16));
|
||||
var ticket_dec = CryptoJS.DES.decrypt(
|
||||
{
|
||||
ciphertext: challenge_enc
|
||||
},
|
||||
ticket_key,
|
||||
this.initial_shared_key,
|
||||
{
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.NoPadding
|
||||
|
||||
@@ -202,7 +202,6 @@
|
||||
<Content Include="session_data.js">
|
||||
<SubType>Code</SubType>
|
||||
</Content>
|
||||
<Content Include="user_config.example.json" />
|
||||
<Content Include="user_config.json" />
|
||||
<Content Include="wtvsec.js">
|
||||
<SubType>Code</SubType>
|
||||
|
||||
Reference in New Issue
Block a user