add subcmds/new/password
This commit is contained in:
parent
4daab330cb
commit
97c9dfb986
2 changed files with 261 additions and 0 deletions
|
|
@ -23,6 +23,7 @@ ${BOLD}${UNDERLINE}${RED}Options${RESET}
|
|||
|
||||
${BOLD}${UNDERLINE}${RED}Subcommands${RESET}
|
||||
${BOLD}${CYAN}cache-key${RESET} Generate a new binary-cache signing keypair
|
||||
${BOLD}${CYAN}password${RESET} Generate a new hashed Unix user password
|
||||
${BOLD}${CYAN}ssh-key${RESET} Generate a new SSH keypair
|
||||
${BOLD}${CYAN}wg-key${RESET} Generate a new Wireguard keypair"
|
||||
|
||||
|
|
|
|||
260
ceru/subcmds/new/password
Executable file
260
ceru/subcmds/new/password
Executable file
|
|
@ -0,0 +1,260 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2026 Emile Clark-Boman
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
USAGE="${BOLD}${UNDERLINE}${RED}Usage${RESET}
|
||||
${BOLD}${GREEN}$THIS new password [option...]${RESET}
|
||||
|
||||
${BOLD}${UNDERLINE}${RED}Description${RESET}
|
||||
Generates a new password hash in libxcrypt format with secure defaults.
|
||||
For more advanced usage run the ${BOLD}${MAGENTA}\`mkpasswd\`${RESET} utility directly.
|
||||
|
||||
${BOLD}${UNDERLINE}${RED}Options${RESET}
|
||||
${BOLD}${MAGENTA}-h, --help${RESET} Show this message (^_^)
|
||||
${BOLD}${MAGENTA}-o, --out${RESET} Private key file name to write to (the public key is named identically but ends with ${BOLD}${CYAN}.pub${RESET})
|
||||
${BOLD}${MAGENTA}-j, --json${RESET} Output in JSON format
|
||||
${BOLD}${MAGENTA}-i, --stdin${RESET} Read the password to hash from stdin ${BOLD}$CYAN(single line only)${RESET}
|
||||
${BOLD}${MAGENTA}-t, --type${RESET} The hash algorithm to use: ${BOLD}${MAGENTA}yescrypt, scrypt, bcrypt ${CYAN}(default: yescrypt)${RESET}
|
||||
${BOLD}${MAGENTA}-r, --rounds${RESET} The number of key derivation function rounds to apply ${BOLD}${MAGENTA}(format: ^[0-9]+\$) ${CYAN}(defaults: yescrypt=11, scrypt=10, bcrypt=14)${RESET}
|
||||
${BOLD}${MAGENTA}-s, --salt${RESET} Specify the hash's salt directly ${BOLD}${RED}(not recommended)${RESET} ${BOLD}${CYAN}(default: libxcrypt secure default)${RESET}"
|
||||
|
||||
# ==== Argument Values ====
|
||||
TYPE='yescrypt'
|
||||
ROUNDS=''
|
||||
SALTED=false
|
||||
OUT=''
|
||||
JSON=false
|
||||
STDIN=false
|
||||
EXTRA=''
|
||||
# ==== Argument Values ====
|
||||
|
||||
# parse all args
|
||||
while [[ $# -gt 0 ]]; do
|
||||
ARG="$1"
|
||||
case "$ARG" in
|
||||
-h|--help)
|
||||
throw-usage 0
|
||||
;;
|
||||
-o|--out)
|
||||
shift
|
||||
OUT="$1"; shift
|
||||
;;
|
||||
-j|--json)
|
||||
shift
|
||||
JSON=true
|
||||
;;
|
||||
-i|--stdin)
|
||||
shift
|
||||
STDIN=true
|
||||
;;
|
||||
-t|--type)
|
||||
shift
|
||||
TYPE="$1"; shift
|
||||
;;
|
||||
-r|--rounds)
|
||||
shift
|
||||
ROUNDS="$1"; shift
|
||||
;;
|
||||
-s|--salt)
|
||||
shift
|
||||
if [[ "$SALTED" == false ]]; then
|
||||
SALTED=true
|
||||
EXTRA="$EXTRA --salt=\'$1\'"
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
throw-badflag 1 "$ARG"
|
||||
;;
|
||||
*)
|
||||
throw-badarg 1 "$ARG"
|
||||
;;
|
||||
esac
|
||||
done; unset -v ARG
|
||||
|
||||
# NOTE: Available password hashing methods for /etc/passwd & /etc/shadow
|
||||
# NOTE: Read the manual pages via `man 3 crypt` and `man 5 crypt` (if available)
|
||||
# NOTE: Available online via https://man.archlinux.org/man/crypt.5
|
||||
# WARNING: Due to modern developments in cryptography most of these methods
|
||||
# WARNING: are no longer recommended however some distrobutions still use them.
|
||||
# WARNING: Cerulean intentionally restricts access to only secure algorithms.
|
||||
# $ mkpasswd -m help
|
||||
## Available methods:
|
||||
## yescrypt Yescrypt
|
||||
## gost-yescrypt GOST Yescrypt
|
||||
## scrypt scrypt
|
||||
## bcrypt bcrypt
|
||||
## bcrypt-a bcrypt (obsolete $2a$ version)
|
||||
## sha512crypt SHA-512
|
||||
## sha256crypt SHA-256
|
||||
## sunmd5 SunMD5
|
||||
## md5crypt MD5
|
||||
## bsdicrypt BSDI extended DES-based crypt(3)
|
||||
## descrypt standard 56 bit DES-based crypt(3)
|
||||
## nt NT-Hash
|
||||
function perr-unsupportedhash {
|
||||
local ALGO="$1"
|
||||
echo -e "${BOLD}${CYAN}$THIS${RED} does not support the ${MAGENTA}$ALGO${RED} hashing algorithm${RESET}" >&2
|
||||
}
|
||||
function perr-forbiddenhash {
|
||||
local ALGO="$1"
|
||||
echo -e "${BOLD}${CYAN}Cerulean${RED} intentionally forbids ${MAGENTA}$ALGO-based${RED} hashes.${RESET}" >&2
|
||||
}
|
||||
function perr-recommendhash {
|
||||
local ALGO="$1"
|
||||
echo -e "${BOLD}${CYAN}Cerulean${WHITE} recommends the ${MAGENTA}$ALGO${WHITE} algorithm ${GREEN}(embrace modernity loser)${RESET}" >&2
|
||||
}
|
||||
|
||||
# ensure $TYPE is a valid hash algorithm
|
||||
case "$TYPE" in
|
||||
# ========= PERMITTED HASH ALGORITHMS =========
|
||||
yescrypt)
|
||||
if [[ -z "$ROUNDS" ]]; then
|
||||
ROUNDS='11'
|
||||
fi
|
||||
;;
|
||||
scrypt)
|
||||
if [[ -z "$ROUNDS" ]]; then
|
||||
ROUNDS='10'
|
||||
fi
|
||||
;;
|
||||
bcrypt)
|
||||
if [[ -z "$ROUNDS" ]]; then
|
||||
ROUNDS='14'
|
||||
fi
|
||||
;;
|
||||
|
||||
# ========= FORBIDDEN HASH ALGORITHMS =========
|
||||
gost-yescrypt)
|
||||
perr-unsupportedhash "$TYPE"
|
||||
perr-recommendhash 'yescrypt'
|
||||
echo -e "┏
|
||||
┃ Dear Comrade,
|
||||
┃ It is with a heavy heart I must inform you that \"GOST Algorithms
|
||||
┃ Considered Harmful\" - Edsger Wybe Dijkstra (probably). Alas
|
||||
┃ GOST is considered broken... It is no longer 1970, please grow up :(
|
||||
┃ Слава Родине! - Glory to the Motherland!
|
||||
┗" >&2
|
||||
exit 1
|
||||
;;
|
||||
bcrypt-a)
|
||||
perr-unsupportedhash "$TYPE"
|
||||
perr-recommendhash 'bcrypt'
|
||||
echo -e "┏
|
||||
┃ The alternative prefix \"\$2y$\" is equivalent to \"\$2b$\".
|
||||
┃ It exists for historical reasons only. The alternative prefixes
|
||||
┃ \"\$2a$\" and \"\$2x$\" provide bug-compatibility with
|
||||
┃ crypt_blowfish 1.0.4 and earlier, which incorrectly processed
|
||||
┃ characters with the 8th bit set.
|
||||
┗" >&2
|
||||
exit 1
|
||||
;;
|
||||
sha512crypt|sha256crypt)
|
||||
perr-unsupportedhash "$TYPE"
|
||||
perr-forbiddenhash 'SHA'
|
||||
echo -e "┏
|
||||
┃ SHA-based hashes are considered outdated and generally insecure
|
||||
┃ due to their vulnerabilit to brute-force and collision attacks.
|
||||
┃ Modern algorithms such as yescrypt, scrypt, and bcrypt are recommended.
|
||||
┗" >&2
|
||||
exit 1
|
||||
;;
|
||||
sunmd5|md5crypt)
|
||||
perr-unsupportedhash "$TYPE"
|
||||
perr-forbiddenhash 'MD5'
|
||||
echo -e "┏
|
||||
┃ Not as weak as the DES-based hashes, but MD5 is so cheap
|
||||
┃ on modern hardware that it should not be used for new hashes.
|
||||
┗" >&2
|
||||
exit 1
|
||||
;;
|
||||
bsdicrypt|descrypt)
|
||||
perr-unsupportedhash "$TYPE"
|
||||
perr-forbiddenhash 'DES'
|
||||
echo -e "┏
|
||||
┃ The DES block cipher is cheap on modern hardware. Because there are only
|
||||
┃ 4096 possible salts and 2**56 distinct passphrases, which it
|
||||
┃ truncates to 8 characters, it is feasible to discover any passphrase
|
||||
┃ hashed with this method. It should only be used if you absolutely have to
|
||||
┃ generate hashes that will work on an old operating system that supports nothing else.
|
||||
┗" >&2
|
||||
exit 1
|
||||
;;
|
||||
nt)
|
||||
perr-unsupportedhash "$TYPE"
|
||||
echo -e "${BOLD}Please ${RED}repent${WHITE} for your filthy sins ${RED}you disgusting human...${RESET}" >&2
|
||||
echo -e "┏
|
||||
┃ Available for cross-compatibility's sake on FreeBSD. Based on MD4.
|
||||
┃ Has no salt or tunable cost parameter. It is so weak that
|
||||
┃ almost any human-chosen passphrase hashed with this method is guessable.
|
||||
┃ It should only be used if you absolutely have to generate hashes that
|
||||
┃ will work on an old operating system that supports nothing else.
|
||||
┗" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
echo -e "${BOLD}${RED}Unrecognised hash algorithm ${MAGENTA}\"$TYPE\"${RESET}" >&2
|
||||
echo -e "${BOLD}${GREEN}Supported algorithms: ${MAGENTA}yescrypt, scrypt, bcrypt${RESET}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
unset -f perr-unsupportedhash perr-forbiddenhash perr-recommendhash
|
||||
|
||||
# ensure $ROUNDS is a valid numeric
|
||||
re='^[0-9]+$'
|
||||
if ! [[ "$ROUNDS" =~ ^[0-9]+$ ]] ; then
|
||||
throw-badval 1 "$ROUNDS" '-r|--rounds'
|
||||
fi
|
||||
unset -v re
|
||||
|
||||
# Acquire password from stdin
|
||||
if [[ "$STDIN" == true ]]; then
|
||||
read -s PASS
|
||||
else
|
||||
read -sp "$(echo -e "${BOLD}${GREEN}Password:${RESET} ")" PASS
|
||||
echo # \n
|
||||
read -sp "$(echo -e "${BOLD}${GREEN}Retype Password:${RESET} ")" PASS2
|
||||
echo # \n
|
||||
if [[ "$PASS" != "$PASS2" ]]; then
|
||||
echo -e "${BOLD}${RED}Sorry, passwords do not match${RESET}" >&2
|
||||
exit 1
|
||||
fi
|
||||
unset -v PASS2
|
||||
fi
|
||||
|
||||
# Compute hash of password
|
||||
RESULT=$(mkpasswd -sm "$TYPE" -R "$ROUNDS" $EXTRA <<<"$PASS")
|
||||
unset -v PASS
|
||||
# Format as JSON if necessary
|
||||
if [[ "$JSON" == true ]]; then
|
||||
RESULT="{
|
||||
\"type\": \"${TYPE}\",
|
||||
\"rounds\": ${ROUNDS},
|
||||
\"hash\": \"${RESULT}\"
|
||||
}"
|
||||
fi
|
||||
|
||||
# Display hash result
|
||||
if [[ -n "$OUT" ]]; then
|
||||
echo "$RESULT" > "$OUT"
|
||||
elif [[ "$JSON" == true ]]; then
|
||||
echo "$RESULT"
|
||||
else
|
||||
echo -e "${BOLD}${GREEN}Hash:${WHITE} $RESULT${RESET}"
|
||||
fi
|
||||
unset -v RESULT
|
||||
|
||||
unset -v TYPE ROUNDS SALTED OUT JSON STDIN EXTRA
|
||||
Loading…
Add table
Add a link
Reference in a new issue