Back to case studies

Software case study

Encryptron

A file encryption prototype, from the upload form to access checks.

Languages
Python & JavaScript
Framework
Flask & Jinja
Database
SQLite
Scope
Educational prototype
EncryptronFile workflow
  1. Upload

    File + password

  2. Process

    Flask / Python

  3. Download

    Processed file

SQLiteAccounts, file records, permissions

File processing happens in memory.

What I built

I built a Flask app with account registration, encryption and decryption flows, and an interface for managing file permissions. Users select a file, enter a passphrase, and download the result with an .enc extension.

The frontend uses HTML, CSS, JavaScript, and Jinja templates. The backend handles sessions, compression, file transformations, and SQLite records. It is an educational prototype with a custom cipher.

The upload flow

I split the interface into three steps, with JavaScript controlling which fields and instructions are visible.

  1. Choose a file

    The selected filename replaces “Browse Files.” Reset clears the selection, and Next moves to the passphrase step.

  2. Enter a passphrase

    A password field collects the passphrase. Back returns to the file step; an empty passphrase prevents moving forward.

  3. Submit and download

    The final screen names the file being processed. On submission, the selected file and passphrase are copied into a multipart form and sent to Flask.

Read the frontend flow

Behind the form

  • Accounts and sessions

    Werkzeug hashes account passwords with PBKDF2-SHA256 and checks them at sign-in. Flask sessions carry the user’s ID, and protected routes redirect signed-out visitors to the login page.

  • Compression and processing

    The app reads the upload into memory, compresses it with zipfile, and applies the custom transformation. The decryption flow reverses the transformation and decompresses the result.

  • Passphrase and cipher

    The file passphrase is hashed with SHA-256, and the first 16 bytes become key material for a custom ten-round, Feistel-style transformation. It processes 16-byte blocks. This experimental routine is separate from the PBKDF2 hashing used for account passwords.

  • Three database tables

    users holds accounts and password hashes. files records the filename, owner, and timestamp. permissions connects file IDs to users with access; the owner gets a permission record when a file is processed.

Returning the file

The processing route wraps the result in BytesIO and returns it with Flask’s send_file. The file contents stay in memory for this request; SQLite stores the associated records.

return send_file(
    io.BytesIO(encrypted_data),
    mimetype='application/octet-stream',
    as_attachment=True,
    download_name=encrypted_filename
)

From the encryption route in app.py.

Decryption and sharing

Before decryption, the app looks up the uploaded filename and checks whether the signed-in user owns the record or has a matching permission entry. A missing record or failed permission check stops the request before processing.

The Manage Access page lists the owner’s files and the usernames with access. Its forms can grant access, revoke a permission, or remove a file’s database record. Granting access does not send the file itself.

Memory and storage

In-memory processing keeps this workflow straightforward, but the file size directly affects memory usage. A larger deployment would need explicit limits and a different strategy for large files.

SQLite makes local setup simple. Moving to multiple application instances would require revisiting database access and deployment choices.

What I’d carry forward

Building the full flow made the different responsibilities clearer: form validation, file processing, account authentication, and file permissions each need their own checks.

For the next backend project, I’d define access rules and failure cases earlier, and keep file-processing logic separate from route handlers so it can be tested independently.

What I’d change next

The custom cipher is experimental, so this version is not intended to protect sensitive files. I’d replace it with vetted authenticated encryption before considering production use.

I’d also make ownership checks consistent across every permission-changing route, use an unambiguous file identifier instead of relying on filename lookup, and add upload limits. Tests should cover missing files, invalid passphrases, duplicate filenames, and unauthorized permission changes.

On the frontend, I’d replace browser alerts with inline validation and simplify the form submission so the file input does not need to be copied.

Read the implementation