-
Notifications
You must be signed in to change notification settings - Fork 22
ROFL Container Notes
Boreeas edited this page May 1, 2014
·
4 revisions
The ROFL container is documented in the repository as C structs.
- "ubyte" - unsigned byte - 1 byte
- "ushort" - unsigned short - 2 bytes
- "uint" - unsigned int - 4 bytes
- "ulong" - unsigned long - 4 bytes
- "ulonglong" - unsigned long long - 8 bytes
- All values are little-endian unless mentioned otherwise
ROFL files begin with 6 bytes: "RIOT\0\0" (that's 'RIOT' followed by two null bytes).
Next up is the file's signature, which is 256 bytes long.
Now we have:
- Header length (ushort)
- File length (uint)
- Metadata offset (uint)
- Metadata length (uint)
- Payload header offset (uint)
- Payload header length (uint)
- Payload offset (uint)
This is the JSON encoded metadata. It's "Header>Length fields>Metadata length" bytes long.
- Game ID (ulonglong)
- Game length (uint)
- Keyframe count (uint)
- Chunk count (uint)
- End startup chunk ID (uint)
- Start game chunk ID (uint)
- Keyframe interval (uint)
- Encryption key length (ushort)
- Encryption key (string, see encryption key length, base64 encoded)
Repeated chunk headers:
- Chunk ID (uint)
- Chunk type (ubyte - 0 indicates keyframe, 1 indicates chunk)
- Chunk length (uint)
- Next chunk ID (uint)
- Offset (uint)
Payload header format is same as chunk header - see above.
These are in an as-yet unknown format. For more information, refer to ROFL/Payload.
To decrypt the files, you need to do the following (in pseudocode):
function decrypt(key, data) {
decrypted = blowfish_ecb_mode.decrypt(key, data)
return pkcs5padding.remove(decrypted)
}
function decompress(data) {
return gzip.decompress(data)
}
function make_encryption_key(file_data) {
raw_stored_encryption_key = base64_decode(file_data.payload_header.encryption_key)
game_id_str = str(file_data.payload_header.game_id)
return decrypt(game_id_str, raw_stored_encryption_key)
}
decrypted_chunks = []
encryption_key = make_encryption_key(file_data)
for (chunk in chunks) {
decrypted_chunks.add(decompress(decrypt(encryption_key, chunk)))
}
For examples of this, see the references section.
In summary:
- Chunks are encrypted with a "Chunk Encryption Key"
- The "Chunk Encryption Key" is a decrypted version of the encryption key stored in the payload header
- The encryption method used for both chunk encryption and encryption key encryption is Blowfish, in ECB mode, with PKCS#5 padding
- Chunks are also compressed, and should decrypt to have a valid GZip header (NB: not plain zlib/deflate, full gzip)
- The encryption key is encrypted using the Game ID, in base 10, as a string, as the key (e.g. "123457890")
- Ruby unpacker script [does not handle decrypting - decrypt.rb script in repo is designed for a slightly different header format and will not work with a "plain" Riot header]
- Python unpacker script [unpacks ROFL file, decrypts and decompresses chunks]