Decrypt Ruckus Backups
This code works for backups and debug logs, and also ZoneDirector Software Images.
TIP
Use e.g. 7-Zip to explore/extract the decrypted .tgz
archive.
Online Tool
Your internet browser will use JavaScript to decrypt your backup.
No data from the encrypted file will leave your PC.
Can I also encrypt files?
If you enjoy living dangerously, then you can try re-encrypting modified Unleashed and ZoneDirector backups using this tool.
If a modified backup breaks your device, hopefully a factory reset will fix things. But be careful!
Ruckus Crypt bash Functions (using Python)
WARNING
Python is very slow.
To keep things speedy(ish), this code is quite ugly and complicated, and uses lots of memory.
The online tool, above, and Powershell functions, below, are much faster.
NOTE
These functions don't yet understand the new encryption method used by Unleashed 200.15.
If you need to decrypt a backup from Unleashed 200.15+ then please use the Online Tool, above, or aioruckus.
function rks_encrypt {
RUCKUS_SRC="$1" RUCKUS_DEST="$2" python3 - <<END
import os
import struct
input_path = os.environ['RUCKUS_SRC']
output_path = os.environ['RUCKUS_DEST']
(xor_int, xor_flip) = struct.unpack('QQ', b')\x1aB\x05\xbd,\xd6\xf25\xad\xb8\xe0?T\xc58')
structInt8 = struct.Struct('Q')
with open(input_path, "rb") as input_file:
with open(output_path, "wb") as output_file:
input_len = os.path.getsize(input_path)
input_blocks = input_len // 8
output_int = 0
input_data = input_file.read(input_blocks * 8)
for input_int in struct.unpack_from(str(input_blocks) + "Q", input_data):
output_int ^= xor_int ^ input_int
xor_int ^= xor_flip
output_file.write(structInt8.pack(output_int))
input_block = input_file.read()
input_padding = 8 - len(input_block)
input_int = structInt8.unpack(input_block.ljust(8, bytes([input_padding | input_padding << 4])))[0]
output_int ^= xor_int ^ input_int
output_file.write(structInt8.pack(output_int))
END
}
function rks_decrypt {
RUCKUS_SRC="$1" RUCKUS_DEST="$2" python3 - <<END
import os
import struct
input_path = os.environ['RUCKUS_SRC']
output_path = os.environ['RUCKUS_DEST']
(xor_int, xor_flip) = struct.unpack('QQ', b')\x1aB\x05\xbd,\xd6\xf25\xad\xb8\xe0?T\xc58')
structInt8 = struct.Struct('Q')
with open(input_path, "rb") as input_file:
with open(output_path, "wb") as output_file:
input_data = input_file.read()
previous_input_int = 0
for input_int in struct.unpack_from(str(len(input_data) // 8) + "Q", input_data):
output_bytes = structInt8.pack(previous_input_int ^ xor_int ^ input_int)
xor_int ^= xor_flip
previous_input_int = input_int
output_file.write(output_bytes)
output_padding = int.from_bytes(output_bytes[-1:], 'big') & 0xf
output_file.seek(-output_padding, os.SEEK_END)
output_file.truncate()
END
}
Decrypt a backup
$ rks_decrypt ruckus_db_073122_14_17.bak ruckus_db_073122_14_17.bak.tgz
Re-encrypt a backup
$ rks_encrypt ruckus_db_073122_14_17.bak.tgz ruckus_db_073122_14_17.modded.bak
Ruckus Crypt PowerShell Functions (using C#)
WARNING
If you want to process your backup for re-upload then you need to be careful to use unix line endings in a few places, so it will probably be easier to tinker in WSL if you're a Windows user.
NOTE
These functions don't yet understand the new encryption method used by Unleashed 200.15.
If you need to decrypt a backup from Unleashed 200.15+ then please use the Online Tool, above, or aioruckus.
Add-Type -Language CSharp @"
using System;
using System.IO;
namespace Ms264556
{
public static class Ruckus
{
private static readonly byte[] XorBytes = new byte[] { 0x29, 0x1A, 0x42, 0x05, 0xbd, 0x2c, 0xd6, 0xf2, 0x1c, 0xb7, 0xfa, 0xe5, 0x82, 0x78, 0x13, 0xca };
public static void DecryptFile(string sourcePath, string destinationPath)
{
var inputBlock = new byte[8];
var previousInputBlock = new byte[8];
var outputBlock = new byte[8];
using (var input = File.OpenRead(sourcePath))
using (var output = File.Open(destinationPath, FileMode.Create))
{
int offset = 0;
while (true)
{
var bytesRead = input.Read(inputBlock, 0, 8);
if (offset > 7)
{
var bytesToWrite = bytesRead == 0 ? (8 - outputBlock[7]) & 0xf : 8;
if (bytesToWrite > 0) { output.Write(outputBlock, 0, bytesToWrite); }
}
if (bytesRead == 0) break;
if (bytesRead != 8) throw new Exception("Corrupt input file");
for (int i = 0; i < bytesRead; i++)
{
outputBlock[i] = (byte)(XorBytes[offset++ % 16] ^ inputBlock[i] ^ previousInputBlock[i]);
previousInputBlock[i] = inputBlock[i];
}
}
}
}
public static void EncryptFile(string sourcePath, string destinationPath)
{
var inputBlock = new byte[8];
var previousInputBlock = new byte[8];
using (var input = File.OpenRead(sourcePath))
using (var output = File.Open(destinationPath, FileMode.Create))
{
int offset = 0;
while (true)
{
var bytesRead = input.Read(inputBlock, 0, 8);
if (bytesRead < 8)
{
byte paddingBytes = (byte)(8 - bytesRead);
byte padding = (byte)(paddingBytes | paddingBytes << 4);
for (int i = 0; i < paddingBytes; i++) { inputBlock[i + bytesRead] = padding; }
}
for (int i = 0; i < 8; i++)
{
inputBlock[i] = (byte)(XorBytes[offset++ % 16] ^ inputBlock[i] ^ previousInputBlock[i]);
previousInputBlock[i] = inputBlock[i];
}
output.Write(inputBlock, 0, 8);
if (bytesRead < 8) break;
}
}
}
}
}
"@;
Decrypt a backup
[Ms264556.Ruckus]::DecryptFile("C:\Users\Ms264556\Downloads\ruckus_db_073122_14_17.bak", "C:\Users\Ms264556\Downloads\ruckus_db_073122_14_17.bak.tgz")
Re-encrypt a backup
[Ms264556.Ruckus]::EncryptFile("C:\Users\Ms264556\Downloads\ruckus_db_073122_14_17.bak.tgz", "C:\Users\Ms264556\Downloads\ruckus_db_073122_14_17.modded.bak")