Conversation
The Dag, nitro-image and nitro-std keys are ordinary byte[] literals in bitwig.jar. They are built by bytecode -- newarray byte followed by one bastore per element -- so the bytes sit one every four, interleaved with opcodes, and never appear as a contiguous run. Searching the jars for them finds nothing in any encoding, which is what the docs concluded from; reading the arrays out of the bytecode finds all three. examples/extract_keys_from_jar.py scans every class for byte[] literals of 48+ bytes -- the cipher factory rejects anything shorter with "Key too short" -- and verifies each candidate against your own installed archives before reporting it. A 6.1 jar holds five such arrays across ~17,000 classes: the three keys, one in the Skia shader filesystem, and one unrelated table. Nothing prints as hex without --hex, and no key material is committed. Verified on 5.1.9 and 6.1, which carry byte-identical values for all three keys: 517/517 nitro-image members decompile on 6.1, 121/121 nitro-std members decrypt to valid Nitro source, and factory 0004 document metadata parses. A 5.1.9 nitro-image decrypts correctly but does not decompile, because the nitrobin container gained a field between the two builds. The image check therefore looks for the member's own name in the plaintext rather than decompiling, so a format gap is not read as a bad key. nitro-std is the same Dag cipher as nitro-image under a 96-byte key with a 192-byte IV, not a runtime PRNG, so it decrypts offline as well. The live-JVM controllers are unchanged and stay documented as the live route; only the claim that they are the only route is removed.
Scanning the jar took 3.1s, nearly all of it a Python loop over the bytes of all 31,476 classes. A class with no `newarray byte` in it cannot hold an array literal, and that check is a substring search rather than a loop, so it runs first: 281 classes walked instead of 31,476, and the whole scan drops to 0.8s. No candidate can hide behind it. The pattern the walk looks for begins with those two bytes by definition, so the filter has no false negatives. Output is byte-identical on 5.1.9 and 6.1: the same five arrays, the same three keys, the same two unidentified. Every class is still looked at rather than narrowing to the packages the keys occupy today. The three sit in three different places, and a package filter would quietly stop finding them the release one moves. Also corrects the class count. The earlier figure of ~17,000 came from unpacking the jar to disk on a case-insensitive filesystem, which silently collapses Bitwig's obfuscated names: `Aa.class` and `aA.class` are two classes and become one file. The jar holds 31,476; unzip on macOS leaves 17,290 of them. The survey itself was read from the archive rather than from the unpacked tree, so its result was never affected - five arrays is still five arrays - but the number quoted beside it was wrong, and a reader unpacking the jar to check would have hit the same trap. That is now written down where it bites.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Dag, nitro-image and nitro-std keys are ordinary byte[] literals in bitwig.jar. They are built by bytecode -- newarray byte followed by one bastore per element -- so the bytes sit one every four, interleaved with opcodes, and never appear as a contiguous run. Searching the jars for them finds nothing in any encoding, which is what the docs concluded from; reading the arrays out of the bytecode finds all three. This supersedes the 0.1.1 removal of --from-jar: a static route does exist, it simply is not a contiguous byte string.
examples/extract_keys_from_jar.py scans every class for byte[] literals of 48+ bytes -- the cipher factory rejects anything shorter with "Key too short" -- and verifies each candidate against your own installed archives before reporting it. A 6.1 jar holds five such arrays across its 31,476 classes: the three keys, one in the Skia shader filesystem, and one unrelated table. Only the 281 classes that contain a newarray byte opcode are walked, which no candidate can hide from, because the pattern being matched starts with those two bytes; the whole scan takes about 0.8s. Nothing prints as hex without --hex, and no key material is committed.
One trap if you unpack the jar to check this by hand: Bitwig's obfuscated names differ only by case, so Aa.class and aA.class are two classes, and unzip on a case-insensitive filesystem silently collapses them to 17,290 of the 31,476.
The script reads the archive directly and is not affected.
Verified on 5.1.9 and 6.1, which carry byte-identical values for all three keys: 517/517 nitro-image members decompile on 6.1, 121/121 nitro-std members decrypt to valid Nitro source, and factory 0004 document metadata parses.
A 5.1.9 nitro-image decrypts correctly but does not decompile, because the nitrobin container gained a field between the two builds. The image check therefore looks for the member's own name in the plaintext rather than decompiling, so a format gap is not read as a bad key.
nitro-std is the same Dag cipher as nitro-image under a 96-byte key with a 192-byte IV, not a runtime PRNG, so it decrypts offline as well. The live-JVM controllers are unchanged and stay documented as the live route; only the claim that they are the only route is removed.