Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -29,13 +29,7 @@ except FileNotFoundError:
|
|
29 |
logger.error("FATAL: keylock_pub.pem not found.")
|
30 |
PUBLIC_KEY_PEM_STRING = "Error: keylock_pub.pem file not found on the server."
|
31 |
|
32 |
-
# ---
|
33 |
-
AES_KEY_SIZE_CRYPTO = 32
|
34 |
-
AES_GCM_NONCE_SIZE_CRYPTO = 12
|
35 |
-
|
36 |
-
# ==============================================================================
|
37 |
-
# CORE DECRYPTION FUNCTION
|
38 |
-
# ==============================================================================
|
39 |
def decode_data(image_base64_string: str) -> dict:
|
40 |
"""
|
41 |
This is the core logic function that will be exposed as an API.
|
@@ -43,15 +37,12 @@ def decode_data(image_base64_string: str) -> dict:
|
|
43 |
"""
|
44 |
if not KEYLOCK_PRIV_KEY:
|
45 |
raise gr.Error("Server Error: The API is not configured with a private key.")
|
46 |
-
|
47 |
try:
|
48 |
-
# 1. Decode base64 and load image
|
49 |
image_buffer = base64.b64decode(image_base64_string)
|
50 |
private_key = serialization.load_pem_private_key(KEYLOCK_PRIV_KEY.encode(), password=None)
|
51 |
img = Image.open(io.BytesIO(image_buffer)).convert("RGB")
|
52 |
pixel_data = np.array(img).ravel()
|
53 |
|
54 |
-
# 2. Extract LSB data
|
55 |
HEADER_BITS = 32
|
56 |
if pixel_data.size < HEADER_BITS: raise ValueError("Image too small for header.")
|
57 |
header_binary_string = "".join(str(pixel & 1) for pixel in pixel_data[:HEADER_BITS])
|
@@ -63,25 +54,22 @@ def decode_data(image_base64_string: str) -> dict:
|
|
63 |
data_binary_string = "".join(str(pixel & 1) for pixel in pixel_data[HEADER_BITS:end_offset])
|
64 |
crypto_payload = int(data_binary_string, 2).to_bytes(data_length, byteorder='big')
|
65 |
|
66 |
-
# 3. Decrypt payload
|
67 |
rsa_key_size_bytes = private_key.key_size // 8
|
68 |
offset = 4
|
69 |
encrypted_aes_key_len = struct.unpack('>I', crypto_payload[:offset])[0]
|
70 |
if encrypted_aes_key_len != rsa_key_size_bytes: raise ValueError("Key pair mismatch.")
|
71 |
encrypted_aes_key = crypto_payload[offset:offset + encrypted_aes_key_len]
|
72 |
offset += encrypted_aes_key_len
|
73 |
-
nonce = crypto_payload[offset:offset + AES_GCM_NONCE_SIZE_CRYPTO
|
74 |
-
offset +=
|
75 |
ciphertext_with_tag = crypto_payload[offset:]
|
76 |
recovered_aes_key = private_key.decrypt(
|
77 |
encrypted_aes_key,
|
78 |
padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)
|
79 |
)
|
80 |
decrypted_bytes = AESGCM(recovered_aes_key).decrypt(nonce, ciphertext_with_tag, None)
|
81 |
-
|
82 |
logger.info("Successfully decoded an image.")
|
83 |
return json.loads(decrypted_bytes.decode('utf-8'))
|
84 |
-
|
85 |
except (ValueError, InvalidTag, TypeError) as e:
|
86 |
logger.error(f"Decryption failed: {e}")
|
87 |
raise gr.Error(f"Decryption failed. The image may be corrupt, or was not encrypted with the correct public key. Details: {e}")
|
@@ -94,7 +82,7 @@ def decode_data(image_base64_string: str) -> dict:
|
|
94 |
# ==============================================================================
|
95 |
with gr.Blocks(title="Secure Decoder API") as demo:
|
96 |
gr.Markdown("# Secure KeyLock Decoder API (Server)")
|
97 |
-
gr.Markdown("This Gradio Space provides the secure, server-side API for decoding KeyLock images.
|
98 |
|
99 |
with gr.Tabs():
|
100 |
with gr.TabItem("API Status & Docs"):
|
@@ -105,23 +93,24 @@ with gr.Blocks(title="Secure Decoder API") as demo:
|
|
105 |
gr.Markdown("---")
|
106 |
gr.Markdown("## API Documentation")
|
107 |
gr.Markdown("### Public Key\nUse the following public key to encrypt data for this service.")
|
108 |
-
gr.Code(value=PUBLIC_KEY_PEM_STRING, language="
|
109 |
|
110 |
-
|
|
|
111 |
gr.Markdown("- **Method**: `POST`\n- **Body**: `JSON`")
|
112 |
gr.Code(language="json", label="JSON Payload Schema", value='{\n "data": [\n "iVBORw0KGgoAAAANSUhEUgA..."\n ]\n}')
|
113 |
-
gr.Code(language="
|
114 |
value="""# Get base64 string: B64_STRING=$(base64 -w 0 /path/to/image.png)
|
115 |
curl -X POST -H "Content-Type: application/json" \\
|
116 |
-d '{"data": ["'$B64_STRING'"]}' \\
|
117 |
-
https://your-space-name.hf.space/run/
|
118 |
|
119 |
# --- This is the hidden component that creates the API endpoint ---
|
120 |
-
# We define the function it calls (`decode_data`) and give it an `api_name`.
|
121 |
with gr.Row(visible=False):
|
122 |
api_input = gr.Textbox()
|
123 |
api_output = gr.JSON()
|
124 |
-
|
|
|
125 |
|
126 |
if __name__ == "__main__":
|
127 |
demo.launch()
|
|
|
29 |
logger.error("FATAL: keylock_pub.pem not found.")
|
30 |
PUBLIC_KEY_PEM_STRING = "Error: keylock_pub.pem file not found on the server."
|
31 |
|
32 |
+
# --- Core Decryption Function ---
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
def decode_data(image_base64_string: str) -> dict:
|
34 |
"""
|
35 |
This is the core logic function that will be exposed as an API.
|
|
|
37 |
"""
|
38 |
if not KEYLOCK_PRIV_KEY:
|
39 |
raise gr.Error("Server Error: The API is not configured with a private key.")
|
|
|
40 |
try:
|
|
|
41 |
image_buffer = base64.b64decode(image_base64_string)
|
42 |
private_key = serialization.load_pem_private_key(KEYLOCK_PRIV_KEY.encode(), password=None)
|
43 |
img = Image.open(io.BytesIO(image_buffer)).convert("RGB")
|
44 |
pixel_data = np.array(img).ravel()
|
45 |
|
|
|
46 |
HEADER_BITS = 32
|
47 |
if pixel_data.size < HEADER_BITS: raise ValueError("Image too small for header.")
|
48 |
header_binary_string = "".join(str(pixel & 1) for pixel in pixel_data[:HEADER_BITS])
|
|
|
54 |
data_binary_string = "".join(str(pixel & 1) for pixel in pixel_data[HEADER_BITS:end_offset])
|
55 |
crypto_payload = int(data_binary_string, 2).to_bytes(data_length, byteorder='big')
|
56 |
|
|
|
57 |
rsa_key_size_bytes = private_key.key_size // 8
|
58 |
offset = 4
|
59 |
encrypted_aes_key_len = struct.unpack('>I', crypto_payload[:offset])[0]
|
60 |
if encrypted_aes_key_len != rsa_key_size_bytes: raise ValueError("Key pair mismatch.")
|
61 |
encrypted_aes_key = crypto_payload[offset:offset + encrypted_aes_key_len]
|
62 |
offset += encrypted_aes_key_len
|
63 |
+
nonce = crypto_payload[offset:offset + 12] # AES_GCM_NONCE_SIZE_CRYPTO
|
64 |
+
offset += 12
|
65 |
ciphertext_with_tag = crypto_payload[offset:]
|
66 |
recovered_aes_key = private_key.decrypt(
|
67 |
encrypted_aes_key,
|
68 |
padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)
|
69 |
)
|
70 |
decrypted_bytes = AESGCM(recovered_aes_key).decrypt(nonce, ciphertext_with_tag, None)
|
|
|
71 |
logger.info("Successfully decoded an image.")
|
72 |
return json.loads(decrypted_bytes.decode('utf-8'))
|
|
|
73 |
except (ValueError, InvalidTag, TypeError) as e:
|
74 |
logger.error(f"Decryption failed: {e}")
|
75 |
raise gr.Error(f"Decryption failed. The image may be corrupt, or was not encrypted with the correct public key. Details: {e}")
|
|
|
82 |
# ==============================================================================
|
83 |
with gr.Blocks(title="Secure Decoder API") as demo:
|
84 |
gr.Markdown("# Secure KeyLock Decoder API (Server)")
|
85 |
+
gr.Markdown("This Gradio Space provides the secure, server-side API for decoding KeyLock images.")
|
86 |
|
87 |
with gr.Tabs():
|
88 |
with gr.TabItem("API Status & Docs"):
|
|
|
93 |
gr.Markdown("---")
|
94 |
gr.Markdown("## API Documentation")
|
95 |
gr.Markdown("### Public Key\nUse the following public key to encrypt data for this service.")
|
96 |
+
gr.Code(value=PUBLIC_KEY_PEM_STRING, language="pem", label="Service Public Key")
|
97 |
|
98 |
+
# --- UPDATED DOCUMENTATION TO REFLECT NEW API NAME ---
|
99 |
+
gr.Markdown("### API Endpoint: `/run/keylock-auth-decoder`")
|
100 |
gr.Markdown("- **Method**: `POST`\n- **Body**: `JSON`")
|
101 |
gr.Code(language="json", label="JSON Payload Schema", value='{\n "data": [\n "iVBORw0KGgoAAAANSUhEUgA..."\n ]\n}')
|
102 |
+
gr.Code(language="bash", label="Example using cURL",
|
103 |
value="""# Get base64 string: B64_STRING=$(base64 -w 0 /path/to/image.png)
|
104 |
curl -X POST -H "Content-Type: application/json" \\
|
105 |
-d '{"data": ["'$B64_STRING'"]}' \\
|
106 |
+
https://your-space-name.hf.space/run/keylock-auth-decoder""")
|
107 |
|
108 |
# --- This is the hidden component that creates the API endpoint ---
|
|
|
109 |
with gr.Row(visible=False):
|
110 |
api_input = gr.Textbox()
|
111 |
api_output = gr.JSON()
|
112 |
+
# --- RENAMED API ENDPOINT AS REQUESTED ---
|
113 |
+
gr.Interface(fn=decode_data, inputs=api_input, outputs=api_output, api_name="keylock-auth-decoder")
|
114 |
|
115 |
if __name__ == "__main__":
|
116 |
demo.launch()
|