Skip to content

Commit

Permalink
Updates to make testing happy
Browse files Browse the repository at this point in the history
  • Loading branch information
Apollon77 committed Aug 15, 2024
1 parent 59883ed commit 96af23b
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 79 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
name: Lint

on: push
on:
push:
branches:
- '*'
pull_request: {}

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4

- name: Install and cache dependencies
uses: bahmutov/npm-install@v1
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
name: Test

on: push
on:
push:
branches:
- '*'
pull_request: {}

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x, 14.x, 16.x, 18.x]
node-version: [12.x, 14.x, 16.x, 18.x, 20.x, 22.x]

steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4

- name: Install and cache dependencies
uses: bahmutov/npm-install@v1
Expand Down
51 changes: 26 additions & 25 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -717,8 +717,9 @@ class TuyaDevice extends EventEmitter {
debug('Protocol 3.4, 3.5: Local Random Key: ' + this._tmpLocalKey.toString('hex'));
debug('Protocol 3.4, 3.5: Remote Random Key: ' + this._tmpRemoteKey.toString('hex'));

if(this.device.version === '3.4' || this.device.version === '3.5')
if (this.device.version === '3.4' || this.device.version === '3.5') {
this._currentSequenceN = packet.sequenceN - 1;
}

const calcLocalHmac = this.device.parser.cipher.hmac(this._tmpLocalKey).toString('hex');
const expLocalHmac = packet.payload.slice(16, 16 + 32).toString('hex');
Expand Down Expand Up @@ -749,10 +750,12 @@ class TuyaDevice extends EventEmitter {
this.sessionKey[i] = this._tmpLocalKey[i] ^ this._tmpRemoteKey[i];
}

if(this.device.version === '3.4')
if (this.device.version === '3.4') {
this.sessionKey = this.device.parser.cipher._encrypt34({data: this.sessionKey});
else if(this.device.version === '3.5')
} else if (this.device.version === '3.5') {
this.sessionKey = this.device.parser.cipher._encrypt35({data: this.sessionKey, iv: this._tmpLocalKey});
}

debug('Protocol 3.4, 3.5: Session Key: ' + this.sessionKey.toString('hex'));
debug('Protocol 3.4, 3.5: Initialization done');

Expand Down Expand Up @@ -780,11 +783,9 @@ class TuyaDevice extends EventEmitter {
packet.commandByte === CommandType.CONTROL ||
packet.commandByte === CommandType.CONTROL_NEW
) && packet.payload === false) {

if(this.device.version === '3.5')
{
if (this.device.version === '3.5') {
// Move resolver to next sequence for incoming response after ack
this._resolvers[(parseInt(packet.sequenceN) + 1).toString()] = this._resolvers[packet.sequenceN.toString()];
this._resolvers[(parseInt(packet.sequenceN, 10) + 1).toString()] = this._resolvers[packet.sequenceN.toString()];
delete this._resolvers[packet.sequenceN.toString()];
}

Expand All @@ -804,26 +805,26 @@ class TuyaDevice extends EventEmitter {
this._setResolveAllowGet = undefined;
delete this._resolvers[packet.sequenceN];
this._expectRefreshResponseForSequenceN = undefined;
} else {
} else if (packet.sequenceN in this._resolvers) {
// Call data resolver for sequence number
if (packet.sequenceN in this._resolvers) {
debug('Received DP_REFRESH response packet - resolve');
this._resolvers[packet.sequenceN](packet.payload);

// Remove resolver
delete this._resolvers[packet.sequenceN];
this._expectRefreshResponseForSequenceN = undefined;
} else if (this._expectRefreshResponseForSequenceN && this._expectRefreshResponseForSequenceN in this._resolvers) {
debug('Received DP_REFRESH response packet without data - resolve');
this._resolvers[this._expectRefreshResponseForSequenceN](packet.payload);

// Remove resolver
delete this._resolvers[this._expectRefreshResponseForSequenceN];
this._expectRefreshResponseForSequenceN = undefined;
} else {
debug('Received DP_REFRESH response packet - no resolver found for sequence number' + packet.sequenceN);
}

debug('Received DP_REFRESH response packet - resolve');
this._resolvers[packet.sequenceN](packet.payload);

// Remove resolver
delete this._resolvers[packet.sequenceN];
this._expectRefreshResponseForSequenceN = undefined;
} else if (this._expectRefreshResponseForSequenceN && this._expectRefreshResponseForSequenceN in this._resolvers) {
debug('Received DP_REFRESH response packet without data - resolve');
this._resolvers[this._expectRefreshResponseForSequenceN](packet.payload);

// Remove resolver
delete this._resolvers[this._expectRefreshResponseForSequenceN];
this._expectRefreshResponseForSequenceN = undefined;
} else {
debug('Received DP_REFRESH response packet - no resolver found for sequence number' + packet.sequenceN);
}

return;
}

Expand Down
34 changes: 16 additions & 18 deletions lib/cipher.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class TuyaCipher {
return this._encrypt34(options);
}

else if (this.version === '3.5') {
if (this.version === '3.5') {
return this._encrypt35(options);
}

Expand Down Expand Up @@ -95,19 +95,18 @@ class TuyaCipher {
*/
_encrypt35(options) {
let encrypted;
let localIV = Buffer.from((Date.now() * 10).toString().substring(0, 12));
if(options.iv !== undefined) localIV = options.iv.slice(0, 12);
let localIV = Buffer.from((Date.now() * 10).toString().slice(0, 12));
if (options.iv !== undefined) {
localIV = options.iv.slice(0, 12);
}

const cipher = crypto.createCipheriv('aes-128-gcm', this.getKey(), localIV);
if(options.aad !== undefined)
{
if (options.aad === undefined) {
encrypted = Buffer.concat([cipher.update(options.data), cipher.final()]);
} else {
cipher.setAAD(options.aad);
encrypted = Buffer.concat([localIV, cipher.update(options.data), cipher.final(), cipher.getAuthTag(), Buffer.from([0x00, 0x00, 0x99, 0x66])]);
}
else
{
encrypted = Buffer.concat([cipher.update(options.data), cipher.final()]);
}

return encrypted;
}
Expand All @@ -123,7 +122,7 @@ class TuyaCipher {
return this._decrypt34(data);
}

else if (this.version === '3.5') {
if (this.version === '3.5') {
return this._decrypt35(data);
}

Expand Down Expand Up @@ -222,24 +221,23 @@ class TuyaCipher {
*/
_decrypt35(data) {
let result;
let header = data.slice(0, 14);
let iv = data.slice(14, 26);
let tag = data.slice(data.length - 16);
const header = data.slice(0, 14);
const iv = data.slice(14, 26);
const tag = data.slice(data.length - 16);
data = data.slice(26, data.length - 16);

try {
const decipher = crypto.createDecipheriv('aes-128-gcm', this.getKey(), iv);
decipher.setAuthTag(tag);
decipher.setAAD(header);

result = Buffer.concat([decipher.update(data), decipher.final()]);
result = result.slice(4); // remove 32bit return code
result = result.slice(4); // Remove 32bit return code
} catch (_) {
throw new Error('Decrypt failed');
}

// Try to parse data as JSON,
// otherwise return as string.

// Try to parse data as JSON, otherwise return as string.
// 3.5 protocol
// {"protocol":4,"t":1632405905,"data":{"dps":{"101":true},"cid":"00123456789abcde"}}
try {
Expand Down
40 changes: 17 additions & 23 deletions lib/message-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,9 @@ class MessageParser {
let leftover = false;

let suffixLocation = buffer.indexOf('0000AA55', 0, 'hex');
if (suffixLocation === -1) // Couldn't find 0000AA55 during parse
if (suffixLocation === -1) {// Couldn't find 0000AA55 during parse
suffixLocation = buffer.indexOf('00009966', 0, 'hex');
}

if (suffixLocation !== buffer.length - 4) {
leftover = buffer.slice(suffixLocation + 4);
Expand All @@ -138,8 +139,7 @@ class MessageParser {
let commandByte;
let payloadSize;

if (suffix === 0x0000AA55)
{
if (suffix === 0x0000AA55) {
// Get sequence number
sequenceN = buffer.readUInt32BE(4);

Expand All @@ -153,9 +153,7 @@ class MessageParser {
if (buffer.length - 8 < payloadSize) {
throw new TypeError(`Packet missing payload: payload has length ${payloadSize}.`);
}
}
else if (suffix === 0x00009966)
{
} else if (suffix === 0x00009966) {
// Get sequence number
sequenceN = buffer.readUInt32BE(6);

Expand Down Expand Up @@ -185,13 +183,15 @@ class MessageParser {
// Get the payload
// Adjust for messages lacking a return code
let payload;
if(this.version !== '3.5')
{
if (this.version === '3.5') {
payload = buffer.slice(HEADER_SIZE_3_5, HEADER_SIZE_3_5 + payloadSize);
sequenceN = buffer.slice(6, 10).readUInt32BE();
commandByte = buffer.slice(10, 14).readUInt32BE();
} else {
if (returnCode & 0xFFFFFF00) {
if (this.version === '3.4' && !packageFromDiscovery) {
payload = buffer.slice(HEADER_SIZE, HEADER_SIZE + payloadSize - 0x24);
}
else if (this.version === '3.5' && !packageFromDiscovery) {
} else if (this.version === '3.5' && !packageFromDiscovery) {
payload = buffer.slice(HEADER_SIZE, HEADER_SIZE + payloadSize - 0x24);
} else {
payload = buffer.slice(HEADER_SIZE, HEADER_SIZE + payloadSize - 8);
Expand Down Expand Up @@ -221,12 +221,6 @@ class MessageParser {
}
}
}
else
{
payload = buffer.slice(HEADER_SIZE_3_5, HEADER_SIZE_3_5 + payloadSize);
sequenceN = buffer.slice(6, 10).readUInt32BE();
commandByte = buffer.slice(10, 14).readUInt32BE();
}

return {payload, leftover, commandByte, sequenceN};
}
Expand Down Expand Up @@ -255,10 +249,10 @@ class MessageParser {
}

// Incoming 3.5 data isn't 0 because of iv and tag so check size after
if(this.version === '3.5')
{
if(data.length === 0)
if (this.version === '3.5') {
if (data.length === 0) {
return false;
}
}

// Try to parse data as JSON.
Expand Down Expand Up @@ -336,7 +330,7 @@ class MessageParser {
return this._encode34(options);
}

else if (this.version === '3.5') {
if (this.version === '3.5') {
return this._encode35(options);
}

Expand Down Expand Up @@ -498,18 +492,18 @@ class MessageParser {
Buffer.from('3.5').copy(buffer, 0);
payload.copy(buffer, 15);
payload = buffer;
options.data = '3.5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + options.data;
// OO options.data = '3.5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + options.data;
}

// Allocate buffer for prefix, unknown, sequence, command, length
let buffer = Buffer.alloc(18);

// Add prefix, command, and length
buffer.writeUInt32BE(0x00006699, 0); // Prefix
buffer.writeUInt16BE(0x0, 4); // Unknown
buffer.writeUInt32BE(options.sequenceN, 6); // Sequence
buffer.writeUInt32BE(options.commandByte, 10); // Command
buffer.writeUInt32BE(payload.length + 0x1c, 14); // Length
buffer.writeUInt32BE(payload.length + 28 /* 0x1c */, 14); // Length

const encrypted = this.cipher.encrypt({
data: payload,
Expand Down
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
},
"homepage": "https://github.com/codetheweb/tuyapi#readme",
"dependencies": {
"debug": "^4.3.4",
"debug": "^4.3.6",
"p-queue": "6.6.2",
"p-retry": "4.6.2",
"p-timeout": "3.2.0"
Expand Down

0 comments on commit 96af23b

Please sign in to comment.