Architecture
The system follows a hybrid (envelope) encryption model layered on top of a simple Streamlit front end.
High-level overview
+---------------------------+
User -------> | Streamlit UI (main.py) |
+-------------+-------------+
|
+---------------------+---------------------+
| |
v v
+-----------------------+ +-----------------------+
| Authentication | | Encryption layer |
| login.py / | | data.py |
| create_user.py | +-----+-----------+-----+
+-----------+-----------+ | |
| | |
v v v
+-------------------+ +-------------------+ +-------------------+
| logins.txt | | keys/ | | records.txt |
| bcrypt-hashed | | RSA keypairs | | encrypted records |
| users | | per role | | |
+-------------------+ +---------+---------+ +-------------------+
^
| generates
+-------+--------+
| generator.py |
+----------------+
Encryption flow (write)
An authorized user submits a record through a dynamic form defined in fields.json. data.py generates a fresh AES key + IV, encrypts the record, then wraps the AES key once per authorized role with that role's RSA public key.
User fills record form (fields.json)
|
v
Generate random AES key + IV
|
+--------+-----------------------------+
| |
v v
AES-CBC encrypt record For each authorized role:
(+ PKCS#7 padding) load role's RSA-2048 public key
| |
| v
| RSA-OAEP encrypt AES key
| (= per-role key envelope)
| |
+------------------+-------------------+
v
Assemble JSON record:
IV + ciphertext + {role -> encrypted key} map
|
v
Append base64 JSON line to records.txt
Decryption flow (read)
A user's role selects which RSA private key is loaded. For each record, the matching encrypted AES key is unwrapped and used to decrypt the ciphertext; records with no key for that role are reported as unauthorized.
Logged-in user's role
|
v
Load role's RSA-2048 private key from keys/
|
v
For each record in records.txt:
|
v
Encrypted AES key exists for this role? ----- No ----> Report: not authorized
|
Yes
|
v
RSA-OAEP decrypt --> recover AES key
|
v
AES-CBC decrypt ciphertext (using stored IV)
|
v
Display decrypted record
Authentication & access control
- Credentials live in JSON-formatted text files; passwords are hashed with bcrypt (salted) in
login.py/create_user.py. - Each user carries a list of attributes (role, optionally a specialty) and a
can_writeflag. main.pyenforces role-based UI gating: only Admins see the Admin Panel and Dashboard, only users with write permission can encrypt, and only roles with a matching RSA key can decrypt a given record.
This design simulates Attribute-Based Encryption (ABE) behavior — a record can be made readable by multiple roles at once — using only RSA and AES primitives.
Tech Stack
| Layer | Tools |
|---|---|
| Language | Python 3 |
| Web framework / UI | Streamlit |
| Cryptography | PyCryptodome — AES-128 (CBC) record encryption, RSA-2048 + PKCS1_OAEP key wrapping |
| Password hashing | bcrypt (salted) |
| Data handling | pandas (dashboard charts), Python stdlib (json, base64, datetime, os) |
| Storage | Flat files — records.txt (JSON-lines), logins.txt (JSON), keys/ (PEM RSA keypairs) |