Spaces:
Paused
Paused
import secrets | |
import string | |
from Crypto.Cipher import AES | |
from Crypto.Random import get_random_bytes | |
from Crypto.Util.Padding import pad, unpad | |
from Crypto.Hash import SHA256 | |
import base64 | |
import gradio as gr | |
# Custom CSS for modern styling | |
custom_css = """ | |
body { | |
font-family: 'Inter', sans-serif; | |
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%); | |
} | |
.gradio-container { | |
max-width: 800px; | |
margin: 20px auto; | |
background: rgba(255, 255, 255, 0.95); | |
border-radius: 16px; | |
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); | |
padding: 24px; | |
} | |
h1, .gr-markdown h1 { | |
color: #1e3c72; | |
font-weight: 700; | |
text-align: center; | |
margin-bottom: 16px; | |
} | |
.gr-button { | |
border-radius: 8px; | |
padding: 12px 24px; | |
font-weight: 600; | |
transition: all 0.3s ease; | |
} | |
.gr-button-primary { | |
background: #2a5298; | |
color: white; | |
border: none; | |
} | |
.gr-button-primary:hover { | |
background: #1e3c72; | |
transform: translateY(-2px); | |
} | |
.gr-button-secondary { | |
background: #f1f3f5; | |
color: #2a5298; | |
border: 1px solid #2a5298; | |
} | |
.gr-button-secondary:hover { | |
background: #e9ecef; | |
transform: translateY(-2px); | |
} | |
.gr-textbox, .gr-radio { | |
border-radius: 8px; | |
border: 1px solid #dee2e6; | |
background: #f8f9fa; | |
} | |
.gr-textbox:focus { | |
border-color: #2a5298; | |
box-shadow: 0 0 0 3px rgba(42, 82, 152, 0.2); | |
} | |
.gr-row { | |
gap: 16px; | |
} | |
.card { | |
background: white; | |
border-radius: 12px; | |
padding: 16px; | |
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); | |
margin-bottom: 16px; | |
} | |
.footer { | |
text-align: center; | |
color: #6c757d; | |
font-size: 0.9em; | |
margin-top: 24px; | |
} | |
""" | |
def generate_random_key(length=32): | |
"""Generate a random key with digits, uppercase, lowercase, and special characters.""" | |
characters = string.ascii_lowercase + string.ascii_uppercase + string.digits + string.punctuation | |
key = ( | |
secrets.choice(string.ascii_lowercase) + | |
secrets.choice(string.ascii_uppercase) + | |
secrets.choice(string.digits) + | |
secrets.choice(string.punctuation) | |
) | |
key += ''.join(secrets.choice(characters) for _ in range(length - 4)) | |
key_list = list(key) | |
secrets.SystemRandom().shuffle(key_list) | |
return ''.join(key_list) | |
def derive_key(custom_key, key_size): | |
"""Derive a key of specified size (128, 192, or 256 bits) from a custom string using SHA-256.""" | |
hasher = SHA256.new() | |
hasher.update(custom_key.encode('utf-8')) | |
full_hash = hasher.digest() | |
return full_hash[:int(key_size) // 8] | |
def encrypt_string(plain_text, key, key_size): | |
"""Encrypt a string using AES in CBC mode with specified key size.""" | |
try: | |
if not plain_text: | |
return "Error: Please provide text to encrypt." | |
if not key: | |
return "Error: Please provide a key." | |
derived_key = derive_key(key, key_size) | |
plain_text_bytes = plain_text.encode('utf-8') | |
iv = get_random_bytes(AES.block_size) | |
cipher = AES.new(derived_key, AES.MODE_CBC, iv) | |
padded_text = pad(plain_text_bytes, AES.block_size) | |
cipher_text = cipher.encrypt(padded_text) | |
return base64.b64encode(iv + cipher_text).decode('utf-8') | |
except Exception as e: | |
return f"Encryption error: {str(e)}" | |
def decrypt_string(encrypted_text, key, key_size): | |
"""Decrypt a string using AES in CBC mode with specified key size.""" | |
try: | |
if not encrypted_text: | |
return "Error: Please provide encrypted text." | |
if not key: | |
return "Error: Please provide a key." | |
derived_key = derive_key(key, key_size) | |
raw = base64.b64decode(encrypted_text) | |
iv = raw[:AES.block_size] | |
cipher_text = raw[AES.block_size:] | |
cipher = AES.new(derived_key, AES.MODE_CBC, iv) | |
padded_text = cipher.decrypt(cipher_text) | |
return unpad(padded_text, AES.block_size).decode('utf-8') | |
except Exception as e: | |
return f"Decryption error: {str(e)}" | |
def generate_and_display_key(): | |
"""Generate and return a random key.""" | |
return generate_random_key() | |
# Gradio interface | |
with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo: | |
gr.Markdown( | |
""" | |
# π AES Encryption/Decryption App | |
Securely encrypt or decrypt text using AES with 128, 192, or 256-bit keys. | |
""" | |
) | |
with gr.Column(elem_classes="card"): | |
gr.Markdown("### Configuration") | |
key_size = gr.Radio( | |
choices=["128", "192", "256"], | |
label="AES Key Size (bits)", | |
value="256", | |
elem_classes="radio" | |
) | |
with gr.Row(): | |
custom_key = gr.Textbox( | |
label="Custom Key", | |
placeholder="Enter your key or generate one", | |
lines=1, | |
show_copy_button=True | |
) | |
generate_key_btn = gr.Button("Generate Key", variant="secondary") | |
with gr.Column(elem_classes="card"): | |
gr.Markdown("### Input") | |
input_text = gr.Textbox( | |
label="Input Text", | |
placeholder="Enter text to encrypt or encrypted text to decrypt", | |
lines=4 | |
) | |
with gr.Row(): | |
encrypt_btn = gr.Button("π Encrypt", variant="primary") | |
decrypt_btn = gr.Button("π Decrypt", variant="secondary") | |
with gr.Column(elem_classes="card"): | |
gr.Markdown("### Output") | |
output_text = gr.Textbox( | |
label="Result", | |
placeholder="Result will appear here", | |
lines=4, | |
interactive=False, | |
show_copy_button=True | |
) | |
gr.Markdown( | |
""" | |
<div class="footer"> | |
Powered by Gradio & PyCrypto | Β© 2025 SecureApp | |
</div> | |
""" | |
) | |
# Event handlers | |
generate_key_btn.click( | |
fn=generate_and_display_key, | |
outputs=custom_key | |
) | |
encrypt_btn.click( | |
fn=encrypt_string, | |
inputs=[input_text, custom_key, key_size], | |
outputs=output_text | |
) | |
decrypt_btn.click( | |
fn=decrypt_string, | |
inputs=[input_text, custom_key, key_size], | |
outputs=output_text | |
) | |
# Launch the app | |
demo.launch() |