broadfield-dev commited on
Commit
ca0728b
·
verified ·
1 Parent(s): b0a2643

Create component.py

Browse files
Files changed (1) hide show
  1. component.py +185 -0
component.py ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from PIL import Image, ImageOps, ImageDraw, ImageFont
3
+ import requests
4
+ import io
5
+ import json
6
+ import logging
7
+ import os
8
+ import struct
9
+ import tempfile
10
+ import random
11
+ from cryptography.hazmat.primitives import serialization
12
+ from cryptography.hazmat.primitives.asymmetric import rsa, padding
13
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
14
+ from cryptography.hazmat.primitives import hashes
15
+ import numpy as np
16
+
17
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
18
+ PREFERRED_FONTS = ["Arial", "Helvetica", "DejaVu Sans", "Verdana", "Calibri", "sans-serif"]
19
+
20
+ class AppServerLogic:
21
+ def __init__(self):
22
+ self.private_key_object = None
23
+ self.public_key_pem = ""
24
+ self._initialize_keys()
25
+
26
+ def _initialize_keys(self):
27
+ key_pem = os.environ.get('KEYLOCK_PRIV_KEY')
28
+ if not key_pem:
29
+ pk = rsa.generate_private_key(public_exponent=65537, key_size=2048)
30
+ key_pem = pk.private_bytes(encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption()).decode('utf-8')
31
+ try:
32
+ self.private_key_object = serialization.load_pem_private_key(key_pem.encode(), password=None)
33
+ self.public_key_pem = self.private_key_object.public_key().public_bytes(encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo).decode('utf-8')
34
+ except Exception as e:
35
+ logging.error(f"Key initialization failed: {e}")
36
+
37
+ def decode_payload(self, image_input):
38
+ if not self.private_key_object:
39
+ return {"status": "Error", "message": "Server key not configured."}
40
+ try:
41
+ pixel_data = np.array(image_input.convert("RGB")).ravel()
42
+ header_binary = "".join(str(p & 1) for p in pixel_data[:32])
43
+ data_length = int(header_binary, 2)
44
+ required_pixels = 32 + data_length * 8
45
+ if required_pixels > len(pixel_data):
46
+ raise ValueError("Incomplete payload in image.")
47
+ data_binary = "".join(str(p & 1) for p in pixel_data[32:required_pixels])
48
+ crypto_payload = int(data_binary, 2).to_bytes(data_length, byteorder='big')
49
+ offset = 4
50
+ encrypted_aes_key_len = struct.unpack('>I', crypto_payload[:offset])[0]
51
+ encrypted_aes_key = crypto_payload[offset:offset + encrypted_aes_key_len]; offset += encrypted_aes_key_len
52
+ nonce = crypto_payload[offset:offset + 12]; offset += 12
53
+ ciphertext = crypto_payload[offset:]
54
+ recovered_aes_key = self.private_key_object.decrypt(encrypted_aes_key, padding.OAEP(mgf=padding.MGF1(hashes.SHA256()), algorithm=hashes.SHA256(), label=None))
55
+ payload = json.loads(AESGCM(recovered_aes_key).decrypt(nonce, ciphertext, None).decode())
56
+ return {"status": "Success", "payload": payload}
57
+ except Exception as e:
58
+ return {"status": "Error", "message": f"Decryption Failed: {e}"}
59
+
60
+ @staticmethod
61
+ def _get_font(preferred_fonts, base_size):
62
+ fp = None
63
+ for n in preferred_fonts:
64
+ try: ImageFont.truetype(n.lower()+".ttf", 10); fp = n.lower()+".ttf"; break
65
+ except IOError: continue
66
+ if fp: return ImageFont.truetype(fp, base_size)
67
+ return ImageFont.load_default(size=base_size)
68
+
69
+ @staticmethod
70
+ def _generate_starfield_image(w=800, h=800):
71
+ center_x, center_y = w / 2, h / 2
72
+ y_coords, x_coords = np.mgrid[0:h, 0:w]
73
+ distance = np.sqrt((x_coords - center_x)**2 + (y_coords - center_y)**2)
74
+ max_distance = np.sqrt(center_x**2 + center_y**2)
75
+ distance_norm = distance / max_distance
76
+ bg_center_color = np.array([20, 25, 40])
77
+ bg_outer_color = np.array([0, 0, 5])
78
+ gradient = bg_outer_color + (bg_center_color - bg_outer_color) * (1 - distance_norm[..., np.newaxis])
79
+ img = Image.fromarray(gradient.astype(np.uint8), 'RGB')
80
+ draw = ImageDraw.Draw(img)
81
+ for _ in range(int((w * h) / 200)):
82
+ x, y = random.randint(0, w - 1), random.randint(0, h - 1)
83
+ brightness = random.randint(30, 90)
84
+ draw.point((x, y), fill=(int(brightness*0.9), int(brightness*0.9), brightness))
85
+ star_colors = [(255, 255, 255), (220, 230, 255), (255, 240, 220)]
86
+ for _ in range(int((w * h) / 1000)):
87
+ x, y = random.randint(0, w - 1), random.randint(0, h - 1)
88
+ size = 0.5 + (2.5 * (random.random() ** 2))
89
+ brightness = 120 + (135 * (random.random() ** 1.5))
90
+ color = random.choice(star_colors)
91
+ final_color = tuple(int(c * (brightness / 255.0)) for c in color)
92
+ glow_size = size * 3
93
+ glow_color = tuple(int(c * 0.3) for c in final_color)
94
+ draw.ellipse([x - glow_size, y - glow_size, x + glow_size, y + glow_size], fill=glow_color)
95
+ draw.ellipse([x - size, y - size, x + size, y + size], fill=final_color)
96
+ return img
97
+
98
+ def _draw_overlay(self, image: Image.Image) -> Image.Image:
99
+ img_overlayed = image.copy().convert("RGBA")
100
+ draw = ImageDraw.Draw(img_overlayed, "RGBA")
101
+ width, height = img_overlayed.size
102
+ overlay_color = (10, 15, 30, 200)
103
+ title_color = (200, 220, 255)
104
+ font_bold = self._get_font(PREFERRED_FONTS, 30)
105
+ draw.rectangle([0, 20, width, 80], fill=overlay_color)
106
+ draw.text((width / 2, 50), "KeyLock Secure Data", fill=title_color, font=font_bold, anchor="ms")
107
+ final_image_rgb = Image.new("RGB", img_overlayed.size, (0, 0, 0))
108
+ final_image_rgb.paste(img_overlayed, (0, 0), img_overlayed)
109
+ return final_image_rgb
110
+
111
+ def generate_encrypted_image(self, payload_dict):
112
+ base_image = self._generate_starfield_image()
113
+ image_with_overlay = self._draw_overlay(base_image)
114
+ json_bytes = json.dumps(payload_dict).encode('utf-8')
115
+ public_key = serialization.load_pem_public_key(self.public_key_pem.encode('utf-8'))
116
+ aes_key, nonce = os.urandom(32), os.urandom(12)
117
+ ciphertext = AESGCM(aes_key).encrypt(nonce, json_bytes, None)
118
+ rsa_key = public_key.encrypt(aes_key, padding.OAEP(mgf=padding.MGF1(hashes.SHA256()), algorithm=hashes.SHA256(), label=None))
119
+ payload = struct.pack('>I', len(rsa_key)) + rsa_key + nonce + ciphertext
120
+ pixel_data = np.array(image_with_overlay).ravel()
121
+ binary_payload = ''.join(format(b, '08b') for b in struct.pack('>I', len(payload)) + payload)
122
+ pixel_data[:len(binary_payload)] = (pixel_data[:len(binary_payload)] & 0xFE) | np.array(list(binary_payload), dtype=np.uint8)
123
+ final_image = Image.fromarray(pixel_data.reshape(image_with_overlay.size[1], image_with_overlay.size[0], 3), 'RGB')
124
+
125
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
126
+ final_image.save(f.name, "PNG")
127
+ return f.name, f.name
128
+
129
+ @staticmethod
130
+ def generate_pem_keys():
131
+ pk = rsa.generate_private_key(public_exponent=65537, key_size=2048)
132
+ priv = pk.private_bytes(encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption()).decode()
133
+ pub = pk.public_key().public_bytes(encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo).decode()
134
+ return priv, pub
135
+
136
+ class KeylockDecoderComponent(gr.components.Component):
137
+ EVENTS = ["change"]
138
+
139
+ def __init__(self, server_logic, **kwargs):
140
+ self.server_logic = server_logic
141
+ self.value = None
142
+ super().__init__(value=self.value, **kwargs)
143
+
144
+ def _format_message(self, result: dict | None) -> str:
145
+ if not result or not result.get("status"): return "Upload a KeyLock image to auto-fill credentials."
146
+ status = result["status"]
147
+ if status == "Success":
148
+ user = result.get("payload", {}).get("USER", "N/A")
149
+ return f"<p style='color:green; font-weight:bold;'>✅ Success! Decoded credentials for '{user}'.</p>"
150
+ else:
151
+ message = result.get("message", "An unknown error occurred.")
152
+ return f"<p style='color:red; font-weight:bold;'>❌ Error: {message}</p>"
153
+
154
+ def preprocess(self, payload): return payload
155
+ def postprocess(self, value): return value
156
+ def api_info(self): return {"type": "object", "example": {"status": "Success", "payload": {"USER": "demo-user"}}}
157
+
158
+ def render(self):
159
+ value_state = gr.State(value=self.value)
160
+
161
+ with gr.Column():
162
+ image_input = gr.Image(label="KeyLock Image", type="pil", show_label=False)
163
+ status_display = gr.Markdown(self._format_message(self.value))
164
+ with gr.Accordion("Generate Encrypted Image", open=False):
165
+ payload_input = gr.JSON(label="Payload to Encrypt", value={"API_KEY": "sk-12345-abcde", "USER": "demo-user"})
166
+ generate_img_button = gr.Button("Generate Image", variant="secondary")
167
+ generated_image_preview = gr.Image(label="Generated Image Preview", type="filepath", interactive=False)
168
+ generated_file_download = gr.File(label="Download Uncorrupted PNG", interactive=False)
169
+ with gr.Accordion("Create New Standalone Key Pair", open=False):
170
+ generate_keys_button = gr.Button("Generate Keys", variant="secondary")
171
+ with gr.Row():
172
+ output_private_key = gr.Code(label="Generated Private Key", language="python")
173
+ output_public_key = gr.Code(label="Generated Public Key", language="python")
174
+
175
+ def on_image_upload(image):
176
+ if image is None: return None, "Upload a KeyLock image to auto-fill credentials."
177
+ result_dict = self.server_logic.decode_payload(image)
178
+ formatted_message = self._format_message(result_dict)
179
+ return result_dict, formatted_message
180
+
181
+ image_input.upload(fn=on_image_upload, inputs=[image_input], outputs=[value_state, status_display])
182
+ generate_img_button.click(fn=self.server_logic.generate_encrypted_image, inputs=[payload_input], outputs=[generated_image_preview, generated_file_download])
183
+ generate_keys_button.click(fn=self.server_logic.generate_pem_keys, inputs=None, outputs=[output_private_key, output_public_key])
184
+
185
+ return {"value": value_state}