Skip to content

README: correct key array size, and confirm the key on 5.15.x and OC200 hardware - #1

Open
willmcginnis wants to merge 1 commit into
jgrimard:mainfrom
willmcginnis:verified-on-5.15-and-oc200-hardware
Open

README: correct key array size, and confirm the key on 5.15.x and OC200 hardware#1
willmcginnis wants to merge 1 commit into
jgrimard:mainfrom
willmcginnis:verified-on-5.15-and-oc200-hardware

Conversation

@willmcginnis

Copy link
Copy Markdown

Thanks for publishing this — it's the clearest writeup of the format I found, and the only one that documents the inner v2# layer.

Two things, one a doc fix and one some independent confirmation.

Array size. The README describes the key array c as "a 224-byte array" with "the remaining 216 bytes used unchanged". It's 256 and 248 — the literal in omada_decrypt.py has 256 elements, so only the prose disagreed with the code.

Verified further back than the README claims. I decrypted two backups from OC200 v1 hardware controllers running 5.15.24.21 and 6.1.0.19 using this same key. Both produced gzip with a valid CRC32/ISIZE trailer and parsed as JSON (242,357 → 1,329,200 bytes, and 496,475 → 2,657,176 bytes). That's a full major version below the 6.2.10.17 you reverse-engineered from, and on hardware rather than a software controller, so the "other 6.x versions are likely compatible but unverified" caveat looks like it can be narrowed. I added a small table rather than editing your claim out from under you — adjust the wording however you prefer.

I also added a note on checking a candidate key without a controller to test against. Because Java's gzip header is constant and the keystream is static, every backup starts with the same ten ciphertext bytes, so XOR-ing those against the known header recovers the keystream prefix and any candidate key can be checked instantly. That's how I validated the format before I had a key at all, and it's what let me rule out a wrong implementation I'd been handed.

One incidental confirmation of something you already had right: your gzip-framing note says Java writes the OS byte as 0xFF. I'd seen that claimed backwards elsewhere, so I measured it on OpenJDK 21 — GZIPOutputStream emits 1f 8b 08 00 00 00 00 00 00 ff. Your README is correct.

Docs only, no code changes.

The key array `c` is 256 bytes with 248 passed through unchanged, not
224 with 216. The code already does this correctly (the literal in
omada_decrypt.py has 256 elements); only the prose disagreed.

Also records two independent confirmations that the key is unchanged
on 5.15.24.21 and 6.1.0.19, both OC200 v1 hardware controllers. That
extends the confirmed range a full major version below 6.2.10.17 and
onto hardware, so the "other 6.x versions are likely compatible but
unverified" caveat can be narrowed.

Adds a short note on checking a candidate key without a controller:
the constant 10-byte ciphertext prefix and the static keystream make
any candidate verifiable by XOR alone. Its last byte independently
confirms this README's gzip-framing note that Java writes OS as 0xFF,
measured on OpenJDK 21.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant