|
| 1 | +import argparse |
| 2 | +import os |
| 3 | +import rsa |
| 4 | +import struct |
| 5 | +from binascii import unhexlify |
| 6 | +from Crypto.Cipher import AES |
| 7 | +from nlzss import encode_file |
| 8 | + |
| 9 | +def u8(data): |
| 10 | + return struct.pack(">B", data) |
| 11 | + |
| 12 | + |
| 13 | +def u16(data): |
| 14 | + return struct.pack(">H", data) |
| 15 | + |
| 16 | + |
| 17 | +def u32(data): |
| 18 | + return struct.pack(">I", data) |
| 19 | + |
| 20 | +parser = argparse.ArgumentParser(description="Sign / Encrypt WiiConnect24 files.") |
| 21 | +parser.add_argument("-t", "--type", |
| 22 | + type=str, nargs="+", |
| 23 | + help="Type of file. Set either enc for encrypted file, or dec for non-encrypted file.") |
| 24 | +parser.add_argument("-in", "--input", |
| 25 | + type=str, nargs="+", |
| 26 | + help="Input file.") |
| 27 | +parser.add_argument("-out", "--output", |
| 28 | + type=str, nargs="+", |
| 29 | + help="Output file.") |
| 30 | +parser.add_argument("-c", "--compress", |
| 31 | + type=str, nargs="+", |
| 32 | + help="If set, this will compress the file before signing with LZ10.") |
| 33 | +parser.add_argument("-key", "--aes-key", |
| 34 | + type=str, nargs="+", |
| 35 | + help="AES key in hex or a path.") |
| 36 | +parser.add_argument("-iv", "--iv-key", |
| 37 | + type=str, nargs="+", |
| 38 | + help="AES IV in hex or a path.") |
| 39 | +parser.add_argument("-rsa", "--rsa-key-path", |
| 40 | + type=str, nargs="+", |
| 41 | + help="RSA private key path. If not specified, it will use the private key in Private.pem if it exists.") |
| 42 | + |
| 43 | +args = parser.parse_args() |
| 44 | + |
| 45 | +if args.compress is not None: |
| 46 | + encode_file(in_path=args.input[0], out_path="temp") |
| 47 | + filename = "temp" |
| 48 | +else: |
| 49 | + filename = args.input[0] |
| 50 | + |
| 51 | +with open(filename, "rb") as f: |
| 52 | + data = f.read() |
| 53 | + |
| 54 | +if args.rsa_key_path is not None: |
| 55 | + rsa_key_path = args.rsa_key_path[0] |
| 56 | +else: |
| 57 | + rsa_key_path = "Private.pem" |
| 58 | + |
| 59 | +with open(rsa_key_path, "rb") as source_file: |
| 60 | + private_key_data = source_file.read() |
| 61 | + |
| 62 | +private_key = rsa.PrivateKey.load_pkcs1(private_key_data, "PEM") |
| 63 | + |
| 64 | +signature = rsa.sign(data, private_key, "SHA-1") |
| 65 | + |
| 66 | +if args.type[0] == "enc": |
| 67 | + if args.iv_key is not None: |
| 68 | + try: |
| 69 | + iv = unhexlify(args.iv_key[0]) |
| 70 | + except: |
| 71 | + iv = open(args.iv_key[0], "rb").read() |
| 72 | + else: |
| 73 | + iv = os.urandom(16) |
| 74 | + |
| 75 | + try: |
| 76 | + key = unhexlify(args.aes_key[0]) |
| 77 | + except: |
| 78 | + key = open(args.aes_key[0], "rb").read() |
| 79 | + |
| 80 | + aes = AES.new(key, AES.MODE_OFB, iv=iv) |
| 81 | + processed = aes.encrypt(data) |
| 82 | +elif args.type[0] == "dec": |
| 83 | + processed = data |
| 84 | + |
| 85 | +content = {} |
| 86 | + |
| 87 | +content["magic"] = b"WC24" if args.type[0] == "enc" else u32(0) |
| 88 | +content["version"] = u32(1) if args.type[0] == "enc" else u32(0) |
| 89 | +content["filler"] = u32(0) |
| 90 | +content["crypt_type"] = u8(1) if args.type[0] == "enc" else u8(0) |
| 91 | +content["pad"] = u8(0) * 3 |
| 92 | +content["reserved"] = u8(0) * 32 |
| 93 | +content["iv"] = iv if args.type[0] == "enc" else u8(0) * 16 |
| 94 | +content["signature"] = signature |
| 95 | +content["data"] = processed |
| 96 | + |
| 97 | +if os.path.exists(args.output[0]): |
| 98 | + os.remove(args.output[0]) |
| 99 | + |
| 100 | +if args.type[0] == "dec": |
| 101 | + os.remove("temp") |
| 102 | + |
| 103 | +for values in content.values(): |
| 104 | + with open(args.output[0], "ab+") as f: |
| 105 | + f.write(values) |
| 106 | + |
| 107 | +print("Completed Successfully") |
0 commit comments