WTVSec Updates
- optimize WordArray to Buffer functions - update documentation in WTVSec - update WTVSec barrowed function in WTVLzpf - removed NewRC4Session, was a pointless alias to SecureOn
This commit is contained in:
@@ -455,7 +455,7 @@ class WTVLzpf {
|
|||||||
if (data.words) {
|
if (data.words) {
|
||||||
var WTVSec = require("./WTVSec.js");
|
var WTVSec = require("./WTVSec.js");
|
||||||
wtvsec = new WTVSec(1);
|
wtvsec = new WTVSec(1);
|
||||||
data = new Buffer.from(wtvsec.wordArrayToUint8Array(data));
|
data = wtvsec.wordArrayToBuffer(data);
|
||||||
WTVSec, wtvsec = null;
|
WTVSec, wtvsec = null;
|
||||||
} else if (!data.byteLength) {
|
} else if (!data.byteLength) {
|
||||||
// otherwise if its not already a Buffer, convert it to one
|
// otherwise if its not already a Buffer, convert it to one
|
||||||
|
|||||||
@@ -32,6 +32,15 @@ class WTVSec {
|
|||||||
RC4Session = new Array();
|
RC4Session = new Array();
|
||||||
zdebug = false;
|
zdebug = false;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Initialize the WTVSec class.
|
||||||
|
*
|
||||||
|
* @param {Number} wtv_incarnation Sets the wtv-incarnation for this instance
|
||||||
|
* @param {Boolean} zdebug Enable debugging
|
||||||
|
*
|
||||||
|
*/
|
||||||
constructor(wtv_incarnation = 1, zdebug = false) {
|
constructor(wtv_incarnation = 1, zdebug = false) {
|
||||||
this.zdebug = zdebug;
|
this.zdebug = zdebug;
|
||||||
this.initial_shared_key = CryptoJS.enc.Base64.parse(this.initial_shared_key_b64);
|
this.initial_shared_key = CryptoJS.enc.Base64.parse(this.initial_shared_key_b64);
|
||||||
@@ -44,6 +53,11 @@ class WTVSec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the wtv-incarnation for this instance
|
||||||
|
*
|
||||||
|
* @param {Number} wtv_incarnation
|
||||||
|
*/
|
||||||
set_incarnation(wtv_incarnation) {
|
set_incarnation(wtv_incarnation) {
|
||||||
if (this.incarnation != wtv_incarnation) {
|
if (this.incarnation != wtv_incarnation) {
|
||||||
this.incarnation = wtv_incarnation;
|
this.incarnation = wtv_incarnation;
|
||||||
@@ -51,14 +65,26 @@ class WTVSec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the wtv-incaration for this instance by 1
|
||||||
|
*/
|
||||||
increment_incarnation() {
|
increment_incarnation() {
|
||||||
this.set_incarnation(parseInt(this.incarnation) + 1);
|
this.set_incarnation(parseInt(this.incarnation) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones a WordArray to allow modification without referencing its original
|
||||||
|
* @param {CryptoJS.lib.WordArray} wa
|
||||||
|
*
|
||||||
|
* @returns {CryptoJS.lib.WordArray}
|
||||||
|
*/
|
||||||
DuplicateWordArray(wa) {
|
DuplicateWordArray(wa) {
|
||||||
return CryptoJS.lib.WordArray.create(this.wordArrayToUint8Array(wa).buffer);
|
return CryptoJS.lib.WordArray.create(this.wordArrayToBuffer(wa));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the wtv-ticket for this instance
|
||||||
|
*/
|
||||||
PrepareTicket() {
|
PrepareTicket() {
|
||||||
// store last challenge response in ticket
|
// store last challenge response in ticket
|
||||||
var ticket_data = this.challenge_raw;
|
var ticket_data = this.challenge_raw;
|
||||||
@@ -77,6 +103,11 @@ class WTVSec {
|
|||||||
return this.ticket_b64;
|
return this.ticket_b64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a wtv-ticket to set up this instance
|
||||||
|
*
|
||||||
|
* @param {Base64} ticket_b64
|
||||||
|
*/
|
||||||
DecodeTicket(ticket_b64) {
|
DecodeTicket(ticket_b64) {
|
||||||
var ticket_hex = CryptoJS.enc.Base64.parse(ticket_b64).toString(CryptoJS.enc.Hex);
|
var ticket_hex = CryptoJS.enc.Base64.parse(ticket_b64).toString(CryptoJS.enc.Hex);
|
||||||
var challenge_key = CryptoJS.enc.Hex.parse(ticket_hex.substring(0, 16));
|
var challenge_key = CryptoJS.enc.Hex.parse(ticket_hex.substring(0, 16));
|
||||||
@@ -95,6 +126,13 @@ class WTVSec {
|
|||||||
console.log(" * Decoded session from wtv-ticket");
|
console.log(" * Decoded session from wtv-ticket");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a wtv-challenge to get the expected response
|
||||||
|
* @param {Base64} wtv_challenge
|
||||||
|
* @param {any} key
|
||||||
|
*
|
||||||
|
* @returns {CryptoJS.lib.WordArray} wtv-challenge-response (or blank if failed)
|
||||||
|
*/
|
||||||
ProcessChallenge(wtv_challenge, key = this.current_shared_key) {
|
ProcessChallenge(wtv_challenge, key = this.current_shared_key) {
|
||||||
var challenge_raw = CryptoJS.enc.Base64.parse(wtv_challenge);
|
var challenge_raw = CryptoJS.enc.Base64.parse(wtv_challenge);
|
||||||
|
|
||||||
@@ -139,7 +177,6 @@ class WTVSec {
|
|||||||
var challenge_response = CryptoJS.enc.Hex.parse(challenge_raw_hex.substr(0, (8 * 2))).concat(echo_encrypted.ciphertext);
|
var challenge_response = CryptoJS.enc.Hex.parse(challenge_raw_hex.substr(0, (8 * 2))).concat(echo_encrypted.ciphertext);
|
||||||
return challenge_response;
|
return challenge_response;
|
||||||
} else {
|
} else {
|
||||||
throw ("Couldn't solve challenge");
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -147,6 +184,11 @@ class WTVSec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a wtv-challenge for this instance
|
||||||
|
*
|
||||||
|
* @returns {Base64} wtv-challenge
|
||||||
|
*/
|
||||||
IssueChallenge() {
|
IssueChallenge() {
|
||||||
/*
|
/*
|
||||||
* bytes 0-8: Random id? Just echoed in the response
|
* bytes 0-8: Random id? Just echoed in the response
|
||||||
@@ -188,41 +230,21 @@ class WTVSec {
|
|||||||
return challenge_b64;
|
return challenge_b64;
|
||||||
}
|
}
|
||||||
|
|
||||||
wordToByteArray(word, length) {
|
/**
|
||||||
var ba = [],
|
* convert a CryptoJS.lib.WordArray to a Javascript Buffer
|
||||||
i,
|
* @param {CryptoJS.lib.WordArray} wordArray
|
||||||
xFF = 0xFF;
|
*
|
||||||
if (length > 0)
|
* #returns {Buffer} JS Buffer object
|
||||||
ba.push(word >>> 24);
|
*/
|
||||||
if (length > 1)
|
wordArrayToBuffer(wordArray) {
|
||||||
ba.push((word >>> 16) & xFF);
|
return new Buffer.from(wordArray.toString(CryptoJS.enc.Hex), 'hex');
|
||||||
if (length > 2)
|
|
||||||
ba.push((word >>> 8) & xFF);
|
|
||||||
if (length > 3)
|
|
||||||
ba.push(word & xFF);
|
|
||||||
|
|
||||||
return ba;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wordArrayToUint8Array(wordArray, length = 0) {
|
/**
|
||||||
if (wordArray.hasOwnProperty("sigBytes") && wordArray.hasOwnProperty("words")) {
|
* Starts an encryption session
|
||||||
length = wordArray.sigBytes;
|
* @param {Number} rc4session Session Type (0 = enc k1, 1 = dec k1, 3 = enc k2, 4 = dec k2, default: all)
|
||||||
wordArray = wordArray.words;
|
*
|
||||||
}
|
*/
|
||||||
|
|
||||||
var result = [],
|
|
||||||
bytes,
|
|
||||||
i = 0;
|
|
||||||
while (length > 0) {
|
|
||||||
bytes = this.wordToByteArray(wordArray[i], Math.min(4, length));
|
|
||||||
length -= bytes.length;
|
|
||||||
result.push(bytes);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return new Uint8Array([].concat.apply([], result));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SecureOn(rc4session = null) {
|
SecureOn(rc4session = null) {
|
||||||
if (this.zdebug) console.log(" # Generating RC4 sessions with wtv-incarnation: " + this.incarnation);
|
if (this.zdebug) console.log(" # Generating RC4 sessions with wtv-incarnation: " + this.incarnation);
|
||||||
|
|
||||||
@@ -230,33 +252,37 @@ class WTVSec {
|
|||||||
endianness(buf, 4);
|
endianness(buf, 4);
|
||||||
this.hRC4_Key1 = CryptoJS.MD5(this.DuplicateWordArray(this.session_key1).concat(CryptoJS.lib.WordArray.create(buf).concat(this.DuplicateWordArray(this.session_key1))));
|
this.hRC4_Key1 = CryptoJS.MD5(this.DuplicateWordArray(this.session_key1).concat(CryptoJS.lib.WordArray.create(buf).concat(this.DuplicateWordArray(this.session_key1))));
|
||||||
this.hRC4_Key2 = CryptoJS.MD5(this.DuplicateWordArray(this.session_key2).concat(CryptoJS.lib.WordArray.create(buf).concat(this.DuplicateWordArray(this.session_key2))));
|
this.hRC4_Key2 = CryptoJS.MD5(this.DuplicateWordArray(this.session_key2).concat(CryptoJS.lib.WordArray.create(buf).concat(this.DuplicateWordArray(this.session_key2))));
|
||||||
|
var key1 = this.wordArrayToBuffer(this.hRC4_Key1);
|
||||||
|
var key2 = this.wordArrayToBuffer(this.hRC4_Key2);
|
||||||
switch (rc4session) {
|
switch (rc4session) {
|
||||||
case 0:
|
case 0:
|
||||||
this.RC4Session[0] = crypto.createCipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key1)),'');
|
this.RC4Session[0] = crypto.createCipheriv('rc4', key1,'');
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
this.RC4Session[1] = crypto.createDecipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key1)),'');
|
this.RC4Session[1] = crypto.createDecipheriv('rc4', key1,'');
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
this.RC4Session[2] = crypto.createCipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key2)),'');
|
this.RC4Session[2] = crypto.createCipheriv('rc4', key2,'');
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
this.RC4Session[3] = crypto.createDecipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key2)),'');
|
this.RC4Session[3] = crypto.createDecipheriv('rc4', key2,'');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.RC4Session[0] = crypto.createCipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key1)), '');
|
this.RC4Session[0] = crypto.createCipheriv('rc4', key1, '');
|
||||||
this.RC4Session[1] = crypto.createDecipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key1)), '');
|
this.RC4Session[1] = crypto.createDecipheriv('rc4', key1, '');
|
||||||
this.RC4Session[2] = crypto.createCipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key2)), '');
|
this.RC4Session[2] = crypto.createCipheriv('rc4', key2, '');
|
||||||
this.RC4Session[3] = crypto.createDecipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key2)), '');
|
this.RC4Session[3] = crypto.createDecipheriv('rc4', key2, '');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
NewRC4Session(num) {
|
* RC4 Encrypt data
|
||||||
this.SecureOn(num);
|
* @param {Number} keynum Which key to use (0 = k1, 1 = k2)
|
||||||
}
|
* @param {CryptoJS.lib.WordArray|ArrayBuffer|Buffer} data Data to encrypt
|
||||||
|
*
|
||||||
|
* @returns {ArrayBuffer} Encrypted data
|
||||||
|
*/
|
||||||
Encrypt(keynum, data) {
|
Encrypt(keynum, data) {
|
||||||
var session_id;
|
var session_id;
|
||||||
switch (keynum) {
|
switch (keynum) {
|
||||||
@@ -271,16 +297,23 @@ class WTVSec {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!this.RC4Session[session_id]) {
|
if (!this.RC4Session[session_id]) {
|
||||||
this.NewRC4Session(session_id);
|
this.SecureOn(session_id);
|
||||||
}
|
}
|
||||||
if (data.words) {
|
if (data.words) {
|
||||||
data = new Buffer.from(this.wordArrayToUint8Array(data));
|
data = this.wordArrayToBuffer(data);
|
||||||
} else if (data.constructor === ArrayBuffer) {
|
} else if (data.constructor === ArrayBuffer) {
|
||||||
data = new Buffer.from(data);
|
data = new Buffer.from(data);
|
||||||
}
|
}
|
||||||
return this.RC4Session[session_id].update(data);
|
return this.RC4Session[session_id].update(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RC4 Decrypt data
|
||||||
|
* @param {Number} keynum Which key to use (0 = k1, 1 = k2)
|
||||||
|
* @param {CryptoJS.lib.WordArray|ArrayBuffer|Buffer} data Data to decrypt
|
||||||
|
*
|
||||||
|
* @returns {ArrayBuffer} Decrypted data
|
||||||
|
*/
|
||||||
Decrypt(keynum, data) {
|
Decrypt(keynum, data) {
|
||||||
var session_id;
|
var session_id;
|
||||||
switch (keynum) {
|
switch (keynum) {
|
||||||
@@ -295,10 +328,10 @@ class WTVSec {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!this.RC4Session[session_id]) {
|
if (!this.RC4Session[session_id]) {
|
||||||
this.NewRC4Session(session_id);
|
this.SecureOn(session_id);
|
||||||
}
|
}
|
||||||
if (data.words) {
|
if (data.words) {
|
||||||
data = new Buffer.from(this.wordArrayToUint8Array(data));
|
data = this.wordArrayToBuffer(data);
|
||||||
} else if (data.constructor === ArrayBuffer) {
|
} else if (data.constructor === ArrayBuffer) {
|
||||||
data = new Buffer.from(data);
|
data = new Buffer.from(data);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user