Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 265dff3329 | |||
| 9e45c34365 |
@@ -1,77 +1,172 @@
|
|||||||
# PacCrypt WebApp
|
# PacCrypt WebApp
|
||||||
|
|
||||||
**PacCrypt** is a web-based application designed to provide secure encoding, encryption, and password generation. It allows users to easily encrypt and decrypt text and files, with both basic and advanced encryption options. It also features a password generator and a simple Pac-Man game as an Easter egg!
|
**PacCrypt** is a web-based platform that allows you to securely encrypt/decrypt text and files, generate passwords, and even enjoy a hidden Pac-Man game!
|
||||||
|
Built using Python (Flask), JavaScript, and AES-GCM encryption.
|
||||||
|
|
||||||
## Features
|
Official Website: [paccrypt.unnaturalll.dev](http://paccrypt.unnaturalll.dev)
|
||||||
|
|
||||||
- **Basic and Advanced Encryption**: Choose between simple encryption (Caesar Cipher) or more secure AES-GCM encryption.
|
---
|
||||||
- **File Encryption/Decryption**: Encrypt or decrypt files with a password.
|
|
||||||
- **Password Generator**: Generate secure random passwords with customizable length and complexity.
|
|
||||||
- **Pac-Man Game**: A fun Easter egg! Play a Pac-Man game when you type "pacman" in the text area.
|
|
||||||
- **Copy to Clipboard**: Copy generated passwords or encrypted results with one click.
|
|
||||||
- **Responsive Design**: Fully responsive web design that works across different screen sizes.
|
|
||||||
|
|
||||||
## Installation
|
## ✨ Features
|
||||||
|
|
||||||
### Prerequisites
|
- 🔒 **Basic and Advanced Encryption** (Text and Files)
|
||||||
|
- 🔑 **Password Generator**
|
||||||
|
- 🄹️ **Pac-Man Easter Egg** (Type `pacman` to unlock!)
|
||||||
|
- 📱 **Responsive Design** (Mobile Friendly)
|
||||||
|
- ⚡ **One-Click Start Scripts** (Dev and Production modes)
|
||||||
|
- 🎨 **Modern Animated UI** (Dark Mode + Green Neon Theme)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 👨💻 Installation
|
||||||
|
|
||||||
|
### 📋 Prerequisites
|
||||||
|
|
||||||
- **Python 3.7+**
|
- **Python 3.7+**
|
||||||
- **Nginx** (for reverse proxy and SSL configuration)
|
- **Flask 3+**
|
||||||
|
- **Cryptography 42+**
|
||||||
|
- **Waitress 2.1+**
|
||||||
|
- **Nginx** (Recommended for production)
|
||||||
|
|
||||||
Official PacCrypt website: paccrypt.unnaturalll.dev
|
---
|
||||||
|
|
||||||
### Steps to Set Up Locally (Windows)
|
### ⚡ Quick Setup
|
||||||
|
|
||||||
1. Clone the repository:
|
1. Clone the repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
git clone https://github.com/TySP-Dev/PacCrypt.git
|
git clone https://github.com/TySP-Dev/PacCrypt.git
|
||||||
cd paccrypt-webapp
|
cd paccrypt-webapp-final
|
||||||
|
```
|
||||||
|
|
||||||
2. Create and activate a virtual environment:
|
2. Create and activate a virtual environment:
|
||||||
python3 -m venv venv
|
|
||||||
source venv/bin/activate # On Windows, use `venv\Scripts\activate`
|
|
||||||
|
|
||||||
3. Install the required Python dependencies:
|
```bash
|
||||||
|
python -m venv venv
|
||||||
|
source venv/bin/activate # Windows: venv\Scripts\activate
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Install required Python packages:
|
||||||
|
|
||||||
|
```bash
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
4. Run the Flask app:
|
4. Start the app:
|
||||||
python app.py
|
|
||||||
|
|
||||||
5. Open http://127.0.0.1:5000 to access the app locally.
|
**Windows**:
|
||||||
|
```bash
|
||||||
|
start_dev.bat # For Development
|
||||||
|
start_prod.bat # For Production
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
**Linux / Mac**:
|
||||||
|
```bash
|
||||||
|
chmod +x start_dev.sh start_prod.sh
|
||||||
|
./start_dev.sh # For Development
|
||||||
|
./start_prod.sh # For Production
|
||||||
|
```
|
||||||
|
|
||||||
### Encryption and Decryption
|
5. Access the app at:
|
||||||
|
[http://127.0.0.1:5000](http://127.0.0.1:5000)
|
||||||
|
|
||||||
Select the encryption type (Basic or Advanced).
|
---
|
||||||
|
|
||||||
For text encryption/decryption:
|
## 🚀 Usage Guide
|
||||||
|
|
||||||
Enter text in the Input Text area.
|
### 🔒 Text Encryption/Decryption
|
||||||
|
|
||||||
Choose whether to Encrypt or Decrypt.
|
- Select **Encryption Type** (Basic or Advanced)
|
||||||
|
- Enter text
|
||||||
|
- Provide password (Advanced only)
|
||||||
|
- Choose **Encrypt** or **Decrypt**
|
||||||
|
- Click **Submit**
|
||||||
|
|
||||||
Enter a password (if using advanced encryption).
|
### 📁 File Encryption/Decryption
|
||||||
|
|
||||||
For file encryption/decryption:
|
- Select **Advanced** encryption
|
||||||
|
- Upload a file
|
||||||
|
- Provide password
|
||||||
|
- Choose **Encrypt** or **Decrypt**
|
||||||
|
- Click **Submit**
|
||||||
|
|
||||||
Upload a file.
|
### 🔑 Password Generator
|
||||||
|
|
||||||
Enter a password for encryption/decryption.
|
- Click **Generate** to create a secure password
|
||||||
|
- Click **Copy** to save it to clipboard
|
||||||
|
|
||||||
Click Encrypt or Decrypt.
|
### 🎮 Pac-Man Easter Egg
|
||||||
|
|
||||||
### Password Generation
|
- Type **`pacman`** into the input box to unlock the hidden Pac-Man game!
|
||||||
|
|
||||||
Click the Generate button to create a random password, then use the Copy button to copy it to your clipboard.
|
---
|
||||||
|
|
||||||
### Pac-Man Game (Easter Egg)
|
## 🛡️ Hosting with Nginx (optional)
|
||||||
|
|
||||||
Type the word "pacman" in the input box to unlock the Pac-Man game!
|
Recommended for secure public deployment.
|
||||||
|
|
||||||
### Contributing
|
Example minimal Nginx config:
|
||||||
|
|
||||||
Feel free to open an issue or submit a pull request for improvements, bug fixes, or new features!
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name yourdomain.com;
|
||||||
|
|
||||||
### License
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:5000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
This project is open source and available under the MIT License.
|
> Tip: Set up SSL with Let's Encrypt for HTTPS security! 🔐
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📂 Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
paccrypt-webapp-final/
|
||||||
|
├── app.py
|
||||||
|
├── requirements.txt
|
||||||
|
├── templates/
|
||||||
|
│ ├── index.html
|
||||||
|
│ ├── 404.html
|
||||||
|
│ └── 403.html
|
||||||
|
│ └── 500.html
|
||||||
|
├── static/
|
||||||
|
│ ├── css/
|
||||||
|
│ │ └── styles.css
|
||||||
|
│ ├── js/
|
||||||
|
│ │ └── script.js
|
||||||
|
│ ├── img/
|
||||||
|
│ │ └── PacCrypt.png
|
||||||
|
│ └── audio/
|
||||||
|
│ └── chomp.mp3
|
||||||
|
├── start_dev.bat
|
||||||
|
├── start_prod.bat
|
||||||
|
├── start_dev.sh
|
||||||
|
├── start_prod.sh
|
||||||
|
├── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
Contributions are welcome!
|
||||||
|
|
||||||
|
- Add new features
|
||||||
|
- Fix bugs
|
||||||
|
- Improve performance
|
||||||
|
- Expand the Pac-Man Easter Egg 🎮
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
This project is licensed under the **MIT License**.
|
||||||
|
|
||||||
|
---
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
|
## DEV DEV DEV
|
||||||
|
|
||||||
|
import os
|
||||||
from flask import Flask, render_template, request, jsonify
|
from flask import Flask, render_template, request, jsonify
|
||||||
import html
|
import html
|
||||||
import os
|
|
||||||
import base64
|
import base64
|
||||||
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
||||||
from cryptography.hazmat.primitives.hashes import SHA256
|
from cryptography.hazmat.primitives.hashes import SHA256
|
||||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||||
from waitress import serve
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
# Basic Encoder/Decoder
|
# ====== Your App Code ======
|
||||||
|
|
||||||
ALPHABET = list('abcdefghijklmnopqrstuvwxyz')
|
ALPHABET = list('abcdefghijklmnopqrstuvwxyz')
|
||||||
|
|
||||||
def simple_encode(text: str) -> str:
|
def simple_encode(text: str) -> str:
|
||||||
@@ -24,7 +26,6 @@ def simple_decode(text: str) -> str:
|
|||||||
for c in text.lower()
|
for c in text.lower()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Advanced Encrypt/Decrypt using AES-GCM
|
|
||||||
def derive_key(password: str, salt: bytes) -> bytes:
|
def derive_key(password: str, salt: bytes) -> bytes:
|
||||||
kdf = PBKDF2HMAC(
|
kdf = PBKDF2HMAC(
|
||||||
algorithm=SHA256(),
|
algorithm=SHA256(),
|
||||||
@@ -56,7 +57,6 @@ def advanced_decrypt(token_b64: str, password: str) -> str:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return "[Error] Invalid password or corrupted data!"
|
return "[Error] Invalid password or corrupted data!"
|
||||||
|
|
||||||
# Combined Route for Page & AJAX
|
|
||||||
@app.route("/", methods=["GET", "POST"])
|
@app.route("/", methods=["GET", "POST"])
|
||||||
def index():
|
def index():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@@ -83,6 +83,30 @@ def index():
|
|||||||
encryption_type="advanced"
|
encryption_type="advanced"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ====== Smart Server Startup ======
|
||||||
|
|
||||||
|
@app.errorhandler(404)
|
||||||
|
def page_not_found(e):
|
||||||
|
return render_template('404.html'), 404
|
||||||
|
|
||||||
|
@app.errorhandler(500)
|
||||||
|
def server_error(e):
|
||||||
|
return render_template('500.html'), 500
|
||||||
|
|
||||||
|
@app.errorhandler(403)
|
||||||
|
def forbidden(e):
|
||||||
|
return render_template('403.html'), 403
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Use Waitress to serve the app in production
|
PRODUCTION = os.getenv("PRODUCTION", "false").lower() == "true"
|
||||||
serve(app, host="0.0.0.0", port=5000)
|
|
||||||
|
if PRODUCTION:
|
||||||
|
from waitress import serve
|
||||||
|
print("[INFO] Running in PRODUCTION mode with Waitress.")
|
||||||
|
serve(app, host="0.0.0.0", port=5000)
|
||||||
|
else:
|
||||||
|
print("[INFO] Running in DEVELOPMENT mode with Flask server.")
|
||||||
|
app.run(debug=True, host="0.0.0.0", port=5000)
|
||||||
|
|
||||||
|
|
||||||
|
## DEV DEV DEV
|
||||||
|
|||||||
+6
-3
@@ -1,5 +1,8 @@
|
|||||||
### **requirements.txt**
|
### **requirements.txt**
|
||||||
|
|
||||||
Flask==2.1.2
|
flask==3.0.3
|
||||||
cryptography==3.4.8
|
cryptography==42.0.5
|
||||||
nginx==1.21.0 # Only needed for Nginx integration, not installed via pip
|
waitress==2.1.2
|
||||||
|
|
||||||
|
# nginx - Only needed for Nginx integration, not installed via pip
|
||||||
|
# Run pip install -r requirements.txt
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
@echo off
|
||||||
|
echo Starting PacCrypt in DEVELOPMENT mode...
|
||||||
|
set PRODUCTION=false
|
||||||
|
python app.py
|
||||||
|
pause
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "Starting PacCrypt in DEVELOPMENT mode..."
|
||||||
|
export PRODUCTION=false
|
||||||
|
python3 app.py
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
@echo off
|
||||||
|
echo Starting PacCrypt in PRODUCTION mode...
|
||||||
|
set PRODUCTION=true
|
||||||
|
python app.py
|
||||||
|
pause
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "Starting PacCrypt in PRODUCTION mode..."
|
||||||
|
export PRODUCTION=true
|
||||||
|
python3 app.py
|
||||||
Binary file not shown.
+60
-47
@@ -9,23 +9,25 @@
|
|||||||
body {
|
body {
|
||||||
font-family: 'Poppins', sans-serif;
|
font-family: 'Poppins', sans-serif;
|
||||||
background-color: #121212;
|
background-color: #121212;
|
||||||
color: #f0f0f0;
|
color: #00ff99;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
justify-content: center; /* Vertically center content */
|
justify-content: center;
|
||||||
align-items: center; /* Horizontally center content */
|
align-items: center;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Header ===== */
|
/* ===== Header ===== */
|
||||||
header {
|
header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 30px 20px;
|
padding: 25px;
|
||||||
background-color: #1c1c1c;
|
background-color: #1c1c1c;
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5);
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 0 15px rgba(0, 255, 153, 0.4);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
|
margin-bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
header h1 {
|
header h1 {
|
||||||
@@ -35,7 +37,7 @@ header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
header p {
|
header p {
|
||||||
font-size: 1.1em;
|
font-size: 1.2em;
|
||||||
color: #00ff99;
|
color: #00ff99;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,13 +51,12 @@ main {
|
|||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
gap: 30px;
|
gap: 30px;
|
||||||
justify-content: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Section Card Styling ===== */
|
/* ===== Section Card Styling ===== */
|
||||||
.card {
|
.card {
|
||||||
background-color: #1e1e1e;
|
background-color: #1e1e1e;
|
||||||
padding: 20px 25px;
|
padding: 25px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -68,7 +69,7 @@ main {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 15px;
|
gap: 20px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +93,8 @@ textarea {
|
|||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="password"] {
|
input[type="password"],
|
||||||
|
#password {
|
||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +125,7 @@ select:focus {
|
|||||||
box-shadow: 0 0 8px rgba(0, 255, 153, 0.8);
|
box-shadow: 0 0 8px rgba(0, 255, 153, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Match input and output textarea sizes ===== */
|
/* ===== Match input and output sizes ===== */
|
||||||
#input-text,
|
#input-text,
|
||||||
#output-text {
|
#output-text {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
@@ -138,8 +140,8 @@ select:focus {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 8px;
|
gap: 15px;
|
||||||
margin-top: 8px;
|
margin-top: 15px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,8 +154,8 @@ button {
|
|||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: 0.3s;
|
transition: 0.3s;
|
||||||
width: 100%; /* Makes buttons stretch to fill container */
|
width: 100%;
|
||||||
max-width: 200px; /* Restricts button width */
|
max-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
button:hover {
|
||||||
@@ -161,7 +163,7 @@ button {
|
|||||||
color: #121212;
|
color: #121212;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Toggle Buttons ===== */
|
/* ===== Toggle Buttons (Encode/Decode, Encrypt/Decrypt) ===== */
|
||||||
.radio-group {
|
.radio-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -174,13 +176,15 @@ button {
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 8px 18px;
|
padding: 1px 1px;
|
||||||
border: 2px solid #00ff99;
|
border: 2px solid #00ff99;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: #2c2f33;
|
background-color: #2c2f33;
|
||||||
color: #00ff99;
|
color: #00ff99;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: 0.3s;
|
transition: 0.3s;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio-button:hover {
|
.radio-button:hover {
|
||||||
@@ -188,21 +192,27 @@ button {
|
|||||||
color: #121212;
|
color: #121212;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hide the actual radio input */
|
||||||
.radio-button input {
|
.radio-button input {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* When selected, make the ENTIRE BUTTON glow */
|
||||||
.radio-button input:checked + span {
|
.radio-button input:checked + span {
|
||||||
background-color: #00ff99;
|
background-color: #2c2f33;
|
||||||
color: #121212;
|
color: #00ff99;
|
||||||
padding: 8px 18px;
|
box-shadow: 0 0 15px rgba(0, 255, 153, 0.7);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
display: inline-block;
|
padding: 8px 18px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ===== Remove File Button ===== */
|
/* ===== Remove File Button ===== */
|
||||||
#remove-file-btn {
|
#remove-file-btn {
|
||||||
display: none; /* only shows when a file is selected */
|
display: none;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border: 2px solid #ff5555;
|
border: 2px solid #ff5555;
|
||||||
@@ -221,14 +231,19 @@ button {
|
|||||||
/* ===== Toast Notifications ===== */
|
/* ===== Toast Notifications ===== */
|
||||||
.toast {
|
.toast {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
min-width: 250px;
|
width: 80%;
|
||||||
|
max-width: 500px;
|
||||||
|
min-height: 50px;
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
color: #00ff99;
|
color: #00ff99;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 6px;
|
border-radius: 8px;
|
||||||
padding: 10px;
|
padding: 14px;
|
||||||
margin-top: 8px;
|
margin: 10px auto 0 auto;
|
||||||
font-size: 0.9em;
|
font-size: 1em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,11 +289,14 @@ button {
|
|||||||
/* ===== Footer ===== */
|
/* ===== Footer ===== */
|
||||||
footer {
|
footer {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 18px;
|
padding: 25px;
|
||||||
background-color: #1c1c1c;
|
background-color: #1c1c1c;
|
||||||
color: #00ff99;
|
color: #00ff99;
|
||||||
margin-top: auto;
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 0 15px rgba(0, 255, 153, 0.4);
|
||||||
|
margin-top: 30px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 800px;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer a {
|
footer a {
|
||||||
@@ -290,22 +308,17 @@ footer {
|
|||||||
color: #ff0066;
|
color: #ff0066;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Password Input Field ===== */
|
/* ===== Responsive Tweaks ===== */
|
||||||
#password-input {
|
@media (max-width: 600px) {
|
||||||
display: flex; /* Password input is visible by default */
|
input,
|
||||||
margin-top: 15px;
|
textarea,
|
||||||
flex-direction: column;
|
select,
|
||||||
gap: 10px;
|
#input-text,
|
||||||
width: 100%;
|
#output-text,
|
||||||
max-width: 500px;
|
#password-field,
|
||||||
}
|
#password,
|
||||||
|
#file-password {
|
||||||
#password-input input {
|
width: 100%;
|
||||||
padding: 12px;
|
max-width: 90%;
|
||||||
font-size: 1em;
|
|
||||||
border: 2px solid #00ff99;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: #2c2f33;
|
|
||||||
color: #00ff99;
|
|
||||||
width: 100%; /* Ensure the password field takes full width */
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
+85
-106
@@ -1,41 +1,31 @@
|
|||||||
// ===== AES Encryption =====
|
// ===== AES Advanced Encryption =====
|
||||||
async function encryptAdvanced(message, password) {
|
async function encryptAdvanced(message, password) {
|
||||||
// Create a random salt for key derivation
|
|
||||||
const salt = crypto.getRandomValues(new Uint8Array(16));
|
const salt = crypto.getRandomValues(new Uint8Array(16));
|
||||||
|
|
||||||
// Derive a key from the password using PBKDF2 and the salt
|
|
||||||
const key = await deriveKey(password, salt);
|
const key = await deriveKey(password, salt);
|
||||||
|
|
||||||
// Create a random initialization vector (IV)
|
|
||||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||||
|
|
||||||
// Encode the message as a Uint8Array
|
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
const encodedMessage = encoder.encode(message);
|
const encodedMessage = encoder.encode(message);
|
||||||
|
|
||||||
// Encrypt the message using AES-GCM
|
|
||||||
const encryptedMessage = await crypto.subtle.encrypt(
|
const encryptedMessage = await crypto.subtle.encrypt(
|
||||||
{ name: 'AES-GCM', iv: iv },
|
{ name: 'AES-GCM', iv: iv },
|
||||||
key,
|
key,
|
||||||
encodedMessage
|
encodedMessage
|
||||||
);
|
);
|
||||||
|
|
||||||
// Combine salt, IV, and encrypted message
|
|
||||||
const encryptedArray = new Uint8Array(salt.length + iv.length + encryptedMessage.byteLength);
|
const encryptedArray = new Uint8Array(salt.length + iv.length + encryptedMessage.byteLength);
|
||||||
encryptedArray.set(salt);
|
encryptedArray.set(salt);
|
||||||
encryptedArray.set(iv, salt.length);
|
encryptedArray.set(iv, salt.length);
|
||||||
encryptedArray.set(new Uint8Array(encryptedMessage), salt.length + iv.length);
|
encryptedArray.set(new Uint8Array(encryptedMessage), salt.length + iv.length);
|
||||||
|
|
||||||
// Convert the result to base64 to send to the server
|
return btoa(String.fromCharCode(...encryptedArray));
|
||||||
return btoa(String.fromCharCode.apply(null, encryptedArray));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive a key from the password using PBKDF2
|
|
||||||
async function deriveKey(password, salt) {
|
async function deriveKey(password, salt) {
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
const passwordBuffer = encoder.encode(password);
|
const passwordBuffer = encoder.encode(password);
|
||||||
|
|
||||||
const key = await crypto.subtle.importKey(
|
const keyMaterial = await crypto.subtle.importKey(
|
||||||
'raw',
|
'raw',
|
||||||
passwordBuffer,
|
passwordBuffer,
|
||||||
{ name: 'PBKDF2' },
|
{ name: 'PBKDF2' },
|
||||||
@@ -47,37 +37,31 @@ async function deriveKey(password, salt) {
|
|||||||
{
|
{
|
||||||
name: 'PBKDF2',
|
name: 'PBKDF2',
|
||||||
salt: salt,
|
salt: salt,
|
||||||
iterations: 100000,
|
iterations: 200000,
|
||||||
hash: 'SHA-256',
|
hash: 'SHA-256',
|
||||||
},
|
},
|
||||||
key,
|
keyMaterial,
|
||||||
{ name: 'AES-GCM', length: 256 },
|
{ name: 'AES-GCM', length: 256 },
|
||||||
false,
|
false,
|
||||||
['encrypt', 'decrypt']
|
['encrypt', 'decrypt']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== AES Decryption =====
|
|
||||||
async function decryptAdvanced(encryptedData, password) {
|
async function decryptAdvanced(encryptedData, password) {
|
||||||
// Decode the base64-encoded encrypted data
|
const encryptedArray = new Uint8Array(atob(encryptedData).split("").map(c => c.charCodeAt(0)));
|
||||||
const encryptedArray = new Uint8Array(atob(encryptedData).split("").map(char => char.charCodeAt(0)));
|
|
||||||
|
|
||||||
// Extract salt, IV, and encrypted message from the encrypted data
|
|
||||||
const salt = encryptedArray.slice(0, 16);
|
const salt = encryptedArray.slice(0, 16);
|
||||||
const iv = encryptedArray.slice(16, 28);
|
const iv = encryptedArray.slice(16, 28);
|
||||||
const encryptedMessage = encryptedArray.slice(28);
|
const encryptedMessage = encryptedArray.slice(28);
|
||||||
|
|
||||||
// Derive the key from the password and salt
|
|
||||||
const key = await deriveKey(password, salt);
|
const key = await deriveKey(password, salt);
|
||||||
|
|
||||||
// Decrypt the message using AES-GCM
|
|
||||||
const decryptedMessage = await crypto.subtle.decrypt(
|
const decryptedMessage = await crypto.subtle.decrypt(
|
||||||
{ name: 'AES-GCM', iv: iv },
|
{ name: 'AES-GCM', iv: iv },
|
||||||
key,
|
key,
|
||||||
encryptedMessage
|
encryptedMessage
|
||||||
);
|
);
|
||||||
|
|
||||||
// Decode the decrypted message to text
|
|
||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
return decoder.decode(decryptedMessage);
|
return decoder.decode(decryptedMessage);
|
||||||
}
|
}
|
||||||
@@ -85,81 +69,54 @@ async function decryptAdvanced(encryptedData, password) {
|
|||||||
// ===== UI Toggles =====
|
// ===== UI Toggles =====
|
||||||
function toggleEncryptionOptions() {
|
function toggleEncryptionOptions() {
|
||||||
const type = document.getElementById("encryption-type").value;
|
const type = document.getElementById("encryption-type").value;
|
||||||
const pwdContainer = document.getElementById("password-input");
|
document.getElementById("password-input").style.display = (type === 'advanced') ? 'flex' : 'none';
|
||||||
pwdContainer.style.display = (type === 'advanced') ? 'flex' : 'none';
|
|
||||||
if (type === 'basic') removeFile();
|
if (type === 'basic') removeFile();
|
||||||
|
|
||||||
toggleInputMode();
|
toggleInputMode();
|
||||||
document.getElementById("encrypt-label").textContent =
|
document.getElementById("encrypt-label").textContent = (type === 'basic') ? "Encode" : "Encrypt";
|
||||||
(type === 'basic') ? "Encode" : "Encrypt";
|
document.getElementById("decrypt-label").textContent = (type === 'basic') ? "Decode" : "Decrypt";
|
||||||
document.getElementById("decrypt-label").textContent =
|
|
||||||
(type === 'basic') ? "Decode" : "Decrypt";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Remove File Button =====
|
|
||||||
function removeFile() {
|
function removeFile() {
|
||||||
document.getElementById("file-input").value = ""; // Clear the file input
|
document.getElementById("file-input").value = "";
|
||||||
document.getElementById("remove-file-btn").style.display = 'none'; // Hide the remove file button
|
document.getElementById("remove-file-btn").style.display = 'none';
|
||||||
toggleInputMode(); // Reapply the input mode logic
|
toggleInputMode();
|
||||||
document.getElementById("file-password-input").style.display = 'none'; // Hide the file password input
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Input vs. File Toggle =====
|
|
||||||
function toggleInputMode() {
|
function toggleInputMode() {
|
||||||
const textValue = document.getElementById("input-text").value.trim();
|
const textValue = document.getElementById("input-text").value.trim();
|
||||||
const fileSelected = document.getElementById("file-input").files.length > 0;
|
const fileSelected = document.getElementById("file-input").files.length > 0;
|
||||||
const isAdvanced = document.getElementById("encryption-type").value === 'advanced';
|
const isAdvanced = document.getElementById("encryption-type").value === 'advanced';
|
||||||
|
|
||||||
// Show/hide text area based on file selection
|
document.getElementById("text-section").style.display = fileSelected ? 'none' : 'flex';
|
||||||
document.getElementById("text-section").style.display =
|
document.getElementById("file-section").style.display = (isAdvanced && !textValue) ? 'flex' : 'none';
|
||||||
fileSelected ? 'none' : 'flex';
|
document.getElementById("remove-file-btn").style.display = fileSelected ? 'inline-block' : 'none';
|
||||||
|
|
||||||
// Show/hide file input section when in advanced mode and no text input is given
|
|
||||||
document.getElementById("file-section").style.display =
|
|
||||||
(isAdvanced && !textValue) ? 'flex' : 'none';
|
|
||||||
|
|
||||||
// Show/hide the remove file button
|
|
||||||
document.getElementById("remove-file-btn").style.display =
|
|
||||||
fileSelected ? 'inline-block' : 'none';
|
|
||||||
|
|
||||||
// ALWAYS show the password input in advanced mode
|
|
||||||
if (isAdvanced) {
|
if (isAdvanced) {
|
||||||
document.getElementById("password-input").style.display = 'flex';
|
document.getElementById("password-input").style.display = 'flex';
|
||||||
} else {
|
|
||||||
document.getElementById("password-input").style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show the dedicated password input for file encryption if a file is selected
|
|
||||||
if (fileSelected) {
|
|
||||||
document.getElementById("file-password-input").style.display = 'flex'; // Show password input for files
|
|
||||||
} else {
|
|
||||||
document.getElementById("file-password-input").style.display = 'none'; // Hide when no file is selected
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Validate and Submit Form =====
|
// ===== Form Submission =====
|
||||||
async function handleSubmit(event) {
|
async function handleSubmit(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// If the encryption type is advanced, ensure password is provided
|
|
||||||
const password = document.getElementById("password").value;
|
const password = document.getElementById("password").value;
|
||||||
const filePassword = document.getElementById("file-password") ? document.getElementById("file-password").value : '';
|
|
||||||
const encryptionType = document.getElementById("encryption-type").value;
|
const encryptionType = document.getElementById("encryption-type").value;
|
||||||
|
|
||||||
if (encryptionType === 'advanced' && !password && !filePassword) {
|
if (encryptionType === 'advanced' && !password) {
|
||||||
alert("Password is required for advanced encryption.");
|
alert("Password is required for advanced encryption.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the form data
|
|
||||||
const payload = {
|
const payload = {
|
||||||
"encryption-type": encryptionType,
|
"encryption-type": encryptionType,
|
||||||
operation: document.querySelector('input[name="operation"]:checked').value,
|
operation: document.querySelector('input[name="operation"]:checked').value,
|
||||||
message: document.getElementById("input-text").value,
|
message: document.getElementById("input-text").value,
|
||||||
password: password,
|
password: password
|
||||||
"file-password": filePassword
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle file upload encryption/decryption
|
|
||||||
const fileInput = document.getElementById("file-input");
|
const fileInput = document.getElementById("file-input");
|
||||||
if (fileInput.files.length > 0) {
|
if (fileInput.files.length > 0) {
|
||||||
const op = document.querySelector('input[name="operation"]:checked').value;
|
const op = document.querySelector('input[name="operation"]:checked').value;
|
||||||
@@ -168,7 +125,6 @@ async function handleSubmit(event) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle text encryption/decryption
|
|
||||||
try {
|
try {
|
||||||
const resp = await fetch("/", {
|
const resp = await fetch("/", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -185,30 +141,35 @@ async function handleSubmit(event) {
|
|||||||
// ===== File Encryption / Decryption =====
|
// ===== File Encryption / Decryption =====
|
||||||
function encryptFile() {
|
function encryptFile() {
|
||||||
const f = document.getElementById("file-input");
|
const f = document.getElementById("file-input");
|
||||||
const pwd = document.getElementById("file-password").value;
|
const pwd = document.getElementById("password").value;
|
||||||
if (!pwd) return alert("Please enter a password!");
|
if (!pwd) return alert("Please enter a password!");
|
||||||
if (!f.files.length) return alert("Please select a file!");
|
if (!f.files.length) return alert("Please select a file!");
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = async (e) => {
|
reader.onload = async (e) => {
|
||||||
const raw = e.target.result;
|
const raw = new Uint8Array(e.target.result);
|
||||||
let encryptedMessage = await encryptAdvanced(raw, pwd);
|
const base64Raw = btoa(String.fromCharCode(...raw));
|
||||||
downloadFile(encryptedMessage, f.files[0].name + ".enc");
|
const encrypted = await encryptAdvanced(base64Raw, pwd);
|
||||||
|
downloadFile(encrypted, f.files[0].name + ".enc");
|
||||||
};
|
};
|
||||||
reader.readAsText(f.files[0]);
|
reader.readAsArrayBuffer(f.files[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function decryptFile() {
|
function decryptFile() {
|
||||||
const f = document.getElementById("file-input");
|
const f = document.getElementById("file-input");
|
||||||
const pwd = document.getElementById("file-password").value;
|
const pwd = document.getElementById("password").value;
|
||||||
if (!pwd) return alert("Please enter a password!");
|
if (!pwd) return alert("Please enter a password!");
|
||||||
if (!f.files.length) return alert("Please select a file!");
|
if (!f.files.length) return alert("Please select a file!");
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = async (e) => {
|
reader.onload = async (e) => {
|
||||||
try {
|
try {
|
||||||
const enc = e.target.result;
|
const encryptedText = e.target.result;
|
||||||
const decryptedMessage = await decryptAdvanced(enc, pwd);
|
const decryptedBase64 = await decryptAdvanced(encryptedText, pwd);
|
||||||
downloadFile(decryptedMessage, f.files[0].name.replace(/\.enc$/, ''));
|
const byteString = atob(decryptedBase64);
|
||||||
} catch {
|
const byteArray = new Uint8Array([...byteString].map(c => c.charCodeAt(0)));
|
||||||
|
downloadFileBinary(byteArray, f.files[0].name.replace(/\.enc$/, ''));
|
||||||
|
} catch (err) {
|
||||||
alert("Decryption failed: wrong password or corrupted file.");
|
alert("Decryption failed: wrong password or corrupted file.");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -225,6 +186,16 @@ function downloadFile(content, filename) {
|
|||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function downloadFileBinary(byteArray, filename) {
|
||||||
|
const blob = new Blob([byteArray], { type: "application/octet-stream" });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
// ===== Password Generator =====
|
// ===== Password Generator =====
|
||||||
function generateRandomPassword() {
|
function generateRandomPassword() {
|
||||||
const length = 30;
|
const length = 30;
|
||||||
@@ -240,13 +211,12 @@ function generateRandomPassword() {
|
|||||||
function copyToClipboard(elementId, toastId) {
|
function copyToClipboard(elementId, toastId) {
|
||||||
const copyText = document.getElementById(elementId);
|
const copyText = document.getElementById(elementId);
|
||||||
copyText.select();
|
copyText.select();
|
||||||
copyText.setSelectionRange(0, 99999); // For mobile devices
|
copyText.setSelectionRange(0, 99999);
|
||||||
document.execCommand("copy");
|
document.execCommand("copy");
|
||||||
|
|
||||||
// Show toast notification
|
|
||||||
const toast = document.getElementById(toastId);
|
const toast = document.getElementById(toastId);
|
||||||
toast.classList.add("show");
|
toast.classList.add("show");
|
||||||
setTimeout(() => toast.classList.remove("show"), 2000); // Remove toast after 2 seconds
|
setTimeout(() => toast.classList.remove("show"), 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Pacman Easter Egg =====
|
// ===== Pacman Easter Egg =====
|
||||||
@@ -277,6 +247,28 @@ function resetGame() {
|
|||||||
startPacman();
|
startPacman();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Clear All =====
|
||||||
|
function clearAll() {
|
||||||
|
document.getElementById("input-text").value = "";
|
||||||
|
document.getElementById("output-text").value = "";
|
||||||
|
document.getElementById("file-input").value = "";
|
||||||
|
document.getElementById("password").value = "";
|
||||||
|
|
||||||
|
document.getElementById("pacman-section").style.display = "none";
|
||||||
|
document.getElementById("encoding-section").style.display = "block";
|
||||||
|
|
||||||
|
removeFile();
|
||||||
|
toggleInputMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Initialize =====
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
toggleEncryptionOptions();
|
||||||
|
toggleInputMode();
|
||||||
|
document.getElementById("input-text").addEventListener("input", checkForPacman);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// ===== Pacman Game Variables & Logic =====
|
// ===== Pacman Game Variables & Logic =====
|
||||||
let canvas, ctx, pacman, enemy, walls, dots, score;
|
let canvas, ctx, pacman, enemy, walls, dots, score;
|
||||||
let pacmanSpeed = 40, enemySpeed = 20, cellSize = 40, dotSize = 5;
|
let pacmanSpeed = 40, enemySpeed = 20, cellSize = 40, dotSize = 5;
|
||||||
@@ -441,18 +433,27 @@ function drawChar(ch,color) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function eatDots() {
|
function eatDots() {
|
||||||
dots = dots.filter(d=>{
|
const chompSound = document.getElementById("chomp-sound");
|
||||||
const dx = d.c*cellSize+cellSize/2, dy = d.r*cellSize+cellSize/2;
|
|
||||||
if (Math.abs(pacman.x-dx)<pacman.size && Math.abs(pacman.y-dy)<pacman.size) {
|
dots = dots.filter(d => {
|
||||||
|
const dx = d.c * cellSize + cellSize / 2;
|
||||||
|
const dy = d.r * cellSize + cellSize / 2;
|
||||||
|
if (Math.abs(pacman.x - dx) < pacman.size && Math.abs(pacman.y - dy) < pacman.size) {
|
||||||
score++;
|
score++;
|
||||||
return false;
|
if (chompSound) {
|
||||||
|
chompSound.currentTime = 0; // Reset sound
|
||||||
|
chompSound.volume = 0.4;
|
||||||
|
chompSound.play();
|
||||||
|
}
|
||||||
|
return false; // Remove dot
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
ctx.fillStyle="white";
|
|
||||||
dots.forEach(d=>{
|
ctx.fillStyle = "white";
|
||||||
|
dots.forEach(d => {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(d.c*cellSize+cellSize/2, d.r*cellSize+cellSize/2, dotSize,0,Math.PI*2);
|
ctx.arc(d.c * cellSize + cellSize / 2, d.r * cellSize + cellSize / 2, dotSize, 0, Math.PI * 2);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -472,25 +473,3 @@ function checkGameOver() {
|
|||||||
clearInterval(gameInterval);
|
clearInterval(gameInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Clear All Functionality =====
|
|
||||||
function clearAll() {
|
|
||||||
document.getElementById("input-text").value = "";
|
|
||||||
document.getElementById("output-text").value = "";
|
|
||||||
document.getElementById("file-input").value = "";
|
|
||||||
document.getElementById("password").value = "";
|
|
||||||
document.getElementById("file-password").value = "";
|
|
||||||
|
|
||||||
document.getElementById("pacman-section").style.display = "none";
|
|
||||||
document.getElementById("encoding-section").style.display = "block";
|
|
||||||
|
|
||||||
removeFile();
|
|
||||||
toggleInputMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== Initialize =====
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
|
||||||
toggleEncryptionOptions();
|
|
||||||
toggleInputMode();
|
|
||||||
document.getElementById("input-text").addEventListener("input", checkForPacman);
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>403 - PacCrypt</title>
|
||||||
|
<link rel="icon" type="image/png" href="{{ url_for('static', filename='img/PacCrypt.png') }}">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||||
|
</head>
|
||||||
|
<body class="dark">
|
||||||
|
|
||||||
|
<header class="card" style="margin-bottom: 20px;">
|
||||||
|
<h1>PacCrypt</h1>
|
||||||
|
<p>Secure Encoding, Encryption and Password Generation</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="card" style="padding: 50px 30px;">
|
||||||
|
<h2 style="color: #00ff99; font-size: 2.5em;">403 - Forbidden</h2>
|
||||||
|
<p style="margin-top: 20px; font-size: 1.2em; color: #cccccc;">
|
||||||
|
Looks like this area is locked behind a secret ghost door! 🛡️👻
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="button-group" style="margin-top: 30px;">
|
||||||
|
<a href="{{ url_for('index') }}">
|
||||||
|
<button type="button">Return Home</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="card" style="margin-top: 20px;">
|
||||||
|
<p>© 2025 UnNaturalll-Dev. All rights reserved.</p>
|
||||||
|
<a href="https://github.com/TySP-Dev" target="_blank" id="github-link">
|
||||||
|
<img src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Flogos-world.net%2Fwp-content%2Fuploads%2F2020%2F11%2FGitHub-Logo.png&f=1&nofb=1&ipt=b9d67651e313b2cdbeae8a7ec9320dadb278a21a2e7217810b839c233c04f265"
|
||||||
|
alt="GitHub Logo" width="100">
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>404 - PacCrypt</title>
|
||||||
|
<link rel="icon" type="image/png" href="{{ url_for('static', filename='img/PacCrypt.png') }}">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||||
|
</head>
|
||||||
|
<body class="dark">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h1>PacCrypt</h1>
|
||||||
|
<p>Secure Encoding, Encryption and Password Generation</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
|
||||||
|
<section class="card">
|
||||||
|
<h2>404 - Page Not Found</h2>
|
||||||
|
<p>Oops! The page you're looking for doesn't exist.</p>
|
||||||
|
|
||||||
|
<div class="button-group" style="margin-top: 20px;">
|
||||||
|
<a href="{{ url_for('index') }}">
|
||||||
|
<button type="button">Return Home</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 UnNaturalll-Dev. All rights reserved.</p>
|
||||||
|
<a href="https://github.com/TySP-Dev" target="_blank" id="github-link">
|
||||||
|
<img src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Flogos-world.net%2Fwp-content%2Fuploads%2F2020%2F11%2FGitHub-Logo.png&f=1&nofb=1&ipt=b9d67651e313b2cdbeae8a7ec9320dadb278a21a2e7217810b839c233c04f265"
|
||||||
|
alt="GitHub Logo" width="100">
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>500 - PacCrypt</title>
|
||||||
|
<link rel="icon" type="image/png" href="{{ url_for('static', filename='img/PacCrypt.png') }}">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||||
|
</head>
|
||||||
|
<body class="dark">
|
||||||
|
|
||||||
|
<header class="card" style="margin-bottom: 20px;">
|
||||||
|
<h1>PacCrypt</h1>
|
||||||
|
<p>Secure Encoding, Encryption and Password Generation</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="card" style="padding: 50px 30px;">
|
||||||
|
<h2 style="color: #00ff99; font-size: 2.5em;">500 - Server Error</h2>
|
||||||
|
<p style="margin-top: 20px; font-size: 1.2em; color: #cccccc;">
|
||||||
|
Uh oh! The ghosts chomped the server. 🧟♂️
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="button-group" style="margin-top: 30px;">
|
||||||
|
<a href="{{ url_for('index') }}">
|
||||||
|
<button type="button">Return Home</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="card" style="margin-top: 20px;">
|
||||||
|
<p>© 2025 UnNaturalll-Dev. All rights reserved.</p>
|
||||||
|
<a href="https://github.com/TySP-Dev" target="_blank" id="github-link">
|
||||||
|
<img src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Flogos-world.net%2Fwp-content%2Fuploads%2F2020%2F11%2FGitHub-Logo.png&f=1&nofb=1&ipt=b9d67651e313b2cdbeae8a7ec9320dadb278a21a2e7217810b839c233c04f265"
|
||||||
|
alt="GitHub Logo" width="100">
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+34
-22
@@ -4,29 +4,31 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>PacCrypt</title>
|
<title>PacCrypt</title>
|
||||||
<!-- Favicon Link -->
|
|
||||||
<link rel="icon" type="image/png" href="{{ url_for('static', filename='img/PacCrypt.png') }}">
|
|
||||||
|
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" type="image/png" href="{{ url_for('static', filename='img/PacCrypt.png') }}" />
|
||||||
|
|
||||||
|
<!-- Styles and Scripts -->
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet" />
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}" />
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}" />
|
||||||
<script defer src="{{ url_for('static', filename='js/script.js') }}"></script>
|
<script defer src="{{ url_for('static', filename='js/script.js') }}"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="dark">
|
<body class="dark">
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
<header>
|
<header>
|
||||||
<h1>PacCrypt</h1>
|
<h1>PacCrypt</h1>
|
||||||
<p>Secure Encoding, Encryption and Password Generation</p>
|
<p>Secure Encoding, Encryption and Password Generation</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
|
<!-- Password Generator Section -->
|
||||||
<section id="password-section" class="card">
|
<section id="password-section" class="card">
|
||||||
<h2>Password Generator</h2>
|
<h2>Password Generator</h2>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input
|
<input type="text" id="password-field" placeholder="Generated password will appear here" readonly />
|
||||||
type="text"
|
|
||||||
id="password-field"
|
|
||||||
placeholder="Generated password will appear here"
|
|
||||||
readonly
|
|
||||||
/>
|
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<button type="button" onclick="generateRandomPassword()">Generate</button>
|
<button type="button" onclick="generateRandomPassword()">Generate</button>
|
||||||
<button type="button" onclick="copyToClipboard('password-field', 'password-toast')">Copy</button>
|
<button type="button" onclick="copyToClipboard('password-field', 'password-toast')">Copy</button>
|
||||||
@@ -35,6 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Pacman Game Section -->
|
||||||
<section id="pacman-section" class="card" style="display: none;">
|
<section id="pacman-section" class="card" style="display: none;">
|
||||||
<div class="pacman-wrapper">
|
<div class="pacman-wrapper">
|
||||||
<canvas id="pacmanCanvas" width="800" height="600"></canvas>
|
<canvas id="pacmanCanvas" width="800" height="600"></canvas>
|
||||||
@@ -46,15 +49,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Text Encoder/Decoder & File Encrypt/Decrypt Section -->
|
||||||
<section id="encoding-section" class="card">
|
<section id="encoding-section" class="card">
|
||||||
<h2>Text Encoder / Decoder & File Encryption</h2>
|
<h2>Text Encoder / Decoder & File Encryption</h2>
|
||||||
<form id="main-form" class="form-group" method="POST" onsubmit="handleSubmit(event)">
|
<form id="main-form" class="form-group" method="POST" onsubmit="handleSubmit(event)">
|
||||||
|
|
||||||
|
<!-- Encryption Type Dropdown -->
|
||||||
<label for="encryption-type">Select Encryption Type:</label>
|
<label for="encryption-type">Select Encryption Type:</label>
|
||||||
<select id="encryption-type" name="encryption-type" onchange="toggleEncryptionOptions()">
|
<select id="encryption-type" name="encryption-type" onchange="toggleEncryptionOptions()">
|
||||||
<option value="basic">Basic (Less Secure)</option>
|
<option value="basic">Basic (Less Secure)</option>
|
||||||
<option value="advanced" selected>Advanced (More Secure)</option>
|
<option value="advanced" selected>Advanced (More Secure)</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- Encrypt / Decrypt Radio Buttons -->
|
||||||
<div id="encryption-options" class="radio-group">
|
<div id="encryption-options" class="radio-group">
|
||||||
<label class="radio-button">
|
<label class="radio-button">
|
||||||
<input type="radio" name="operation" value="encrypt" id="encrypt-radio" checked />
|
<input type="radio" name="operation" value="encrypt" id="encrypt-radio" checked />
|
||||||
@@ -66,41 +73,45 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Text Area Input -->
|
||||||
<div id="text-section" class="form-group">
|
<div id="text-section" class="form-group">
|
||||||
<textarea
|
<textarea id="input-text" name="message" placeholder="Enter text here..." oninput="toggleInputMode()"></textarea>
|
||||||
id="input-text"
|
|
||||||
name="message"
|
|
||||||
placeholder="Enter text here..."
|
|
||||||
oninput="toggleInputMode()"
|
|
||||||
></textarea>
|
|
||||||
<div id="password-input">
|
|
||||||
<input type="password" id="password" name="password" placeholder="Enter Password" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Password Input (shared for file + text) -->
|
||||||
|
<div id="password-input" class="form-group">
|
||||||
|
<input type="password" id="password" name="password" placeholder="Enter Password" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- File Upload Section -->
|
||||||
<div id="file-section" class="form-group" style="display: none;">
|
<div id="file-section" class="form-group" style="display: none;">
|
||||||
<input type="file" id="file-input" onchange="toggleInputMode()" />
|
<input type="file" id="file-input" onchange="toggleInputMode()" />
|
||||||
<button type="button" id="remove-file-btn" onclick="removeFile()">Remove File</button>
|
<button type="button" id="remove-file-btn" onclick="removeFile()">Remove File</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="file-password-input" style="display: none;">
|
<!-- Submit Button -->
|
||||||
<input type="password" id="file-password" name="file-password" placeholder="Enter Password for File" />
|
<div class="button-group">
|
||||||
|
<button type="submit" class="submit-button">Submit</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="button" class="submit-button" onclick="handleSubmit(event)">Submit</button>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- Output Text Area -->
|
||||||
<div style="height: 20px;"></div>
|
<div style="height: 20px;"></div>
|
||||||
|
|
||||||
<textarea id="output-text" readonly placeholder="Result will appear here">{{ result }}</textarea>
|
<textarea id="output-text" readonly placeholder="Result will appear here">{{ result }}</textarea>
|
||||||
|
|
||||||
|
<!-- Output Controls -->
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<button type="button" onclick="copyToClipboard('output-text', 'output-toast')">Copy Output</button>
|
<button type="button" onclick="copyToClipboard('output-text', 'output-toast')">Copy Output</button>
|
||||||
<button type="button" onclick="clearAll()">Clear All</button>
|
<button type="button" onclick="clearAll()">Clear All</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Output Toast Notification -->
|
||||||
<div id="output-toast" class="toast">Copied to Clipboard!</div>
|
<div id="output-toast" class="toast">Copied to Clipboard!</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
<footer>
|
<footer>
|
||||||
<p>© 2025 UnNaturalll-Dev. All rights reserved.</p>
|
<p>© 2025 UnNaturalll-Dev. All rights reserved.</p>
|
||||||
<a href="https://github.com/TySP-Dev" target="_blank" id="github-link">
|
<a href="https://github.com/TySP-Dev" target="_blank" id="github-link">
|
||||||
@@ -108,5 +119,6 @@
|
|||||||
alt="GitHub Logo" width="100" />
|
alt="GitHub Logo" width="100" />
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user