├── Makefile ├── license ├── pw └── readme.md /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr 2 | 3 | install: 4 | @install -Dm755 pw $(DESTDIR)$(PREFIX)/bin/pw 5 | 6 | uninstall: 7 | @rm -f $(DESTDIR)$(PREFIX)/bin/pw 8 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) Anirudh Oppiliappan 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | 13 | -------------------------------------------------------------------------------- /pw: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # pw - a mnml password manager 3 | 4 | command -v gpg >/dev/null 2>&1 && gpg=gpg 5 | command -v gpg2 >/dev/null 2>&1 && gpg=gpg2 6 | 7 | # check if xclip or pbcopy exist 8 | # command -v xclip >/dev/null 2>&1 && copy="xclip -rmlastnl -selection clipboard" 9 | # command -v pbcopy >/dev/null 2>&1 && copy="pbcopy | tr -d '\n'" 10 | 11 | # export PW_DIR to your own path 12 | [[ -z "$PW_DIR" ]] && PW_DIR="$HOME/.pw" 13 | 14 | init() { 15 | if [[ ! -e "$PW_DIR" ]]; then 16 | mkdir -p "$PW_DIR" 17 | printf "pw: password directory initialized at %s\n" "$PW_DIR" 18 | else 19 | printf "PW_DIR is %s\n" "$PW_DIR" 20 | die "$PW_DIR exists" 21 | fi 22 | } 23 | 24 | add() { 25 | # $1: path to file 26 | # $2 [optional]: password text 27 | [[ -z "$PW_KEY" ]] && die "\$PW_KEY not set" 28 | 29 | if [[ "$#" -eq 2 ]]; then 30 | pass="$2" 31 | else 32 | # uses default length of 25 chars, unless PW_LEN is set 33 | pass="$(pwgen "${PW_LEN:-25}" 1 -s)" 34 | printf "pw: generated password for %s\n" "$1" 35 | fi 36 | if [[ ! -f "$PW_DIR/$1.gpg" ]]; then 37 | printf "%s" "$pass" | "$gpg "-er "$PW_KEY" -o "$PW_DIR/$1.gpg" 38 | printf "pw: %s/%s.gpg created\n" "$PW_DIR" "$1" 39 | else 40 | die "the file %s/%s.gpg exists" "$PW_DIR" "$1" 41 | fi 42 | } 43 | 44 | list() { 45 | for f in "$PW_DIR"/*.gpg; do 46 | printf '%s\n' "$(basename "${f%.gpg}")" 47 | done 48 | } 49 | 50 | del() { 51 | checkf "$PW_DIR/$1.gpg" 52 | read -rn 1 -p "pw: are you sure you want to delete $1? [y/n]: " 53 | printf "\n" 54 | [[ "$REPLY" == [yY] ]] && { 55 | rm -f "$PW_DIR/$1.gpg" 56 | printf "pw: deleted %s" "$1" 57 | } 58 | } 59 | 60 | show() { 61 | checkf "$PW_DIR/$1.gpg" 62 | "$gpg" --decrypt --quiet --use-agent "$PW_DIR/$1.gpg" 63 | } 64 | 65 | # TODO: rework having to checkf twice 66 | 67 | copy() { 68 | checkf "$PW_DIR/$1.gpg" 69 | if [[ "$OSTYPE" =~ darwin* ]]; then 70 | show "$1" | head -1 | pbcopy | tr -d '\n' 71 | else 72 | show "$1" | head -1 | xclip -rmlastnl -selection clipboard 73 | fi 74 | printf "pw: copied %s to clipboard\n" "$1" 75 | } 76 | 77 | usage() { 78 | usage=" 79 | pw - mnml password manager 80 | 81 | usage: pw [options] [NAME] 82 | All options except -i and -h require a NAME argument. 83 | 84 | options: 85 | -i Initializes password directory at \$HOME/.pw or at \$PW_DIR, if it exists. 86 | -a Add a password. 87 | -g Generate a password. 88 | -s Print password to STDOUT. 89 | -l List out all passwords. 90 | -c Copy existing password to clipboard. 91 | -d Delete password. 92 | -h Display this help message and exit. 93 | 94 | Requires PW_KEY to be set. Optionally, set PW_DIR for custom directory location. 95 | Set PW_LEN to an integer of your choice, to override the default password length of 25. 96 | " 97 | 98 | printf "%s" "$usage" 99 | exit 1 100 | } 101 | 102 | checkf() { 103 | [[ ! -f "$1" ]] && 104 | die "$1 does not exist" 105 | } 106 | 107 | die() { 108 | printf "error: %s\n" "$1" >&2 109 | exit 1 110 | } 111 | 112 | 113 | main() { 114 | [[ -z "$1" ]] && { 115 | usage 116 | } 117 | 118 | while getopts "ila:g:s:c:d:h" options 119 | do 120 | # shellcheck disable=SC2221,SC2222 121 | case "$options" in 122 | i) init ;; 123 | l) list ;; 124 | g) add "$OPTARG" ;; 125 | a) 126 | read -rsp "enter password: " pass 127 | printf "\n" 128 | add "$OPTARG" "$pass" 129 | ;; 130 | s) show "$OPTARG" ;; 131 | c) copy "$OPTARG" ;; 132 | d) del "$OPTARG" ;; 133 | *|h) usage ;; 134 | esac 135 | done 136 | 137 | shift $(( OPTIND -1 )) 138 | } 139 | 140 | main "$@" 141 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # pw 2 | > a mnml password manager 3 | 4 | ## Dependencies 5 | 6 | - `bash` 7 | - `gpg2` (will add `gpg` support later) 8 | - `xclip` for clipboard support 9 | 10 | ## Usage 11 | 12 | Requires `PW_KEY` to be set. Do so by appending this to your `.bashrc` 13 | / `.zshrc`: 14 | 15 | ```shell 16 | export PW_KEY=user@mail.id 17 | ``` 18 | This is your GPG key's associated email ID. 19 | 20 | Optionally, you may set `PW_DIR` to your desired path to change the 21 | default password store location, which is `~/.pw`. To override the 22 | default password length of 25, set `PW_LEN` to a number of your choice. 23 | 24 | ``` 25 | pw - mnml password manager 26 | 27 | usage: pw [options] [NAME] 28 | All options except -i and -h require a NAME argument. 29 | 30 | options: 31 | -i Initializes password directory at $HOME/.pw or at $PW_DIR, if it exists. 32 | -a Add a password. 33 | -g Generate a password. 34 | -s Print password to STDOUT. 35 | -l List out all passwords. 36 | -c Copy existing password to clipboard. 37 | -d Delete password. 38 | -h Display this help message and exit. 39 | ``` 40 | 41 | ## Examples 42 | 43 | ```shell 44 | $ PW_DIR=~/passwords pw -i 45 | pw: password directory initialized at /home/icy/passwords 46 | 47 | $ pw -a twitter 48 | enter password: 49 | pw: /home/icy/.pw/twitter.gpg created 50 | 51 | $ pw -c twitter 52 | pw: copied twitter to clipboard 53 | 54 | $ pw -s twitter 55 | notmyrealpassword 56 | 57 | $ pw -g github 58 | pw: generated password for github 59 | pw: /home/icy/.pw/github.gpg created 60 | 61 | $ pw -l 62 | fake 63 | github 64 | more 65 | passwords 66 | some 67 | twitter 68 | ``` 69 | 70 | ## Notes 71 | 72 | - Uses `pwgen -s` to generate passwords. 73 | - Lacks directory support because I don't need it. 74 | - Why not `pass(1)`? 700 lines vs 100 lines. 75 | --------------------------------------------------------------------------------