logo elektroda
logo elektroda
X
logo elektroda

New DeviceKey-Based Tuya Encrypted KV Decryption Method (BK7252U/TR6260/W800/LN8825B/RTL8720CM)

divadiow  41 2067 Cool? (+3)
📢 Listen (AI):

TL;DR

  • A new Tuya encrypted KV decryption method targets vault-style flash backups on BK7252U, TR6260, W800, LN8825B, and RTL8720CM.
  • It decrypts a wrapped key-record page first, extracts a 16-byte DeviceKey, then derives the vault key by bytewise addition with a BaseKey.
  • The vault data lives in fixed 4KB encrypted pages, and decrypted pages must match the 0x98761234 header and checksum or CRC.
  • When p_key is NULL, Tuya builds the BaseKey from two embedded 16-byte constants containing "HHRRQbyemofrtytf", yielding 9090a4a4a2c4f2cadadecce4e8f2e8cc.
  • The method successfully decoded vault KV on W800, TR6260, BK7252U, LN8825B, and RTL8720CM, but the recovered JSON does not include the pin assignments seen in BK7231N/T dumps.
Generated by the language model.
I've been trying to figure out why we cannot decrypt the key vault on some Tuya flash backups and why this seems to be the case for certain, typically older, platforms. BK7252U, TR6260, W800 to name a few.

The decryption method for BK7231N/T (and some RTLs) is known, though the Tuya 'seed' KEY_PART_1 has been known to deviate away from 8720_2M (effectively '8720') depending on the platform.

eg
Screenshot of BK7231 Easy UART Flasher showing Tuya config extraction tab

In an effort to find the assumed unknown, but present, seed key I spent quite a bit of time in LLM discussions, poring over various SDKs, programmatically trying different plain-text strings from dumps, PowerShell-generated key combinations numbering into the millions, breaking down libs with various csky tools (W800 dump).

What turned out to be different is that this “vault KV” path is not KEY_PART_1-seed driven at all.

Instead of the BK7231N/T-style config extractor model (where a platform “seed” influences the key used to decrypt a specific config blob), this vault mechanism is device-key centred and two-stage:

1) Key-record stage (per-device key material)
A dedicated “key record” region in flash contains the per-device keying material (including a 16-byte DeviceKey). That key record itself is wrapped/encrypted as a unit (sector/page sized, typically 4KB) and must be decrypted first before the DeviceKey can be read. This key-record page is wrapped using a fixed mechanism (typically a constant wrapper key), and decrypting it is what reveals the DeviceKey used for the vault pages.

2) Vault stage (derived vault key + page encryption)
The effective vault key is derived by combining a 16-byte BaseKey with that per-device 16-byte DeviceKey using bytewise addition mod 256. BaseKey is either caller-supplied (p_key) or constructed by the SDK when p_key is NULL (see below).

Code: Text
Log in, to see the code


The vault payload is then stored as fixed-size encrypted pages (typically 4KB each). Each decrypted page is expected to match a known page structure: a magic value in the header (e.g. 0x98761234) plus an integrity field (checksum or CRC) used to confirm the page decryption is correct.

Where the “default BaseKey” comes from (when not explicitly supplied)
The “default BaseKey” is not a flash-layout assumption; it comes from the NULL-key branch in Tuya’s DB init logic inside the prebuilt library.

Concretely, in libtuya_iot.a (object tuya_ws_db.c.o), ws_db_init checks whether p_key is NULL:

If p_key != NULL: it copies 16 bytes from the caller-provided buffer → BaseKey = p_key

If p_key == NULL: it constructs a 16-byte BaseKey in a 16-iteration loop by adding bytes from two embedded 16-byte constants.

Those two constants both contain the same ASCII seed:
HHRRQbyemofrtytf
(16 bytes)

So the NULL-key case is effectively:
BaseKey(byte_index) = (seed(byte_index) + seed(byte_index)) & 0xFF


Doubling the bytes of "HHRRQbyemofrtytf" yields:
9090a4a4a2c4f2cadadecce4e8f2e8cc

(which is the “Tuya default (NULL p_key)” BaseKey value).

How the JSON is recovered
Once the vault pages are decrypted correctly (i.e. page header/magic + checksum or CRC validates), the JSON is not separately encrypted again; it exists as plaintext within the reconstructed decrypted vault region. Extraction is therefore a straightforward carve/parse step over the decrypted bytes: locate candidate JSON starts ({ or [), bracket/quote-balance to a candidate end, then validate by parsing as JSON before emitting the object (with optional dedupe of identical objects/blocks when redundancy is present).

With that knowledge this tkinter/Python program was then developed with (a lot) of help from an LLM:

Screenshot of TY simple_flash Key Vault Decryptor with decoded JSON output

This uses the above mechanism to decrypt vault-style KV on dumps that are structured this way. I've added options to dedupe/collapse duplicate decoded objects (the vaults appear to contain duplicates / redundancy), export decoded JSON, export decrypted blobs (“swap” relates to a secondary region present in LN8825B dumps).

In summary, it turns out this method can successfully decrypt the KV on Tuya W800, TR6260, BK7252U, LN8825B and RTL8720CM. However, the key vaults don't appear to contain the same pin assignment information seen in successful extractions from BK7231N etc dumps.

Of all the dumps in Flashdumps, these can be decoded with this method:

Code: Text
Log in, to see the code


example JSON from a BK7252U:
Code: JSON
Log in, to see the code


This method may only apply to a minority of (mostly older) platforms, and the decoded JSON doesn’t appear to include pin assignments, so the immediate practical value is limited. That said, I still think it’s been a worthwhile adventure. Next step: should this be integrated into Easy Flasher?
Attachments:
  • TY_KV_Decrypt.zip (9.7 KB) You must be logged in to download this attachment.

About Author
divadiow
divadiow wrote 4839 posts with rating 852 , helped 420 times. Live in city Bristol. Been with us since 2023 year.

Comments

p.kaczmarek2 14 Jan 2026 09:57

Very interesting finding, how did you find that out? I think it's worth to add this to the flasher. However... that missing key issue we was searching for in Ghidra is still not yet solved? [Read more]

divadiow 14 Jan 2026 20:38

basically hours of trying stuff with LLM. So many angles explored, but ultimately feeding it the Tuya LN882X SDKs from the Lightning Semi FTP seemed to contain the answer. Expert handling of the LLM also... [Read more]

insmod 14 Jan 2026 20:53

While this can't extract pins from a backup, it's good for those with TuyaMCU devices. From https://www.elektroda.com/rtvforum/topic4097544.html { "tokenResponse": { ... [Read more]

divadiow 14 Jan 2026 21:19

true. I quite like seeing all the decoded json has to offer because it at the very least helps to label each dump file. ultimately, it'd be nice to see Easy Flasher support all methods and output full... [Read more]

p.kaczmarek2 14 Jan 2026 21:44

That's because there is some kind of secondary data structure, each fragment of ASCII text is prefixed with some kind of block header, and we don't parse it, we just naively skip non-printable charact... [Read more]

divadiow 16 Jan 2026 09:48

Good news. The pin data *is* present in at least ECR6600 KV, it's just the extractor was looking for real JSON. The extra info is stored as a brace-delimited key:value block with unquoted keys (not valid... [Read more]

divadiow 16 Jan 2026 10:01

RTL8720CM\Tuya_Master_Recessed_Downlight_(schemaID-000003wp7g)_key73ve9nrcgkhck_fy9psuhjzjga4tyg_CR3L_1.2.17.bin "data": { "Jsonver": "1.1.9", "brightmin": 10, "gmwb":... [Read more]

p.kaczmarek2 16 Jan 2026 10:07

Great finding, but I think that numerical values don't have to have quotes in JSON. Quotes are only required for keys (key names) and strings. I guess we need it in EasyFlasher [Read more]

divadiow 16 Jan 2026 12:26

ah OK. yes, a couple of new decoding and extraction methods now required in EF. + nice complete JSON output options Added after 2 [hours] 16 [minutes]: LN plain text https://obrazki.elektroda.pl/7066433600_1768562731_bigthumb.jpg... [Read more]

insmod 17 Jan 2026 05:29

https://github.com/openshwprojects/BK7231GUIFlashTool/pull/100 Based on TY_KV_Decrypt_Universal.py https://obrazki.elektroda.pl/4992193700_1768624084.png [Read more]

divadiow 17 Jan 2026 06:49

awesome! thank you ECR6600 Tuya_MCL-Y10-L-03W_keysceeymg7xqvk7_AXYU_1.0.15.bin https://obrazki.elektroda.pl/8892014300_1768628860_bigthumb.jpg Added after 2 [minutes]: RTL8720CM Tuya_Master_Recessed_Downlight_(schemaID-000003wp7g)_key73ve9nrcgkhck_fy9psuhjzjga4tyg_CR3L_1.2.17.bin... [Read more]

p.kaczmarek2 17 Jan 2026 11:54

Great! Ready to merge? [Read more]

divadiow 17 Jan 2026 12:56

so far so good for me in use and testing, but I've not been surgical re all changes in PR. [Read more]

insmod 18 Jan 2026 06:43

Debugging features in myDecrypt are gone (as is the function itself), because they're too slow. New algo scans everything in the dump, versus just after magic found. This solves the issue when MAGIC_FIRST_BLOCK... [Read more]

divadiow 18 Jan 2026 15:08

all good still? [Read more]

insmod 18 Jan 2026 23:25

It is, i've yet to encounter any error. It even successfully decrypts duplicate tuya configs in ~1gb file in about 15 seconds. I removed ILRepack from workflow, because antiviruses started going wild... [Read more]

divadiow 18 Jan 2026 23:48

excellent work [Read more]

insmod 19 Jan 2026 10:26

Added minimal KV parsing. (I don't know enough about the structure, but it often works. Is source available?) If user_param_key/baud_cfg checksum passes, then only that data is used. Otherwise it's using... [Read more]

divadiow 19 Jan 2026 11:17

nice https://obrazki.elektroda.pl/2279793600_1768817545_thumb.jpg Added after 5 [minutes]: with the XR806 offset info, shoud it read "And the Tuya section starts at 2052096 (0x1F5000),... [Read more]

FAQ

TL;DR: New method decrypts Tuya vault KV across 5 chip families and 11 sample dumps; "device-key centred and two-stage." [Elektroda, divadiow, post #21808089]

Why it matters: It finally explains why older Tuya platforms wouldn’t decrypt and gives a reproducible path to recover JSON from encrypted KV.

Who this is for: firmware hackers, repairers, and tool authors wondering how to decrypt Tuya KV on BK7252U/TR6260/W800/LN8825B/RTL8720CM without the old seed trick.

Quick facts:

Quick Facts

What changed versus the older BK7231N/T seed-based method?

Older BK7231N/T extractions used a platform seed (e.g., 8720) to decrypt specific blobs. The new finding shows some platforms use a device-key-centered, two-stage vault: first decrypt a wrapped key-record page to reveal a 16‑byte DeviceKey, then derive the vault key and decrypt 4KB pages. [Elektroda, divadiow, post #21808089]

Which chips and example dumps are confirmed to decrypt with this method?

Confirmed families include BK7252U, TR6260, W800, LN8825B, and RTL8720CM. The author lists 11 specific FlashDumps paths that successfully decode, covering doorbells, LED controllers, hubs, and downlights. Tooling validated page headers and checksums, yielding plaintext JSON. [Elektroda, divadiow, post #21808089]

How is the DerivedKey computed for vault pages?

Combine a 16‑byte BaseKey with the 16‑byte DeviceKey using bytewise addition modulo 256. In code terms: DerivedKey[i] = (BaseKey[i] + DeviceKey[i]) & 0xFF for i=0..15. This key decrypts fixed-size vault pages. [Elektroda, divadiow, post #21808089]

Where does the default BaseKey come from when p_key is NULL?

Tuya’s DB init logic constructs a 16‑byte BaseKey by adding two embedded 16‑byte constants. Both constants equal the ASCII seed “HHRRQbyemofrtytf,” so the bytes are doubled, yielding hex 9090a4a4…e8cc. “NULL-key branch” lives in libtuya_iot.a. [Elektroda, divadiow, post #21808089]

How do I verify that page decryption worked?

Each decrypted vault page should show a known header magic (example 0x98761234) and pass an integrity check (checksum or CRC). If either fails, your BaseKey/DeviceKey or page boundary is wrong. “Magic + CRC proves correctness.” [Elektroda, divadiow, post #21808089]

What JSON data can I actually recover?

You can carve plaintext JSON after decryption. Examples include uuid, psk_key, auth_key, SmartLife AP SSID, version fields, region, and tokens. The sample BK7252U JSON shows multiple objects and redundant blocks. Dedupe identical objects during export. [Elektroda, divadiow, post #21808089]

Does this method reveal GPIO pin assignments like BK7231N dumps did?

No. The decoded vaults lack the pin assignment info seen on BK7231N extractions. Practical value is JSON recovery, not pin mapping. Plan alternative pin discovery if needed. [Elektroda, divadiow, post #21808089]

How do I use the Python/tkinter tool to decrypt a dump? (3 steps)

  1. Load the flash dump and select the key-record region to unwrap the DeviceKey.
  2. Choose BaseKey (NULL default or custom) and decrypt vault pages.
  3. Export plaintext JSON and optionally dedupe or save decrypted blobs. “Swap” handles a secondary region in LN8825B. [Elektroda, divadiow, post #21808089]

What is the “key record” and how do I decrypt it?

It’s a dedicated flash page (often 4KB) wrapping per-device key material, including the 16‑byte DeviceKey. Decrypt it first using the fixed wrapper mechanism. After unwrapping, read DeviceKey to derive the vault key. [Elektroda, divadiow, post #21808089]

What is Tuya in this FAQ’s context?

Here, “Tuya” refers to the vendor ecosystem whose firmware stores an encrypted key-value vault and initializes DB keys as described. We focus on how its prebuilt library derives and applies BaseKey and DeviceKey during KV decryption. [Elektroda, divadiow, post #21808089]

Should this land in Easy Flasher?

Yes, a maintainer encouraged adding it. One open issue remains: the separate “missing key” puzzle investigated in Ghidra is still unsolved. Integrate the vault workflow, but track that gap. [Elektroda, p.kaczmarek2, post #21808335]

What are common failure modes or edge cases to watch for?

Edge cases include wrong page alignment, incorrect BaseKey in NULL vs caller-supplied paths, and non-matching magic or CRC. Also, expect no GPIO pin maps in recovered JSON. Some platforms outside the listed set may use different layouts. [Elektroda, divadiow, post #21808089]
Generated by the language model.
%}