A terminal-based secure drop vault for storing and retrieving encrypted notes. Built in Go using the Bubble Tea framework, this application implements strict cryptographic standards including AES-256-CBC encryption, PKCS#7 padding, secure salt/IV generation, and data integrity verification.
- Go (Version 1.24.4) - Download Go
1. Clone the repository and Navigate to the project directory
Open your terminal, clone this repository, and navigate to it:
git clone https://github.com/abidmuin/secure-drop-vault.git && cd secure-drop-vault2. Download dependencies
This project relies on the charmbracelet/bubbletea and lipgloss libraries for the terminal UI. Download them by running:
go mod tidy3. Run the application
Run the application directly using the Go toolchain:
go run .Compile the project into a standalone executable file:
go build -o secure-drop-vault
./secure-drop-vault- Navigation: Use the
Up/Downarrow keys (orj/k) to navigate menus. - Selection: Press
Enterto select a menu option or submit a form. - Typing: Type normally in the input fields. Passwords will be hidden.
- Cancel/Back: Press
Escto cancel a form and return to the main menu. - Quit: Press
qorCtrl+Con the main menu to exit the application.
The application is built in Go using the Bubble Tea framework, which implements the Elm Architecture (Model-View-Update).
- Model: Holds the application state (current screen, cursor position, form inputs, loaded vault data).
- View: Renders the TUI (Terminal User Interface) based on the current state using
lipglossfor styling. - Update: An event loop that listens for I/O (keystrokes) and transitions the state machine.
The application operates on a strict finite state machine to prevent users from accessing forms out of order.
stateDiagram-v2
[*] --> screenMenu
screenMenu --> screenRegister : Select "Register"
screenMenu --> screenStore : Select "Store"
screenMenu --> screenRead : Select "Read"
screenRegister --> screenResult : Submit Form
screenStore --> screenResult : Submit Form
screenRead --> screenResult : Submit Form
screenMenu --> screenResult : Select "Tamper" or "Delete"
screenResult --> screenMenu : Press 'Enter'
screenMenu --> [*] : Press 'q'
All persistent data is stored locally in vault.json file. The application supports a single-user environment. Passwords are never stored on disk.
| Field | Type | Description |
|---|---|---|
username |
String | The registered user's identifier. |
salt |
String (Base64) | A 16-byte cryptographically secure random value. |
hash_mode |
String | The selected hashing algorithm (SHA256 or MD5). |
iv |
String (Base64) | The 16-byte Initialization Vector for AES. |
ciphertext |
String (Base64) | The encrypted note. |
integrity_hash |
String (Hex) | Hash of the IV and Ciphertext for tamper detection. |
When a user registers, the system establishes their cryptographic baseline. Passwords are never stored on disk.
- User inputs
Username,Password, andHashMode. - System calls
crypto/randto generate a 16-byte secure random Salt. Username,Salt, andHashModeare written tovault.json.
When a user stores a note, the system derives an AES key and encrypts the payload.
Step-by-Step:
- Derive Key: The system concatenates the Salt and Password, then hashes them based on the
HashMode. (If MD5 is used, the 16-byte hash is duplicated to fulfill the 32-byte requirement for AES-256). - Padding: The plaintext note is padded to 16-byte blocks using
PKCS#7. - Encryption: A new 16-byte IV is generated. The padded note is encrypted using AES-256 in Cipher Block Chaining (CBC) mode.
- Integrity Hash: The IV and Ciphertext are concatenated and hashed to create the
IntegrityHash. - The
iv,ciphertext, andintegrity_hashare saved to disk.
flowchart TD
subgraph Key Derivation
P[User Password] --> KDF
S[Stored Salt] --> KDF
KDF["Hash: Salt || Password"] --> KEY["32-Byte AES Key"]
end
subgraph Encryption
N[Plaintext Note] --> PAD["PKCS#7 Padding"]
PAD --> AES
IV["Generate 16-Byte Random IV"] --> AES
KEY --> AES["AES-256-CBC Encrypter"]
AES --> C[Ciphertext]
end
subgraph Integrity
IV --> HASH["Hash: IV || Ciphertext"]
C --> HASH
HASH --> IH[Integrity Hash]
end
C -.-> V[(vault.json)]
IV -.-> V
IH -.-> V
Reading a note prioritizes verification before decryption to prevent Padding Oracle Attacks and to expose tampering.
Step-by-Step:
- Integrity Check: The system reads the stored IV and Ciphertext, concatenates them, and re-computes the hash.
- Compare: If the computed hash does not perfectly match the stored IntegrityHash, the system halts and alerts the user of data tampering.
- Derive Key: If integrity passes, the system derives the AES key using the inputted password and stored salt.
- Decrypt: AES-256-CBC decrypts the ciphertext using the key and the stored IV.
- Unpad: PKCS#7 padding is validated and stripped off.
- Display: The original plaintext is rendered to the screen.
flowchart TD
subgraph Data Retrieval
V[(vault.json)] --> IV[Stored IV]
V --> C[Stored Ciphertext]
V --> SH[Stored Integrity Hash]
end
subgraph Integrity Check
IV --> CH["Compute Hash: IV || Ciphertext"]
C --> CH
CH --> CMP{"Calculated == Stored?"}
SH --> CMP
end
CMP -- No --> ERR["Abort: Show Tampering Alert"]
subgraph Decryption
CMP -- Yes --> KDF
P[User Password] --> KDF
S[Stored Salt] --> KDF
KDF[Derive Key] --> KEY["32-Byte AES Key"]
KEY --> AES["AES-256-CBC Decrypter"]
IV --> AES
C --> AES
AES --> UNPAD["PKCS#7 Unpad"]
UNPAD --> N[Display Plaintext Note]
end
To verify the integrity check, this function forcefully corrupts the ciphertext in vault.json.
- Loads
vault.json. - Reads the
ciphertextstring. - Flips the first byte (e.g., if
'A', change to'B'). - Saves the modified JSON, causing all subsequent "Read Note" attempts to fail the Integrity Check.
This function acts as a total system wipe.
- Calls
os.Remove("vault.json"). - If successful, all user data, salts, IVs, and ciphertexts are permanently destroyed.
This project is licensed under the MIT License - see the LICENSE file for details.