├── .gitignore ├── README.md └── passman /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PassMan - Easy, Secure, Password Manager 2 | 3 | PassMan is a secure, and easy to use password manager that runs on the terminal! It's also written in [bash](https://www.gnu.org/software/bash/). 4 | 5 | ## How PassMan Works ... 6 | 7 | PassMan was created out of frustration. For years I've been using encrypted files to store passwords. It works for me. But, it gets painful. Over the years I got faster at it, but it's still a pain in the ass! I decided to finally create and share a script to make my life easier - and hopefully yours. You may ask why I didn't use a service such as [LastPass](https://www.lastpass.com/). The simple answer is ... I don't trust them. 8 | 9 | PassMan stores passwords in an encrypted file as csv and allows you to **add**, **retrieve** and **delete** passwords. When a password is generated, or retrieved, it's automatically copied into your clipboard for ease of use. Cool right? 10 | 11 | I decided to use `gpg` for encryption/decryption because it's very secure (assuming host machine is secure and user password is strong). You can pass the database around securely in emails, or on a USB stick, with no chance of an attacker ever seeing what's inside it. At least not in your lifetime. To learn more about `gpg` checkout the [handbook](https://www.gnupg.org/gph/en/manual.html). 12 | 13 | The password database is encrypted/decrypted using [public key cryptography](https://en.wikipedia.org/wiki/Public-key_cryptography). PassMan can generate the public/private keys for you, or you can pass in your own. 14 | 15 | ## Dependencies 16 | 17 | The following dependencies must be installed for PassMan to work. These can be installed using the package manager on your OS such as `brew`, `apt`, `yum` etc. 18 | 19 | * bash 20 | * gpg 21 | * pwgen 22 | * pbcopy 23 | 24 | ## Installation 25 | 26 | First clone the repo: 27 | 28 | git clone http://github.com/JamesTheHacker/passman 29 | 30 | Move the repo to a directory in your `$PATH`. I suggest using a directory like `~/bin` under your home directory: 31 | 32 | mkdir ~/bin 33 | export PATH=$PATH:$HOME/bin 34 | mv passman/passman ~/bin 35 | chmod -R 700 ~/bin/passman 36 | 37 | Now you can run PassMan in the terminal. To test, display `usage`: 38 | 39 | passman -h 40 | 41 | ## Initial First Run 42 | 43 | If you haven't got a public/private key PassMan can generate one for you using `gpg --full-generate-key`. It can also generate a new encrypted password database. 44 | 45 | To create the public/private key, and password database, lets set up a new password: 46 | 47 | passman -k gmail 48 | 49 | **Note:** The `-k` option is used to pass a key that is used to identify a password. A key you will remember. It's helpful to name them after the website or application the password is used for. For example: `facebook`, `gmail`, `twitter`, `4chan`, `reddit` ... you get the idea. 50 | 51 | Enter `y` and follow the on screen instructions to generate a new public/private key and password database. Remember ... **use a strong password!** 52 | 53 | By default PassMan will store the password database, and public key, as files in the users home directory: 54 | 55 | * Public key: `~/.passman_pk` 56 | * Encrypted password database: `~/.passman_db` 57 | 58 | Once complete a newly generated password will now be stored in the password database. If you press `CTRL+V` you will see the password. Magic. 59 | 60 | Continue reading to learn learn how to `retrieve`, and `delete`, passwords. 61 | 62 | ## Usage 63 | 64 | **Create New Password** 65 | 66 | passman -k facebook 67 | 68 | A password is automatically generated and stored in the clipboard (press `CTRL+V` to use). If a password already exists for the specified key an error will be returned: 69 | 70 | Password with facebook already exists. 71 | 72 | By default the length of generated passwords is `85` characters. If you'd like to generate a longer password use the following: 73 | 74 | passman -k facebook -l 150 75 | 76 | The `-l` option allows you to specify a password length. 77 | 78 | **Add Existing Password** 79 | 80 | To store an existing password use the following command: 81 | 82 | passman -k facebook -p "mysecretpassword" -m add 83 | 84 | **Fetch A Stored Password** 85 | 86 | passman -k facebook -m get 87 | 88 | If password exists it is copied into the clipboard and can be used with `CTRL+V` 89 | 90 | **Delete A Password** 91 | 92 | passman -k facebook -m del 93 | 94 | If password exists it will be deleted from the password database 95 | 96 | **Update A Password** 97 | 98 | Updating a password is 2 commands. First delete the existing one, then generate a new password: 99 | 100 | passman -k facebook -m del 101 | passman -k facebook 102 | 103 | New password is now in the clipboard and can be used with `CTRL+V` 104 | 105 | **Custom Public Key & Password Database** 106 | 107 | You can specify your own public key, and password database, like so: 108 | 109 | passman -k facebook -d ~/path/to/password_db -f ~/path/to/public_key 110 | 111 | You must use the `-d` and `-f` option for every command when using a custom key and/or database. 112 | 113 | ## Security Tips 114 | 115 | PassMan is only as strong as the system using it. 116 | 117 | Here's some tips to help keep you safe: 118 | 119 | * **NEVER SHARE YOUR PRIVATE KEY!** 120 | * When generating a key ensure your password is very strong! The longer the better! 121 | * Backup your private and public keys onto physical media. If you're super paranoid print them off as QR codes and store them in a safe. 122 | * Make regular backups of password database/s in the event of system failure, nature disaster, etc. 123 | * Encrypt your swap space 124 | 125 | ## Issues 126 | 127 | The unencrypted password database is briefly stored in a `/tmp` using `mktemp`. It is possible under certain conditions for this password to end up in swap space. This isn't idea if your swap space is not encrypted. I am working on fixing this. 128 | 129 | ## Contributing 130 | 131 | If have any problems, or would like to suggest a feature, please file an issue. I welcome all pull requests. If you can improve PassMan in any way I will happily merge. 132 | 133 | ## Testing 134 | 135 | This script has only been tested on OSX 10.10.5. If you could test it on your own platform and report any issues I would be eternally grateful. 136 | 137 | ## A Word From The Author ... 138 | 139 | As I write this the UK Government are battling to ban encryption. Apparently to help fight terrorism and keep us safe. In 2017 our security and privacy are at risk. I urge everyone to exercise their right to privacy. Even if it's against the will of higher powers. I'm just your average Joe doing his part to help. Keep fighting the good war. 140 | 141 | If you'd like to contact me, add `@thunderkat` on [Wire](https://app.wire.com/). 142 | -------------------------------------------------------------------------------- /passman: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Author: James Jeffery 4 | # Email: jameslovescode@gmail.com 5 | # Date: 05/03/2017 6 | # Usage: passman -k KEY [-d PASS_DB] [-f PUBLIC_KEY] [-l PASS_LEN] [-p PASSWORD] [-m MODE] 7 | 8 | ########################## 9 | # VARIABLES 10 | ########################## 11 | 12 | # Users home directory. We store important files here. 13 | user_home=$(eval echo ~$USERNAME) 14 | 15 | # Default passman password file location 16 | pass_db="$user_home/.passman_db" 17 | 18 | # Sets the default public key file location 19 | pubkey="$user_home/.passman_pk" 20 | 21 | # Set the default password length 22 | pass_len=85 23 | 24 | # User defined password used for adding existing passwords to db 25 | pass= 26 | 27 | # Sets the default mode 28 | # Modes: 29 | # new: Creates a new password, saves to the password database, and copies to clipboard 30 | # get: Fetches a password by key from the password database 31 | # del: Deletes a password from the password database 32 | # add: Add an existing password to the database 33 | mode="new" 34 | 35 | # This key is used to identify what password to load 36 | key= 37 | 38 | ######################### 39 | # FUNCTIONS 40 | ######################### 41 | 42 | # Prints usage to standard out 43 | function usage { 44 | echo "passman -k KEY [-d PASS_DB] [-f PUBLIC_KEY] [-l PASS_LEN] [-p PASSWORD] [-m MODE]" 45 | echo " -k: password identifier key (required)" 46 | echo " -d: encrypted password database file (optional)" 47 | echo " -f: gpg public key (optional)" 48 | echo " -l: password length (optional)" 49 | echo " -p: user provided password (required for add mode)" 50 | echo " -m: mode: new (default), del, get, add" 51 | exit 52 | } 53 | 54 | # Gets a password by key from encrypted password database 55 | function get_password { 56 | local key=$1 57 | local pass_db=$2 58 | local pubkey=$3 59 | 60 | # Decode encrypted password file and store location 61 | local tmp_db=$(decrypt_pass_db $pass_db $pubkey) 62 | 63 | # Use awk to parse the CSV and find the password by key 64 | local pass=$(awk -F"^$key," '{printf $2}' $tmp_db) 65 | 66 | # If password is found print to stdout. This avoids a newline being printed if no match is found 67 | [ $pass ] && echo $pass 68 | 69 | # Cleanup and delete the tmp file 70 | rm $tmp_db 71 | } 72 | 73 | # Decodes the encrypted pass_db and stores it in a temporary file. 74 | # Calling code should delete the temp file when done. 75 | function decrypt_pass_db { 76 | local pass_db=$1 77 | local pubkey=$2 78 | 79 | # Create a temporary file to store decrypted pass_db 80 | local tmp_db=$(mktemp /tmp/tmp.XXXXXXXXXXXXXXXXXXXXX) 81 | 82 | # Set permissions 83 | chmod 600 $tmp_db 84 | 85 | # Decrypt pass_db 86 | gpg --quiet --recipient-file $pubkey --decrypt $pass_db > $tmp_db 87 | 88 | echo $tmp_db 89 | } 90 | 91 | # Saves password to encrypted password database 92 | function save_password { 93 | local key=$1 94 | local pass=$2 95 | local pass_db=$3 96 | local pubkey=$4 97 | 98 | # Create a temporary file 99 | local tmp_db=$(decrypt_pass_db $pass_db $pubkey) 100 | 101 | # Does the password already exist? 102 | if [ $(get_password $key $pass_db $pubkey) ] 103 | then 104 | echo "Password with $key already exists." 105 | rm $tmp_db 106 | exit 1 107 | fi 108 | 109 | # Append new password 110 | echo "$key,$pass" >> $tmp_db 111 | 112 | # Encrypt the pass_db 113 | gpg --yes --recipient-file $pubkey --output $pass_db --encrypt $tmp_db 114 | 115 | # Kill the temp password file with fire! 116 | rm $tmp_db 117 | } 118 | 119 | # Delete a password 120 | function delete_password { 121 | local key=$1 122 | local pass_db=$2 123 | local pubkey=$3 124 | 125 | # Decrypt password into temporary directory 126 | local tmp_db=$(decrypt_pass_db $pass_db $pubkey) 127 | 128 | # Encrypt the password file and save to pass_db 129 | sed "/^$key,/d" $tmp_db | gpg --yes --recipient-file $pubkey --output $pass_db --encrypt 130 | 131 | # Cleanup 132 | rm $tmp_db 133 | } 134 | 135 | ######################### 136 | # MAIN 137 | ######################### 138 | 139 | # Get option from the command line 140 | while getopts k:p:f:l:m: pm_opts 2>/dev/null 141 | do 142 | case $pm_opts in 143 | k) key=$OPTARG # Password key for fetching passwords 144 | ;; 145 | d) pass_db=$OPTARG # Encrypted password file 146 | ;; 147 | f) pubkey=$OPTARG # Public key file 148 | ;; 149 | l) pass_len=$OPTARG # Length of password to generate 150 | ;; 151 | m) mode=$OPTARG # Program mode 152 | ;; 153 | p) pass=$OPTARG # User supplied password 154 | ;; 155 | ?) usage 156 | esac 157 | done 158 | 159 | # Check if the key is set. This is required for both saving and fetching a password 160 | if [ -z "$key" ] 161 | then 162 | usage 163 | fi 164 | 165 | # Check if pubkey exists 166 | if [ ! -e $pubkey ] 167 | then 168 | echo "Public key $pubkey does not exist!" 169 | read -p "Do you want to create a new key in this location? (y/n): " yn 170 | 171 | # Ask the user if they want to create the pubkey 172 | if [ "$yn" == "y" ] 173 | then 174 | # Generate new gpg key and extract the fingerprint 175 | fingerprint=$(gpg --full-generate-key | grep -m 1 -o "[A-Z0-9]\{40\}") 176 | 177 | # Export the public key to file 178 | gpg --armor --export $fingerprint > $pubkey 179 | 180 | # Set key file permissions 181 | chmod 600 $pubkey 182 | else 183 | exit 184 | fi 185 | fi 186 | 187 | # If pass_db file does not exist, ask the user if they want to create one 188 | if [ ! -e $pass_db ] 189 | then 190 | echo "Encrypted password database $pass_db does not exist!" 191 | read -p "Do you want to create a new database in this location? (y/n): " yn 192 | 193 | if [ "$yn" == "y" ] 194 | then 195 | # Create a new encrypted password file 196 | echo "" | gpg --recipient-file $pubkey --output $pass_db --encrypt 197 | 198 | # Set pass_db permissions 199 | chmod 600 $pass_db 200 | else 201 | exit 202 | fi 203 | fi 204 | 205 | # Perform an action based on the mode 206 | case "$mode" in 207 | new) 208 | # Generate a random password of x length 209 | pass=$(pwgen -s -1 $pass_len) 210 | save_password $key $pass $pass_db $pubkey 211 | echo $pass | pbcopy 212 | ;; 213 | del) delete_password $key $pass_db $pubkey 214 | ;; 215 | get) get_password $key $pass_db $pubkey 216 | ;; 217 | add) save_password $key $pass $pass_db $pubkey 218 | ;; 219 | esac 220 | --------------------------------------------------------------------------------