|
#!/usr/bin/python3 |
|
|
|
import argparse |
|
import os |
|
import sys |
|
from typing import BinaryIO |
|
import xxtea |
|
|
|
|
|
BLOCK_LENGTH = 0x8000 |
|
MIN_BYTE_VALUE, MAX_BYTE_VALUE = 0, 255 |
|
HLX_SFPX_KEY = b'q!9RbM0Dn@Mq5hPb' # found in /usr/lib/libponimg.so |
|
# HL-1B keys are encoded with a Caesar cipher |
|
# dec_key = ''.join([chr((c + 16) % 256) for c in enc_key]) |
|
HL_1B_TR69_KEY = b'15gUPh^sw9@_kfgM' # encoded as !%WE@XNcg)0O[VW= in /userfs/bin/tr69 |
|
HL_1B_OMCI_KEY = b'fia9=RZeB1BL6f=e' # encoded as VYQ)-BJU2!2<&V-U in /lib/libapi_omci_adpt.so and /lib/libcfg_clisvc.so |
|
|
|
|
|
class ListKeyAction(argparse.Action): |
|
def __init__(self, option_strings, dest=argparse.SUPPRESS, default=argparse.SUPPRESS, help=None): |
|
super().__init__(option_strings=option_strings, dest=dest, default=default, nargs=0, help=help) |
|
|
|
def __call__(self, parser, namespace, values, option_string=None): |
|
print(*[f"{key:>15}: {value.decode('ascii')}" for key, value in globals().items() if key.endswith('KEY')], sep=os.linesep) |
|
parser.exit() |
|
|
|
|
|
def decrypt32k(in_stream: BinaryIO, out_stream: BinaryIO, dec_key: bytes) -> None: |
|
while pad_dat := in_stream.read(1): |
|
if (enc_dat := in_stream.read(BLOCK_LENGTH + int.from_bytes(pad_dat))) and len(enc_dat): |
|
dec_dat = xxtea.decrypt(enc_dat, dec_key) |
|
if len(dec_dat): |
|
out_stream.write(dec_dat) |
|
else: |
|
raise BufferError('Decrypted data error') |
|
|
|
|
|
def encrypt32k(in_stream: BinaryIO, out_stream: BinaryIO, enc_key: bytes) -> None: |
|
while (dec_dat := in_stream.read(BLOCK_LENGTH)) and len(dec_dat): |
|
enc_dat = xxtea.encrypt(dec_dat, enc_key) |
|
pad_len = len(enc_dat) - len(dec_dat) |
|
if MIN_BYTE_VALUE < pad_len <= MAX_BYTE_VALUE: |
|
out_stream.write(pad_len.to_bytes(1)) |
|
out_stream.write(enc_dat) |
|
else: |
|
raise ValueError('Encrypted data length out of range') |
|
|
|
|
|
def genkey(key: str) -> bytes: |
|
return f"{key[:16]:<16}".encode('ascii') |
|
|
|
|
|
def main() -> None: |
|
parser = argparse.ArgumentParser(description='HALNy HLX-SFPX Firmware Tool') |
|
parser.add_argument('-l', '--list', action=ListKeyAction, help='list known keys') |
|
parser.add_argument('-k', '--key', default=HLX_SFPX_KEY, type=genkey, help='128-bit key') |
|
parser.add_argument('-e', '--encrypt', action='store_true') |
|
parser.add_argument("infile", type=argparse.FileType('rb')) |
|
parser.add_argument("outfile", type=argparse.FileType('wb')) |
|
|
|
args = parser.parse_args() |
|
|
|
try: |
|
with args.infile as in_file, args.outfile as out_file: |
|
crypt = encrypt32k if args.encrypt else decrypt32k |
|
crypt(in_file, out_file, args.key) |
|
except Exception as err: |
|
os.remove(args.outfile.name) |
|
sys.exit(f"Error: {err}") |
|
else: |
|
sys.exit(0) |
|
|
|
if __name__ == '__main__': |
|
main() |