paul-purecipher commited on
Commit
6906475
·
verified ·
1 Parent(s): 88ceaff

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +316 -0
  2. requirements.txt +7 -0
app.py ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ import os
3
+ import time
4
+ import mimetypes
5
+
6
+ import gradio as gr
7
+ import requests
8
+ import torch
9
+ from diffusers import AutoPipelineForText2Image
10
+ from PIL import Image
11
+
12
+ # --- Configuration ---
13
+ # Fetch the API key from environment variables.
14
+ OMNISEAL_API_KEY = os.getenv("OMNISEAL_API_KEY")
15
+ if not OMNISEAL_API_KEY:
16
+ raise ValueError("Error: OMNISEAL_API_KEY environment variable not set.")
17
+
18
+ # Updated API endpoints to use the unified direct API.
19
+ OMNISEAL_EMBED_URL = "https://api.purecipher.com/unified/embed-file"
20
+ OMNISEAL_RECOVER_URL = "https://api.purecipher.com/unified/retrieve-file"
21
+
22
+ # --- Model Loading ---
23
+ # Load the model once when the script starts.
24
+ print("Loading image generation model... This might take a few minutes the first time.")
25
+ try:
26
+ pipe = AutoPipelineForText2Image.from_pretrained(
27
+ "stabilityai/sd-turbo", torch_dtype=torch.float32
28
+ )
29
+ pipe.to("cpu")
30
+ print("Model loaded successfully.")
31
+ except Exception as e:
32
+ print(f"Failed to load model: {e}")
33
+ pipe = None
34
+
35
+ # --- Helper Functions ---
36
+ def generate_image_from_prompt(prompt: str) -> Image:
37
+ """
38
+ Generates an image locally using the stabilityai/sd-turbo model.
39
+ Returns a PIL Image.
40
+ """
41
+ if pipe is None:
42
+ raise gr.Error(
43
+ "Image generation model is not loaded. Please check the console for errors."
44
+ )
45
+ try:
46
+ image = pipe(prompt=prompt, num_inference_steps=1, guidance_scale=0.0).images[0]
47
+ return image
48
+ except Exception as e:
49
+ raise gr.Error(f"Error generating image locally: {e}")
50
+
51
+ def call_omni_seal_api(url: str, files: dict, data: dict) -> bytes:
52
+ """
53
+ Makes a POST request to the OmniSeal API, automatically including the API token.
54
+ Returns the response content as bytes.
55
+ """
56
+ headers = {
57
+ "accept": "application/json",
58
+ "api_token": OMNISEAL_API_KEY
59
+ }
60
+ try:
61
+ response = requests.post(url, files=files, data=data, headers=headers, timeout=120)
62
+ response.raise_for_status()
63
+ return response.content
64
+ except requests.exceptions.RequestException as e:
65
+ error_message = f"API call failed: {e}."
66
+ if e.response is not None:
67
+ error_message += f" Response: {e.response.text}"
68
+ raise gr.Error(error_message)
69
+
70
+ def pil_to_bytes(image: Image, format="PNG") -> bytes:
71
+ """Converts a PIL Image to bytes."""
72
+ with io.BytesIO() as buf:
73
+ image.save(buf, format=format)
74
+ return buf.getvalue()
75
+
76
+ def bytes_to_pil(image_bytes: bytes) -> Image:
77
+ """Converts bytes to a PIL Image."""
78
+ try:
79
+ return Image.open(io.BytesIO(image_bytes))
80
+ except Exception:
81
+ # If the returned file isn't an image, we can't display it, which is expected for recovery.
82
+ return None
83
+
84
+ # --- Gradio Handler Functions ---
85
+ def fn_generate_image(prompt):
86
+ gr.Info("Generating image... This may take a moment.")
87
+ image = generate_image_from_prompt(prompt)
88
+ gr.Info("Image generated successfully!")
89
+ return image, image
90
+
91
+ def fn_embed_secret(generated_image_state, secret_file, private_seed):
92
+ if not private_seed:
93
+ raise gr.Error("Private seed is required.")
94
+ if generated_image_state is None:
95
+ raise gr.Error("Please generate an image first.")
96
+ if secret_file is None:
97
+ raise gr.Error("Please upload a secret file.")
98
+
99
+ gr.Info("Calling OmniSeal API to embed secret...")
100
+ generated_image_bytes = pil_to_bytes(generated_image_state)
101
+
102
+ secret_file_name = os.path.basename(secret_file.name)
103
+ secret_mime_type, _ = mimetypes.guess_type(secret_file.name)
104
+ if secret_mime_type is None:
105
+ secret_mime_type = "application/octet-stream"
106
+
107
+ with open(secret_file.name, "rb") as f:
108
+ secret_file_bytes = f.read()
109
+
110
+ files = {
111
+ "cover_file": ("generated_image.png", generated_image_bytes, "image/png"),
112
+ "secret_file": (secret_file_name, secret_file_bytes, secret_mime_type),
113
+ }
114
+ data = {"private_seed": private_seed}
115
+
116
+ embedded_image_bytes = call_omni_seal_api(OMNISEAL_EMBED_URL, files, data)
117
+ embedded_image_pil = bytes_to_pil(embedded_image_bytes)
118
+
119
+ embedded_filename = f"embedded_{secret_file_name}.png"
120
+ with open(embedded_filename, "wb") as f:
121
+ f.write(embedded_image_bytes)
122
+
123
+ gr.Info("Secret file embedded successfully!")
124
+ return embedded_image_pil, embedded_filename, embedded_image_pil, secret_file_name
125
+
126
+ def fn_recover_secret(embedded_filename, secret_file_name_state, private_seed):
127
+ if not private_seed:
128
+ raise gr.Error("Private seed is required.")
129
+ if embedded_filename is None:
130
+ raise gr.Error("No embedded image available to recover from.")
131
+
132
+ gr.Info("Calling OmniSeal API to recover secret...")
133
+ #embedded_image_bytes = pil_to_bytes(embedded_image_state)
134
+ uploaded_file_name = os.path.basename(embedded_filename.name)
135
+ uploaded_mime_type, _ = mimetypes.guess_type(embedded_filename.name)
136
+ if uploaded_mime_type is None:
137
+ uploaded_mime_type = "application/octet-stream"
138
+
139
+ with open(embedded_filename.name, "rb") as f:
140
+ embedded_image_bytes = f.read()
141
+ files = {"sealed_file": ("embedded_image.png", embedded_image_bytes, "image/png")}
142
+ data = {"private_seed": private_seed}
143
+
144
+ recovered_bytes = call_omni_seal_api(OMNISEAL_RECOVER_URL, files, data)
145
+
146
+ recovered_filename = secret_file_name_state if secret_file_name_state else "recovered_secret.bin"
147
+ with open(recovered_filename, "wb") as f:
148
+ f.write(recovered_bytes)
149
+
150
+ gr.Info("Secret recovered successfully!")
151
+ return recovered_filename
152
+
153
+ def fn_recover_from_upload(uploaded_file, private_seed):
154
+ if not private_seed:
155
+ raise gr.Error("Private seed is required.")
156
+ if uploaded_file is None:
157
+ raise gr.Error("Please upload an embedded image file.")
158
+
159
+ gr.Info("Calling OmniSeal API to recover secret from uploaded file...")
160
+ uploaded_file_name = os.path.basename(uploaded_file.name)
161
+ uploaded_mime_type, _ = mimetypes.guess_type(uploaded_file.name)
162
+ if uploaded_mime_type is None:
163
+ uploaded_mime_type = "application/octet-stream"
164
+
165
+ with open(uploaded_file.name, "rb") as f:
166
+ embedded_bytes = f.read()
167
+
168
+ files = {"sealed_file": (uploaded_file_name, embedded_bytes, uploaded_mime_type)}
169
+ data = {"private_seed": private_seed}
170
+
171
+ recovered_bytes = call_omni_seal_api(OMNISEAL_RECOVER_URL, files, data)
172
+
173
+ recovered_filename = f"recovered_from_{uploaded_file_name}"
174
+ recovered_filename = ".".join(recovered_filename.split(".")[:-1]) if "." in recovered_filename else recovered_filename
175
+
176
+ with open(recovered_filename, "wb") as f:
177
+ f.write(recovered_bytes)
178
+
179
+ gr.Info("Secret recovered successfully!")
180
+ return recovered_filename
181
+
182
+ def fn_embed_from_upload(cover_file, secret_file, private_seed):
183
+ if not private_seed:
184
+ raise gr.Error("Private seed is required.")
185
+ if cover_file is None:
186
+ raise gr.Error("Please upload a cover file.")
187
+ if secret_file is None:
188
+ raise gr.Error("Please upload a secret file.")
189
+
190
+ gr.Info("Calling OmniSeal API to embed secret in uploaded file...")
191
+
192
+ cover_file_name = os.path.basename(cover_file.name)
193
+ cover_mime_type, _ = mimetypes.guess_type(cover_file.name)
194
+ if cover_mime_type is None:
195
+ cover_mime_type = "application/octet-stream"
196
+
197
+ secret_file_name = os.path.basename(secret_file.name)
198
+ secret_mime_type, _ = mimetypes.guess_type(secret_file.name)
199
+ if secret_mime_type is None:
200
+ secret_mime_type = "application/octet-stream"
201
+
202
+ with open(cover_file.name, "rb") as f_cover, open(secret_file.name, "rb") as f_secret:
203
+ cover_file_bytes = f_cover.read()
204
+ secret_file_bytes = f_secret.read()
205
+
206
+ files = {
207
+ "cover_file": (cover_file_name, cover_file_bytes, cover_mime_type),
208
+ "secret_file": (secret_file_name, secret_file_bytes, secret_mime_type),
209
+ }
210
+ data = {"private_seed": private_seed}
211
+
212
+ embedded_file_bytes = call_omni_seal_api(OMNISEAL_EMBED_URL, files, data)
213
+ embedded_file_pil = bytes_to_pil(embedded_file_bytes)
214
+
215
+ cover_ext = os.path.splitext(cover_file_name)[1]
216
+ embedded_filename = f"embedded_{secret_file_name}{cover_ext}"
217
+ with open(embedded_filename, "wb") as f:
218
+ f.write(embedded_file_bytes)
219
+
220
+ gr.Info("Secret file embedded successfully!")
221
+ return embedded_file_pil, embedded_filename
222
+
223
+ # --- Gradio UI ---
224
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
225
+ gr.Image(value="purecipher_logo_white.png", label="PureCipher Logo", show_label=False)
226
+ gr.Markdown("# PureCipher OmniSeal Demo")
227
+
228
+ gr.Markdown(
229
+ """
230
+ This application demonstrates the power of **PureCipher's OmniSeal API** to hide a secret file within another file.
231
+ 1. **Generate an Image**: Type a prompt to create an image using AI.
232
+ 2. **Embed a Secret**: Upload any file to hide it inside the generated image.
233
+ 3. **Recover the Secret**: Extract the original secret file from the new image.
234
+ """
235
+ )
236
+
237
+ with gr.Row():
238
+ private_seed_input = gr.Textbox(label="Private Seed", value="1234", type="password")
239
+
240
+ # State variables to hold data between steps
241
+ generated_image_state = gr.State()
242
+ embedded_image_state = gr.State()
243
+ secret_file_name_state = gr.State()
244
+
245
+ with gr.Tabs():
246
+ with gr.TabItem("Generate & Embed"):
247
+ with gr.Row():
248
+ with gr.Column(scale=1):
249
+ gr.Markdown("### Step 1: Generate Image")
250
+ prompt_input = gr.Textbox(label="Prompt", value="A majestic lion in the savanna")
251
+ generate_btn = gr.Button("Generate Image", variant="primary")
252
+ gr.Markdown("---")
253
+ gr.Markdown("### Step 2: Embed Secret File")
254
+ secret_file_input = gr.File(label="Upload Secret File")
255
+ embed_btn = gr.Button("Embed Secret File", variant="primary")
256
+ with gr.Column(scale=2):
257
+ gr.Markdown("### Results")
258
+ with gr.Row():
259
+ generated_image_output = gr.Image(label="Original Generated Image", type="pil")
260
+ embedded_image_output = gr.Image(label="Image with Embedded Secret", type="pil")
261
+ recover_btn = gr.Button("Recover Secret from this Image")
262
+ with gr.Row():
263
+ embedded_file_output = gr.File(label="Download Embedded Image")
264
+ recovered_file_output = gr.File(label="Download Recovered Secret")
265
+
266
+ with gr.TabItem("Embed & Recover"):
267
+ with gr.Row():
268
+ with gr.Column(scale=1):
269
+ gr.Markdown("### Step 1: Upload Files")
270
+ cover_file_input_upload = gr.File(label="Upload Cover File")
271
+ secret_file_input_upload = gr.File(label="Upload Secret File")
272
+ embed_upload_btn = gr.Button("Embed Secret File", variant="primary")
273
+ with gr.Column(scale=2):
274
+ gr.Markdown("### Results")
275
+ embedded_image_output_upload = gr.Image(label="Image with Embedded Secret", type="pil")
276
+ embedded_file_output_upload = gr.File(label="Download Embedded File")
277
+
278
+ with gr.TabItem("Recover from Existing Image"):
279
+ with gr.Row():
280
+ with gr.Column():
281
+ gr.Markdown("### Upload an image that already has a secret file embedded in it.")
282
+ upload_image_input = gr.File(label="Upload Embedded Image")
283
+ recover_upload_btn = gr.Button("Recover Secret from Uploaded Image", variant="primary")
284
+ with gr.Column():
285
+ gr.Markdown("### Recovered File")
286
+ upload_recovered_output = gr.File(label="Download Recovered Secret")
287
+
288
+ # Define interactions
289
+ generate_btn.click(
290
+ fn=fn_generate_image,
291
+ inputs=[prompt_input],
292
+ outputs=[generated_image_output, generated_image_state],
293
+ )
294
+ embed_btn.click(
295
+ fn=fn_embed_secret,
296
+ inputs=[generated_image_state, secret_file_input, private_seed_input],
297
+ outputs=[embedded_image_output, embedded_file_output, embedded_image_state, secret_file_name_state],
298
+ )
299
+ recover_btn.click(
300
+ fn=fn_recover_secret,
301
+ inputs=[embedded_file_output, secret_file_name_state, private_seed_input],
302
+ outputs=[recovered_file_output],
303
+ )
304
+ recover_upload_btn.click(
305
+ fn=fn_recover_from_upload,
306
+ inputs=[upload_image_input, private_seed_input],
307
+ outputs=[upload_recovered_output],
308
+ )
309
+ embed_upload_btn.click(
310
+ fn=fn_embed_from_upload,
311
+ inputs=[cover_file_input_upload, secret_file_input_upload, private_seed_input],
312
+ outputs=[embedded_image_output_upload, embedded_file_output_upload],
313
+ )
314
+
315
+ if __name__ == "__main__":
316
+ demo.launch(allowed_paths=["purecipher_logo_white.png"])
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ accelerate
2
+ diffusers
3
+ invisible_watermark
4
+ torch
5
+ transformers
6
+ xformers
7
+ pillow==11.2.1