diff --git a/app/app.py b/app/app.py index 5e68871..88e3878 100644 --- a/app/app.py +++ b/app/app.py @@ -1,4 +1,4 @@ -from flask import Flask, render_template, request, redirect, url_for, jsonify, send_from_directory +from flask import Flask, render_template, request, redirect, url_for, jsonify, send_from_directory, Response from flask_login import LoginManager from functools import wraps import yaml @@ -172,6 +172,10 @@ def access_tinfoil_shop(): shop["referrer"] = f"https://{request.verified_host}" shop["files"] = gen_shop_files(db) + + if app_settings['shop']['encrypt']: + return Response(encrypt_shop(shop), mimetype='application/octet-stream') + return jsonify(shop) if all(header in request.headers for header in TINFOIL_HEADERS): diff --git a/app/constants.py b/app/constants.py index d4ca67b..a8080a1 100644 --- a/app/constants.py +++ b/app/constants.py @@ -29,7 +29,7 @@ "shop": { "motd": "Welcome to your own shop!", "public": False, - "encrypt": False, + "encrypt": True, "clientCertPub": "-----BEGIN PUBLIC KEY-----", "clientCertKey": "-----BEGIN PRIVATE KEY-----", "host": "", diff --git a/app/shop.py b/app/shop.py index 3cfa66c..1e3ee22 100644 --- a/app/shop.py +++ b/app/shop.py @@ -1,4 +1,22 @@ from db import * +from Crypto.PublicKey import RSA +from Crypto.Cipher import PKCS1_OAEP +from Crypto.Hash import SHA256 +from Crypto.Cipher import AES +import zstandard as zstd +import random +import json + +# https://github.com/blawar/tinfoil/blob/master/docs/files/public.key 1160174fa2d7589831f74d149bc403711f3991e4 +TINFOIL_PUBLIC_KEY = '''-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvPdrJigQ0rZAy+jla7hS +jwen8gkF0gjtl+lZGY59KatNd9Kj2gfY7dTMM+5M2tU4Wr3nk8KWr5qKm3hzo/2C +Gbc55im3tlRl6yuFxWQ+c/I2SM5L3xp6eiLUcumMsEo0B7ELmtnHTGCCNAIzTFzV +4XcWGVbkZj83rTFxpLsa1oArTdcz5CG6qgyVe7KbPsft76DAEkV8KaWgnQiG0Dps +INFy4vISmf6L1TgAryJ8l2K4y8QbymyLeMsABdlEI3yRHAm78PSezU57XtQpHW5I +aupup8Es6bcDZQKkRsbOeR9T74tkj+k44QrjZo8xpX9tlJAKEEmwDlyAg0O5CLX3 +CQIDAQAB +-----END PUBLIC KEY-----''' def gen_shop_files(db): shop_files = [] @@ -21,3 +39,26 @@ def gen_shop_files(db): 'size': size }) return shop_files + +def encrypt_shop(shop): + input = json.dumps(shop).encode('utf-8') + # random 128-bit AES key (16 bytes), used later for symmetric encryption (AES) + aesKey = random.randint(0,0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_bytes(0x10, 'big') + # zstandard compression + flag = 0xFD + cctx = zstd.ZstdCompressor(level=22) + buf = cctx.compress(input) + sz = len(buf) + + # Encrypt the AES key with RSA, PKCS1_OAEP padding scheme + pubKey = RSA.importKey(TINFOIL_PUBLIC_KEY) + cipher = PKCS1_OAEP.new(pubKey, hashAlgo = SHA256, label=b'') + # Now the AES key can only be decrypted with Tinfoil private key + sessionKey = cipher.encrypt(aesKey) + + # Encrypting the Data with AES + cipher = AES.new(aesKey, AES.MODE_ECB) + buf = cipher.encrypt(buf + (b'\x00' * (0x10 - (sz % 0x10)))) + + binary_data = b'TINFOIL' + flag.to_bytes(1, byteorder='little') + sessionKey + sz.to_bytes(8, 'little') + buf + return binary_data diff --git a/app/templates/settings.html b/app/templates/settings.html index cd240b7..95e6906 100644 --- a/app/templates/settings.html +++ b/app/templates/settings.html @@ -268,10 +268,10 @@

Shop

+ aria-describedby="encryptShopCheckHelp"> -
Serve encrypted shop, so that only Tinfoil clients - can access the content (coming soon).
+
Serve encrypted and compressed shop, so that only Tinfoil clients + can access the content. Strongly suggested to enable it for security.
diff --git a/requirements.txt b/requirements.txt index 132a742..765d4a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,6 @@ watchdog==4.0.2 Werkzeug==3.0.3 # NSTools -zstandard +zstandard==0.23.0 enlighten pycryptodome \ No newline at end of file