Encryption for Smart Lock API

Discussing technical aspects of Yun development products, including IoT Core and other cloud service APIs, data analysis products, etc.


allcam
Posts: 9

james1 2023年 Dec 8日 02:45

I figured this out. Turns out the access key should be interpreted as an ASCII or UTF-8 string, and not a hex string, for the encryption key. Thus, ticket_key is decrypted as a 128-bit key with 128-bits of padding, not 256 bits total.

But if we use access key UTF-8 string, then it is too long for AES-128 decryption. access key is 32 bytes, and AES-128 need 16 bytes string as key for decryption. But if you treat the access key as Hexdecimal, then it has the correct length when converted to binary.

allcam
Posts: 9

Re: Encryption for Smart Lock API

if I use the first 16 bytes of the access key as the AES-128 decryption key, I do get a 16-byte string with 16-byte padding from ticket key. Then I can encrypt the Tempoary Password with the 16-byte string, but Tuya will throw error "param is illegal

But if we use access key UTF-8 string, then it is too long for AES-128 decryption. access key is 32 bytes, and AES-128 need 16 bytes string as key for decryption. But if you treat the access key as Hexdecimal, then it has the correct length when converted to binary.

allcam
Posts: 9

Re: Encryption for Smart Lock API

find a solution:

  1. when decrypt the ticketKey, use aes-256-ecb and the full "Access Secret/Client Secret" as key,
  2. convert the decipher to utf-8 which is 16 bytes and can be used to encrypt the Temporary Password but use aes-128-ecb instead

Here is my code:
// Decrypt the temporary ticketKey,
const decryptTicketKey = (encryptedKey, TUYA_SECRET_KEY) => {
try {
const decipher = crypto.createDecipheriv('aes-256-ecb', Buffer.from(TUYA_SECRET_KEY, 'utf8'), null);
decipher.setAutoPadding(true);
const decrypted = Buffer.concat([decipher.update(Buffer.from(encryptedKey, 'hex')), decipher.final()]);
// console.log('ticketKey Length (HEX):', encryptedKey.length); // Check the length of encryptedKey
console.log('decrypted Key Length (HEX):', decrypted.length); // Check the length


Code: Select all

    const utf8Key = decrypted.toString('utf8'); 
    console.log('decrypted Key converted back to utf8, not the Binary in buffer:', utf8Key);
    return utf8Key;		
} catch (error) {
    console.error('Error decrypting ticketKey:', error.message);
    throw error;
}

};

// Set a temporary password for the Tuya device
const setTemporaryPassword = async ({ deviceId, name, password, effectiveTime, invalidTime }) => {
try {
// Fetch the temporary ticket
const ticket = await fetchTemporaryKey(deviceId);
const decryptedKey = decryptTicketKey(ticket.ticket_key, TUYA_SECRET_KEY);
// Encrypt the password using the decrypted ticket key
const cipher = crypto.createCipheriv(
'aes-128-ecb',
Buffer.from(decryptedKey, 'utf8'), // Ensure decryptedKey is treated as 16 bytes binary
null
);
cipher.setAutoPadding(true);
const encryptedPassword = Buffer.concat([
cipher.update(Buffer.from(password, 'utf8')),
cipher.final()
]).toString('hex');

// Prepare the schedule_list (example values, adjust as needed)
const scheduleList = [
{
effective_time: 0, // Example: 0:00 (in minutes)
invalid_time: 1440, // Example: 24:00 (in minutes)
working_day: 0 // Example: All days (or specify specific days as required)
}
];
console.log('Request Payload:', {
name: name,
password: encryptedPassword,
effective_time: effectiveTime,
invalid_time: invalidTime,
password_type: 'ticket',
ticket_id: ticket.ticket_id,
effective_time: effectiveTime,
invalid_time: invalidTime,
schedule_list: scheduleList,
});
// Send the request to Tuya
const response = await tuya.request({
method: 'POST',
path: /v1.0/devices/${deviceId}/door-lock/temp-password,
body: {
name: name,
password: encryptedPassword,
effective_time: effectiveTime,
invalid_time: invalidTime,
password_type: 'ticket',
ticket_id: ticket.ticket_id,
schedule_list: scheduleList, // Include schedule_list in the payload
},
});

Code: Select all

if (response.success) {
  console.log(`Temporary password set successfully for device ${deviceId}`);
  console.log(response.result.id);
  return response.result;
} else {
  throw new Error(`Failed to set temporary password: ${response.msg}`);
}

} catch (error) {
console.error(Error setting temporary password for device ${deviceId}:, error.message);
throw error;
}
};

Post Reply