#!/bin/sh -efu
set -o pipefail
# Copyright 2018-2019 Jakub Jirutka <jakub@jirutka.cz>. All rights reserved.
# Licensed under the terms of the MIT license. See <https://opensource.org/licenses/MIT>.
#
#---help---
# Usage:
#   gpg-keygen [options] SECKEY [PUBKEY]
#   gpg-keygen [-h]
#
# Generate a new GPG key pair in a temporary GPG Home and export it to a file.
#
# Arguments:
#   SECKEY                   Where to write the generated secret key. Use "-" for stdout.
#   PUBKEY                   Where to write the generated public key. Use "-" for stdout.
#
# Options:
#   -n --name NAME            User name's real name. Defaults to a random string.
#
#   -m --name-email EMAIL     User name's email address. Defaults to none.
#
#   -c --name-comment STRING  User name's comment. Defaults to none.
#
#   -e --expire-date EXPIRE   When is the key going to expire. Defaults to 0 (no expire).
#
#   -t --key-type ALGO        Type of the primary key and subkey. The algorithm must be capable of
#                             signing. Defaults to RSA.
#
#   -b --key-length NBITS     The requested length of the generated key in bits. Defaults to 3072.
#
#   -u --key-usage USAGE      Space or comma delimited list of key usages. Allowed values are
#                             "encrypt", "sign", and "auth". Defaults to "encrypt,sign,auth".
#
#   -p --passphrase PASS      Passphrase to protect the key. If not provided and environment
#                             variable GPG_PASSPHRASE is unset, you'll be prompted to enter the
#                             passphrase. If empty, the key won't be protected with a passphrase.
#
#   -v --verbose              Be verbose.
#
#   -V --version              Print script's version and exit.
#
#   -h --help                 Show this message and exit.
#
# Environment variables:
#   GPG                       GPG command to run. Defaults to "gpg2".
#   GPG_PASSPHRASE            Passphrase to protect the key.
#
# Script's homepage: <https://gist.github.com/jirutka/8dc567ed4d7b4585111996242aa573a8>.
#
#---help---

PROGNAME='gpg-keygen'
VERSION='0.1.0'

# Prompts user for a password in POSIX compatible way.
ask_pass() {
	local prompt="$1"
	local pass

	printf '%s: ' "$prompt" >&2
	stty -echo
	read -r pass
	stty echo
	printf '\n' >&2

	test -n "$pass" || return 1
	printf '%s\n' "$pass"
}

outfile_arg() {
	local arg="$1"

	if [ "$arg" != '-' ]; then
		mkdir -p "$(dirname "$arg")"
		printf -- '--output %s\n' "$arg"
	fi
}

die() {
	printf '%s: %s\n' "$PROGNAME" "$@" >&2
	exit 1
}

print_help() {
	sed -En '/^#---help---/,/^#---help---/p' "$0" | sed -E 's/^# ?//; 1d;$d;'
}


#=============================  M a i n  ==============================#

: ${GPG:=gpg2}

expire_date='0'
key_length='3072'
key_type='RSA'
key_usage='encrypt,sign,auth'
name_comment=''
name_email=''
name_real=''
verbosity='--quiet'

while [ $# -gt 0 ]; do
	n=2
	case "$1" in
		-e | --expire-date) expire_date="$2";;
		-b | --key-length) key_length="$2";;
		-t | --key-type) key_type="$2";;
		-u | --key-usage) key_usage="$2";;
		-c | --name-comment) name_comment="$2";;
		-m | --name-email) name_email="$2";;
		-n | --name-real) name_real="$2";;
		-p | --passphrase) passphrase="$2";;
		-v | --verbose) verbosity='--verbose'; n=1;;
		-V | --version) echo "$PROGNAME $VERSION"; exit 0;;
		-h | --help) print_help; exit 0;;
		- | --) break;;
		-* | --*) die "unknown option: $1";;
		*) break;;
	esac
	shift $n
done

if [ $# -lt 1 ]; then
	print_help >&2
	exit 1
fi

privkey_out="$1"
pubkey_out="${2:-}"

: ${name_real:="$(cat /dev/urandom | LC_CTYPE=C tr -cd 'A-Za-z0-9' | head -c 8)"}

passphrase="${passphrase-${GPG_PASSPHRASE-$(ask_pass 'Enter passphrase')}}"


temp_dir=$(mktemp -d)
trap "rm -Rf '$temp_dir'" EXIT HUP INT TERM

export GNUPGHOME="$temp_dir"

cat > "$temp_dir/.input" <<-EOF
	%echo Generating a basic GPG key
	${passphrase:-%no-protection}
	Key-Type: $key_type
	Key-Length: $key_length
	Key-Usage: $key_usage
	Name-Real: $name_real
	${name_email:+"Name-Email: $name_email"}
	${name_comment:+"Name-Comment: $name_comment"}
	Expire-Date: $expire_date
	${passphrase:+Passphrase: $passphrase}
	%commit
EOF

# Generate key.
$GPG $verbosity --batch --no-tty --gen-key "$temp_dir/.input"

# Find key's ID.
id=$($GPG --no-tty --list-secret-keys --with-colons 2>/dev/null \
	| awk -F: '/^sec:/ { print $5 }')
echo "$id"

# List created keys.
if [ "$verbosity" = '--verbose' ]; then
	$GPG --no-tty --list-keys >&2
fi

# Export public key.
if [ -n "$pubkey_out" ]; then
	$GPG $verbosity $(outfile_arg "$pubkey_out") \
		--batch --yes --no-tty --armor --export "$id"
fi

# Export secret key.
umask 0077
echo "${passphrase-}" | $GPG $verbosity $(outfile_arg "$privkey_out") \
	--batch --yes --no-tty --pinentry-mode loopback --passphrase-fd 0 \
	--armor --export-secret-keys "$id"
