This repository has been archived by the owner on Sep 9, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Change to index file creation process
- Loading branch information
Showing
4 changed files
with
142 additions
and
88 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,48 @@ | ||
from urllib3 import disable_warnings as disable_url_warnings | ||
from TinGen import UTinGen | ||
from argparse import ArgumentParser | ||
from pathlib import Path | ||
from binascii import unhexlify | ||
from TinGen.utils import create_encrypted_index | ||
from pathlib import Path | ||
from TinGen.utils import CompressionFlag | ||
from TinGen.utils import create_tinfoil_index | ||
from TinGen import UTinGen | ||
from urllib3 import disable_warnings as disable_url_warnings | ||
|
||
if __name__ == "__main__": | ||
disable_url_warnings() | ||
parser = ArgumentParser(description="Script that will allow you to easily generate an index file with Google Drive file links for use with Tinfoil without requiring authentication.") | ||
parser.add_argument(nargs="*", metavar="FOLDER_ID_TO_SCAN", dest="folder_ids", help="Folder ID of Google Drive folders to scan. Can use more than 1 folder IDs at a time. FOLDERS MUST BE PUBLIC FOR SCRIPT TO WORK") | ||
parser.add_argument("--index-file", metavar="INDEX_FILE_PATH", default="index.tfl", help="File Path for unencrypted index file to update.") | ||
parser.add_argument("--encrypt", nargs="?", metavar="ENC_INDEX_FILE_PATH", const="enc_index.tfl", help="Use this flag is you want to encrypt the resulting index file.") | ||
parser.add_argument("--public-key", metavar="PUBLIC_KEY_FILE_PATH", default="public.pem", help="File Path for Public Key to encrypt with.") | ||
parser.add_argument("--vm-file", help="File Path for VM File") | ||
parser.add_argument("--success", metavar="SUCCESS_MESSAGE", help="Success Message to add to index.") | ||
|
||
args = parser.parse_args() | ||
|
||
generator = UTinGen(index_path=args.index_file) | ||
generator.index_folders(args.folder_ids, success=args.success) | ||
|
||
if args.encrypt: | ||
create_encrypted_index(generator.index_json, Path(args.encrypt), Path(args.public_key), None if not args.vm_file else Path(args.vm_file)) | ||
disable_url_warnings() | ||
parser = ArgumentParser(description="Script that will allow you to easily generate an index file with Google Drive file links for use with Tinfoil without requiring authentication") | ||
parser.add_argument(nargs="*", metavar="FOLDER_ID_TO_SCAN", dest="folder_ids", help="Folder ID of Google Drive folders to scan") | ||
parser.add_argument("--index-path", default="index.tfl", help="File path for unencrypted index file to update") | ||
parser.add_argument("--add-nsw-files-without-title-id", action="store_true", help="Adds files without title ID") | ||
parser.add_argument("--add-non-nsw-files", action="store_true", help="Adds files without valid NSW ROM extension(NSP/NSZ/XCI/XCZ) to index") | ||
parser.add_argument("--encrypt", action="store_true", help="Encrypt the resulting index file") | ||
parser.add_argument("--public-key", default="public.pem", help="File path for public key to encrypt with") | ||
parser.add_argument("--vm-file", help="File path for VM file") | ||
parser.add_argument("--success", help="Success message to add to index") | ||
compression_parser = parser.add_mutually_exclusive_group() | ||
compression_parser.add_argument("--zstandard", action="store_true", help="Compresses index with Zstandard compression method") | ||
compression_parser.add_argument("--zlib", action="store_true", help="Compresses index with Zlib compression method") | ||
compression_parser.add_argument("--no-compress", action="store_true", help="Flag to not compress index") | ||
|
||
args = parser.parse_args() | ||
|
||
generator = UTinGen() | ||
generator.index_generator(args.folder_ids, add_non_nsw_files=args.add_non_nsw_files, add_nsw_files_without_title_id=args.add_nsw_files_without_title_id, success=args.success) | ||
|
||
compression_flag = CompressionFlag.ZSTD_COMPRESSION | ||
|
||
if args.zstandard: | ||
compression_flag = CompressionFlag.ZSTD_COMPRESSION | ||
elif args.zlib: | ||
compression_flag = CompressionFlag.ZLIB_COMPRESSION | ||
elif args.no_compress: | ||
compression_flag = CompressionFlag.NO_COMPRESSION | ||
|
||
vm_file = None | ||
public_key = None | ||
|
||
if args.encrypt: | ||
if args.vm_file: | ||
vm_file = args.vm_file | ||
if args.public_key: | ||
public_key = args.public_key | ||
|
||
create_tinfoil_index(generator.index, args.index_path, compression_flag, public_key, vm_file) |
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,77 @@ | ||
from Crypto.Cipher.PKCS1_OAEP import new as new_pkcs1_oaep_ctx | ||
from Crypto.Cipher.AES import new as new_aes_ctx | ||
from binascii import hexlify | ||
from binascii import unhexlify | ||
from Crypto.Cipher.AES import MODE_ECB | ||
from Crypto.PublicKey.RSA import import_key as import_rsa_key | ||
from Crypto.Cipher.AES import new as new_aes_ctx | ||
from Crypto.Cipher.PKCS1_OAEP import new as new_pkcs1_oaep_ctx | ||
from Crypto.Hash import SHA256 | ||
from Crypto.PublicKey.RSA import import_key as import_rsa_key | ||
from enum import IntEnum | ||
from json import dumps as json_serialize | ||
from pathlib import Path | ||
from random import randint | ||
from json import dumps as json_serialize | ||
from zlib import compress as zlib_compress | ||
from zstandard import ZstdCompressor | ||
from binascii import hexlify | ||
from binascii import unhexlify | ||
|
||
def rand_aes_key_generator() -> bytes: | ||
return randint(0, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_bytes(0x10, "big") | ||
|
||
def create_encrypted_index(index_to_write: dict, out_path: Path, rsa_pub_key_path: Path, vm_path: Path): | ||
if rsa_pub_key_path.is_file(): | ||
index_buffer = bytes(json_serialize(index_to_write).encode()) | ||
rsa_pub_key = import_rsa_key(open(rsa_pub_key_path).read()) | ||
rand_aes_key = rand_aes_key_generator() | ||
class CompressionFlag(IntEnum): | ||
ZLIB_COMPRESSION = 0xFE | ||
ZSTD_COMPRESSION = 0xFD | ||
NO_COMPRESSION = 0x00 | ||
|
||
aes_ctx = new_aes_ctx(rand_aes_key, MODE_ECB) | ||
pkcs1_oaep_ctx = new_pkcs1_oaep_ctx(rsa_pub_key, hashAlgo=SHA256, label=b"") | ||
|
||
to_compress_buffer = b"" | ||
def create_tinfoil_index(index_to_write: dict, out_path: Path, compression_flag: int, rsa_pub_key_path: Path=None, vm_path: Path=None): | ||
to_compress_buffer = b"" | ||
|
||
if vm_path is not None and vm_path.is_file(): | ||
to_compress_buffer += b"\x13\x37\xB0\x0B" | ||
vm_buffer = b"" | ||
if vm_path is not None and vm_path.is_file(): | ||
to_compress_buffer += b"\x13\x37\xB0\x0B" | ||
vm_buffer = b"" | ||
|
||
with open(vm_path, "rb") as vm_stream: | ||
vm_buffer += vm_stream.read() | ||
with open(vm_path, "rb") as vm_stream: | ||
vm_buffer += vm_stream.read() | ||
|
||
to_compress_buffer += len(vm_buffer).to_bytes(4, "little") | ||
to_compress_buffer += vm_buffer | ||
to_compress_buffer += len(vm_buffer).to_bytes(4, "little") | ||
to_compress_buffer += vm_buffer | ||
|
||
to_compress_buffer += index_buffer | ||
to_compress_buffer += bytes(json_serialize(index_to_write).encode()) | ||
|
||
index_compressed_buffer = ZstdCompressor(level=22).compress(to_compress_buffer) | ||
to_write_buffer = b"" | ||
session_key = b"" | ||
|
||
session_key = pkcs1_oaep_ctx.encrypt(rand_aes_key) | ||
encrypted_index = aes_ctx.encrypt(index_compressed_buffer + (b"\x00" * (0x10 - (len(index_compressed_buffer) % 0x10)))) | ||
if compression_flag == CompressionFlag.ZSTD_COMPRESSION: | ||
to_write_buffer += ZstdCompressor(level=22).compress(to_compress_buffer) | ||
|
||
Path(out_path.parent).mkdir(parents=True, exist_ok=True) | ||
elif compression_flag == CompressionFlag.ZLIB_COMPRESSION: | ||
to_write_buffer += zlib_compress(to_compress_buffer, 9) | ||
|
||
with open(out_path, "wb") as out_stream: | ||
out_stream.write(b"TINFOIL") | ||
out_stream.write(b"\xFD") # Compression indicator flag - 0xF0 | 0x0D for zstandard, 0xF0 | 0x0E for zlib, 0xF0 | 0x00 for no compression | ||
out_stream.write(session_key) | ||
out_stream.write(len(index_compressed_buffer).to_bytes(8, "little")) | ||
out_stream.write(encrypted_index) | ||
elif compression_flag == CompressionFlag.NO_COMPRESSION: | ||
to_write_buffer += to_compress_buffer | ||
|
||
else: | ||
print(f"{rsa_pub_key_path} does not exist. Cannot encrypt without key.") | ||
raise NotImplementedError("Compression method supplied is not implemented yet.") | ||
|
||
to_write_buffer += (b"\x00" * (0x10 - (len(to_write_buffer) % 0x10))) | ||
|
||
if rsa_pub_key_path.is_file(): | ||
def rand_aes_key_generator() -> bytes: | ||
return randint(0, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_bytes(0x10, "big") | ||
|
||
rsa_pub_key = import_rsa_key(open(rsa_pub_key_path).read()) | ||
rand_aes_key = rand_aes_key_generator() | ||
|
||
pkcs1_oaep_ctx = new_pkcs1_oaep_ctx(rsa_pub_key, hashAlgo=SHA256, label=b"") | ||
aes_ctx = new_aes_ctx(rand_aes_key, MODE_ECB) | ||
|
||
session_key += pkcs1_oaep_ctx.encrypt(rand_aes_key) | ||
to_write_buffer = aes_ctx.encrypt(to_write_buffer) | ||
|
||
else: | ||
session_key += b"\x00" * 0xFF | ||
|
||
Path(out_path.parent).mkdir(parents=True, exist_ok=True) | ||
|
||
with open(out_path, "wb") as out_stream: | ||
out_stream.write(b"TINFOIL") | ||
out_stream.write(bytes(compression_flag)) | ||
out_stream.write(session_key) | ||
out_stream.write(len(to_write_buffer).to_bytes(8, "little")) | ||
out_stream.write(to_write_buffer) |