backup bash cli
This commit is contained in:
parent
ff0d13034b
commit
2f49295004
7 changed files with 0 additions and 0 deletions
58
ceru.bak/ceru
Executable file
58
ceru.bak/ceru
Executable file
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2025-2026 _cry64 (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
|
||||
|
||||
# ======== CONFIGURATION ========
|
||||
CERU_USE_NIX3=true
|
||||
# ======== CONFIGURATION ========
|
||||
|
||||
# libceru env vars
|
||||
THIS=$(basename "$0")
|
||||
source libceru.sh
|
||||
|
||||
USAGE="${BOLD}${UNDERLINE}${RED}Usage${RESET}
|
||||
${BOLD}${GREEN}$THIS [option...] subcommand${RESET}
|
||||
|
||||
${BOLD}${UNDERLINE}${RED}Options${RESET}
|
||||
${BOLD}${MAGENTA}-h, --help${RESET} Show this message (^_^)
|
||||
|
||||
${BOLD}${UNDERLINE}${RED}Subcommands${RESET}
|
||||
${BOLD}${CYAN}deploy${RESET} Deploy your Cerulean network
|
||||
${BOLD}${CYAN}new${RESET} Init new instances of various components (see ${BOLD}${GREEN}\`$THIS new --help\`${RESET})"
|
||||
|
||||
# parse all args
|
||||
SUBCMD=false # whether a subcommand was specified
|
||||
while [[ $# -gt 0 ]]; do
|
||||
ARG=$1
|
||||
case $ARG in
|
||||
-h|--help)
|
||||
throw-usage 0 ;;
|
||||
-*)
|
||||
echo "[!] Unknown option \"$ARG\""
|
||||
exit 1 ;;
|
||||
*)
|
||||
SUBCMD=true
|
||||
break ;;
|
||||
esac
|
||||
done; unset -v ARG
|
||||
|
||||
# invalid usage occurs if no args or subcommand given
|
||||
if [[ $# = 0 || "$SUBCMD" = false ]]; then
|
||||
throw-usage 1
|
||||
fi; unset -v SUBCMD
|
||||
|
||||
# run provided subcommand
|
||||
run-subcmd "$@"
|
||||
131
ceru.bak/libceru.sh
Executable file
131
ceru.bak/libceru.sh
Executable file
|
|
@ -0,0 +1,131 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2025-2026 _cry64 (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.
|
||||
|
||||
# libceru relies on the script that sources
|
||||
# it to set the following environment variables:
|
||||
# CMD_NAME USAGE
|
||||
|
||||
set -u
|
||||
|
||||
# ======== INTERNAL STATE ========
|
||||
SUBCMDS="$PWD/subcmds" # XXX: TODO: don't hardcode as relative
|
||||
CMD_ABS="$SUBCMDS" # (initial value) CMD_ABS stores the current cmd's absolute path
|
||||
CMD_MAJ="$THIS" # (initial value) CMD_MAJ stores the current cmd's parent's name
|
||||
CMD_MIN="" # (initial value) CMD_MIN stores the current cmd's name
|
||||
# ======== INTERNAL STATE ========
|
||||
|
||||
# ANSI Coloring
|
||||
BLACK='\033[30m'
|
||||
RED='\033[31m'
|
||||
GREEN='\033[32m'
|
||||
YELLOW='\033[33m'
|
||||
BLUE='\033[34m'
|
||||
MAGENTA='\033[35m'
|
||||
CYAN='\033[36m'
|
||||
WHITE='\033[37m'
|
||||
DEFCOL='\033[39m' # default colour
|
||||
|
||||
# ANSI Styling
|
||||
RESET='\033[0m'
|
||||
BOLD='\033[1m'
|
||||
DIM='\033[2m'
|
||||
ITALIC='\033[3m'
|
||||
UNDERLINE='\033[4m'
|
||||
BLINKSLOW='\033[5m'
|
||||
BLINKFAST='\033[6m'
|
||||
REVERSE='\033[7m'
|
||||
INVISIBLE='\033[8m'
|
||||
|
||||
# Error Messages
|
||||
function perr { echo -e "${BOLD}${RED}error:${WHITE} $@\n${BOLD}${GREEN}[+]${WHITE} Try ${GREEN}'--help'${WHITE} for more information." >&2; }
|
||||
function perr-usage { echo -e "$USAGE\n" >&2; }
|
||||
function perr-badflag { perr "unrecognised flag ${BOLD}${MAGENTA}'$1'${RESET}"; }
|
||||
function perr-noflagval { perr "flag ${BOLD}${MAGENTA}'$1'${RESET} requires ${BOLD}${MAGENTA}${2}${RESET} argument(s), but only ${BOLD}${MAGENTA}${3}${RESET} were given"; }
|
||||
function perr-badarg { perr "unrecognised arg ${BOLD}${MAGENTA}'$1'${RESET}"; }
|
||||
function perr-noarg { perr "required argument ${BOLD}${MAGENTA}'$1'${RESET} is missing"; }
|
||||
function perr-badval { perr "value ${MAGENTA}\"$1\"${WHITE} is not valid for flag ${CYAN}$2${RESET}"; }
|
||||
# Failures
|
||||
function throw { echo -e "${@:2}" >&2; if [[ "$1" -ge 0 ]]; then exit "$1"; fi; }
|
||||
function throw-usage { throw "$1" "$(perr-usage 2>&1)\n"; }
|
||||
function throw-badflag { throw "$1" "$(perr-badflag "${@:2}" 2>&1)"; }
|
||||
function throw-noflagval { throw "$1" "$(perr-noflagval "${@:2}" 2>&1)"; }
|
||||
function throw-badarg { throw "$1" "$(perr-badarg "${@:2}" 2>&1)"; }
|
||||
function throw-noarg { throw "$1" "$(perr-noarg "${@:2}" 2>&1)"; }
|
||||
function throw-badval { throw "$1" "$(perr-badval "$2" "$3" 2>&1)"; }
|
||||
# Parsing/Validation
|
||||
function required { [[ -n "$1" ]] || throw-noarg 1 "${@:2}"; }
|
||||
|
||||
# Other
|
||||
function confirm-action {
|
||||
local CHAR
|
||||
while :; do
|
||||
echo -e "$1"
|
||||
read -n1 CHAR
|
||||
case $CHAR in
|
||||
[yY])
|
||||
return 0 ;;
|
||||
[nN])
|
||||
return 1 ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
function confirm { confirm-action ":: Proceed? [Y/n] "; }
|
||||
|
||||
function confirm-file-overwrite {
|
||||
local OVERWRITE=false
|
||||
local ARG
|
||||
for ARG in "$@"; do
|
||||
if [[ -f "$ARG" ]]; then
|
||||
# write info (initial) lines on first overwritable file found
|
||||
if [[ "$OVERWRITE" = false ]]; then
|
||||
echo -e "${BOLD}${UNDERLINE}${BLINKFAST}${RED}WARNING!${RESET} ${YELLOW}The following files will be overwritten:${RESET}"
|
||||
OVERWRITE=true
|
||||
fi
|
||||
# list all files that require overwriting
|
||||
echo -e "${BOLD} • ${GREEN}${ARG}${RESET}"
|
||||
fi
|
||||
done
|
||||
[[ "$OVERWRITE" = false ]] || confirm
|
||||
}
|
||||
|
||||
function isnumeric { [[ "$1" =~ ^[0-9]+$ ]]; }
|
||||
|
||||
# ====== Core ======
|
||||
function run-subcmd {
|
||||
# if CMD_MIN is empty, then CMD_MAJ is the root cmd (ie ceru)
|
||||
# and hence CMD_MAJ shouldn't yet be overridden
|
||||
if [[ ! "$CMD_MIN" = "" ]]; then
|
||||
CMD_MAJ="$CMD_MIN" # swapsies!
|
||||
fi
|
||||
# we shift here so $@ is passed correctly when we `source "$TARGET"`
|
||||
CMD_MIN="$1"; shift
|
||||
|
||||
# ensure the current command can take subcommands
|
||||
if [[ -f "$CMD_ABS" ]]; then
|
||||
# XXX: INTERNAL ERROR
|
||||
throw 2 "Subcommand \"$CMD_MIN\" cannot exist, as $CMD_MAJ has no subcommands!"
|
||||
fi
|
||||
|
||||
CMD_ABS="$CMD_ABS/$CMD_MIN"
|
||||
|
||||
# attempt to find the script corresponding to CMD_ABS
|
||||
TARGET="$CMD_ABS"
|
||||
if [[ -d "$CMD_ABS" ]]; then
|
||||
TARGET="$TARGET/default.sh"
|
||||
elif [[ ! -f "$CMD_ABS" ]]; then
|
||||
throw 1 "Command \"$CMD_MAJ\" does not provide subcommand \"$CMD_MIN\"!"
|
||||
fi
|
||||
source "$TARGET"
|
||||
}
|
||||
112
ceru.bak/subcmds/new/cache-key
Executable file
112
ceru.bak/subcmds/new/cache-key
Executable file
|
|
@ -0,0 +1,112 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2025-2026 _cry64 (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 cache-key [option...]${RESET}
|
||||
|
||||
${BOLD}${UNDERLINE}${RED}Options${RESET}
|
||||
${BOLD}${MAGENTA}-h, --help${RESET} Show this message (^_^)
|
||||
${BOLD}${MAGENTA}-f, --force${RESET} Ignores all warnings!!
|
||||
${BOLD}${MAGENTA}-j, --json${RESET} Output in JSON format
|
||||
${BOLD}${MAGENTA}-n, --name${RESET} Identifier of the key (e.g. ${BOLD}${MAGENTA}cache.example.org-1${RESET})
|
||||
${BOLD}${MAGENTA}-o, --out${RESET} Private key file name to write to (the public key is named identically but ends with ${BOLD}${MAGENTA}.pub${RESET})
|
||||
${BOLD}${MAGENTA}-P, --private-only${RESET} Only generate the private key, not the entire keypair"
|
||||
|
||||
# ==== Argument Values ====
|
||||
FORCE=false
|
||||
PRIV_ONLY=false
|
||||
JSON=false
|
||||
NAME=""
|
||||
SINK=""
|
||||
# ==== Argument Values ====
|
||||
|
||||
# parse all args
|
||||
while [[ $# -gt 0 ]]; do
|
||||
ARG="$1"
|
||||
case "$ARG" in
|
||||
-h|--help)
|
||||
throw-usage 0
|
||||
;;
|
||||
-n|--name)
|
||||
shift
|
||||
NAME="$1"; shift
|
||||
;;
|
||||
-P|--private-only)
|
||||
shift
|
||||
PRIV_ONLY=true
|
||||
;;
|
||||
-o|--out)
|
||||
shift
|
||||
SINK="$1"; shift
|
||||
;;
|
||||
-j|--json)
|
||||
shift
|
||||
JSON=true
|
||||
;;
|
||||
-f|--force)
|
||||
shift
|
||||
FORCE=true
|
||||
;;
|
||||
-*)
|
||||
throw-badflag 1 "$ARG"
|
||||
;;
|
||||
*)
|
||||
throw-badarg 1 "$ARG"
|
||||
;;
|
||||
esac
|
||||
done; unset -v ARG
|
||||
|
||||
# fail if NAME not provided
|
||||
required "$NAME" --name
|
||||
|
||||
|
||||
# generate our keypair
|
||||
PRIV_KEY=$(nix key generate-secret --key-name "$NAME")
|
||||
PUB_KEY=$(nix key convert-secret-to-public <<<"$PRIV_KEY")
|
||||
|
||||
# result defaults to unset (only stays unset if we intend on writing to a file)
|
||||
RESULT=""
|
||||
# JSON formatting
|
||||
if [[ "$JSON" = true ]]; then
|
||||
RESULT="{
|
||||
\"privateKey\": \"${PRIV_KEY}\"$(
|
||||
[[ "$PRIV_ONLY" = true ]] \
|
||||
|| echo ",\n \"publicKey\": \"${PUB_KEY}\"")
|
||||
}"
|
||||
if [[ -n "$SINK" ]]; then
|
||||
# confirm the user understands files will be overwritten
|
||||
[[ "$FORCE" = true ]] || confirm-file-overwrite "$SINK" || exit 0
|
||||
echo -e "$RESULT" > "$SINK"
|
||||
else
|
||||
echo -e "$RESULT"
|
||||
fi
|
||||
# standard formatting (stdout)
|
||||
elif [[ -z "$SINK" ]]; then
|
||||
echo -e "${BOLD}${UNDERLINE}${RED}Private Key${RESET}
|
||||
${BOLD}${GREEN}${PRIV_KEY}${RESET}$(
|
||||
[[ "$PRIV_ONLY" = true ]] \
|
||||
|| echo "\n${BOLD}${UNDERLINE}${RED}Public Key${RESET}\n ${BOLD}${GREEN}${PUB_KEY}${RESET}")"
|
||||
# standard formatting (files)
|
||||
else
|
||||
PRIV_SINK="$SINK"
|
||||
PUB_SINK="$SINK.pub"
|
||||
# confirm the user understands files will be overwritten
|
||||
[[ "$FORCE" = true ]] || confirm-file-overwrite "$PRIV_SINK" "$PUB_SINK" || exit 0
|
||||
echo "$PRIV_KEY" > "$PRIV_SINK"
|
||||
echo "$PUB_KEY" > "$PUB_SINK"
|
||||
fi;
|
||||
unset -v PRIV_SINK PUB_SINK PRIV_KEY PUB_KEY RESULT
|
||||
52
ceru.bak/subcmds/new/default.sh
Executable file
52
ceru.bak/subcmds/new/default.sh
Executable file
|
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2025-2026 _cry64 (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 [option...] subcommand${RESET}
|
||||
|
||||
${BOLD}${UNDERLINE}${RED}Options${RESET}
|
||||
${BOLD}${MAGENTA}-h, --help${RESET} Show this message (^_^)
|
||||
|
||||
${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"
|
||||
|
||||
# parse all args
|
||||
SUBCMD=false # where a subcommand was specified
|
||||
while [[ $# -gt 0 ]]; do
|
||||
ARG=$1
|
||||
case $ARG in
|
||||
-h|--help)
|
||||
throw-usage 0 ;;
|
||||
-*)
|
||||
echo "[!] Unknown option \"$ARG\""
|
||||
exit 1 ;;
|
||||
*)
|
||||
SUBCMD=true
|
||||
break ;;
|
||||
esac
|
||||
done; unset -v ARG
|
||||
|
||||
# invalid usage occurs if no args or subcommand given
|
||||
if [[ $# = 0 || "$SUBCMD" = false ]]; then
|
||||
throw-usage 1
|
||||
fi; unset -v SUBCMD
|
||||
|
||||
# run provided subcommand
|
||||
run-subcmd "$@"
|
||||
259
ceru.bak/subcmds/new/password
Executable file
259
ceru.bak/subcmds/new/password
Executable file
|
|
@ -0,0 +1,259 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2025-2026 _cry64 (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 KDF 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
|
||||
# NOTE: ${1,,} is $1 in lowercase
|
||||
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}${RED}The ${MAGENTA}$ALGO${RED} hash algorithm is unsupported!${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 "${BOLD}${YELLOW}┏
|
||||
┃ 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!
|
||||
┗${RESET}" >&2
|
||||
exit 1
|
||||
;;
|
||||
bcrypt-a)
|
||||
perr-unsupportedhash "$TYPE"
|
||||
perr-recommendhash 'bcrypt'
|
||||
echo -e "${BOLD}${YELLOW}┏
|
||||
┃ 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.
|
||||
┗${RESET}" >&2
|
||||
exit 1
|
||||
;;
|
||||
sha512crypt|sha256crypt)
|
||||
perr-unsupportedhash "$TYPE"
|
||||
perr-forbiddenhash 'SHA'
|
||||
echo -e "${BOLD}${YELLOW}┏
|
||||
┃ 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.
|
||||
┗${RESET}" >&2
|
||||
exit 1
|
||||
;;
|
||||
sunmd5|md5crypt)
|
||||
perr-unsupportedhash "$TYPE"
|
||||
perr-forbiddenhash 'MD5'
|
||||
echo -e "${BOLD}${YELLOW}┏
|
||||
┃ 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.
|
||||
┗${RESET}" >&2
|
||||
exit 1
|
||||
;;
|
||||
bsdicrypt|descrypt)
|
||||
perr-unsupportedhash "$TYPE"
|
||||
perr-forbiddenhash 'DES'
|
||||
echo -e "${BOLD}${YELLOW}┏
|
||||
┃ 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.
|
||||
┗${RESET}" >&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 "${BOLD}${YELLOW}┏
|
||||
┃ 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.
|
||||
┗${RESET}" >&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
|
||||
if ! isnumeric "$ROUNDS"; then
|
||||
throw-badval 1 "$ROUNDS" '-r|--rounds'
|
||||
fi
|
||||
|
||||
# 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
|
||||
182
ceru.bak/subcmds/new/ssh-key
Executable file
182
ceru.bak/subcmds/new/ssh-key
Executable file
|
|
@ -0,0 +1,182 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2025-2026 _cry64 (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
|
||||
|
||||
# ===== CONFIGURATION =====
|
||||
DEFAULT_TYPE='ecdsa'
|
||||
DEFAULT_ROUNDS='100'
|
||||
DEFAULT_BITS_ECDSA='521'
|
||||
DEFAULT_BITS_RSA='4096'
|
||||
DEFAULT_BITS_ED25519='NULL'
|
||||
# ===== CONFIGURATION =====
|
||||
|
||||
USAGE="${BOLD}${UNDERLINE}${RED}Usage${RESET}
|
||||
${BOLD}${GREEN}$THIS new ssh-key [option...]${RESET}
|
||||
|
||||
${BOLD}${UNDERLINE}${RED}Description${RESET}
|
||||
Generates a new SSH keypair with secure defaults.
|
||||
For more advanced usage run the ${BOLD}${MAGENTA}\`ssh-keygen\`${RESET} utility directly.
|
||||
|
||||
${BOLD}${UNDERLINE}${RED}Key Sizes${RESET}
|
||||
Key sizes are specified in bits via the ${BOLD}${MAGENTA}-b|--bits${RESET} flag.
|
||||
• ${BOLD}${CYAN}ECDSA keys${RESET} can only operate on elliptic curves of size: ${BOLD}${CYAN}256, 384, or 521 bits${RESET}
|
||||
• ${BOLD}${CYAN}Ed25519 keys${RESET} have a ${BOLD}${MAGENTA}fixed length${RESET} so the key size is ignored
|
||||
• ${BOLD}${CYAN}RSA keys${RESET} have been intentionally restricted to: ${BOLD}${CYAN}2048, 3072, 4096, or 8192 bits${RESET}
|
||||
|
||||
${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}${MAGENTA}.pub${RESET})
|
||||
${BOLD}${MAGENTA}-c, --comment${RESET} A comment or email address to write on the key
|
||||
${BOLD}${MAGENTA}-t, --type${RESET} The cryptographic algorithm to use: ${BOLD}${MAGENTA}ecdsa, ed25519, rsa${RESET} ${BOLD}${CYAN}(default: $DEFAULT_TYPE)${RESET}
|
||||
${BOLD}${MAGENTA}-r, --rounds${RESET} The number of KDF rounds to apply ${BOLD}${CYAN}(default: $DEFAULT_ROUNDS)${RESET}
|
||||
${BOLD}${MAGENTA}-b, --bits${RESET} The key size in bits ${BOLD}${MAGENTA}(see \"Key Sizes\" above) ${CYAN}(defaults: ecdsa=$DEFAULT_BITS_ECDSA, rsa=$DEFAULT_BITS_RSA, ed25519=$DEFAULT_BITS_ED25519)${RESET}
|
||||
${BOLD}${MAGENTA}-N, --nopasswd${RESET} Do not encrypt the private key with a password
|
||||
${BOLD}${MAGENTA}-H, --hardware-key${RESET} Enable the use of a secure hardware key peripheral device (ie YubiKey)"
|
||||
|
||||
# ==== Argument Values ====
|
||||
TYPE="$DEFAULT_TYPE"
|
||||
ROUNDS="$DEFAULT_ROUNDS"
|
||||
BITS=''
|
||||
COMMENT=''
|
||||
OUT=''
|
||||
NOPASSWD=''
|
||||
HWKEY=false
|
||||
# ==== 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
|
||||
;;
|
||||
-c|--comment)
|
||||
shift
|
||||
COMMENT="$1"; shift
|
||||
;;
|
||||
-t|--type)
|
||||
shift
|
||||
# NOTE: ${1,,} is $1 in lowercase
|
||||
TYPE="${1,,}"; shift
|
||||
;;
|
||||
-r|--rounds)
|
||||
shift
|
||||
ROUNDS="$1"; shift
|
||||
;;
|
||||
-b|--bits)
|
||||
shift
|
||||
BITS="$1"; shift
|
||||
;;
|
||||
-N|--nopasswd)
|
||||
shift
|
||||
NOPASSWD="-N ''"
|
||||
;;
|
||||
-H|--hardware-key)
|
||||
shift
|
||||
HWKEY=true
|
||||
;;
|
||||
-*)
|
||||
throw-badflag 1 "$ARG"
|
||||
;;
|
||||
*)
|
||||
throw-badarg 1 "$ARG"
|
||||
;;
|
||||
esac
|
||||
done; unset -v ARG
|
||||
|
||||
# ensure $ROUNDS is a valid numeric
|
||||
if ! isnumeric "$ROUNDS"; then
|
||||
throw-badval 1 "$ROUNDS" '-r|--rounds'
|
||||
fi
|
||||
|
||||
case "$TYPE" in
|
||||
ecdsa)
|
||||
if [[ -z "$BITS" ]]; then
|
||||
BITS='521'
|
||||
else
|
||||
# NOTE: ECDSA keys can only operate on elliptic curves
|
||||
# NOTE: of sizes: 256, 384 or 521 bits
|
||||
case "$BITS" in
|
||||
256|384|512) true
|
||||
;;
|
||||
*)
|
||||
throw-badval 1 "$BITS" '-b|--bits'
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
;;
|
||||
ed25519)
|
||||
# NOTE: the value of BITS does not matter for Ed25519
|
||||
# NOTE: as it operates on a fixed size elliptic curve
|
||||
if [[ -z "$BITS" ]]; then
|
||||
BITS='256'
|
||||
fi
|
||||
;;
|
||||
rsa)
|
||||
if [[ -z "$BITS" ]]; then
|
||||
BITS='4096'
|
||||
else
|
||||
case "$BITS" in
|
||||
2048)
|
||||
echo -e "${BOLD}${UNDERLINE}${YELLOW}WARNING${RESET}${BOLD}: Although ${MAGENTA}2048-bit RSA keys${YELLOW} are considered secure,${RESET}" >&2
|
||||
echo -e "${BOLD}${UNDERLINE}${YELLOW}WARNING${RESET}${BOLD}: it is the growing opinion that these will not be soon.${RESET}" >&2
|
||||
echo -e "${BOLD}${UNDERLINE}${YELLOW}WARNING${RESET}${BOLD}: ${GREEN}Consider using a minimum of ${MAGENTA}3072-bit${YELLOW}, or ideally ${MAGENTA}4096-bit.${RESET}" >&2
|
||||
;;
|
||||
3072|4096|8192) true
|
||||
;;
|
||||
*)
|
||||
throw-badval 1 "$BITS" '-b|--bits'
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
throw-badval 1 "$TYPE" '-t|--type'
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! isnumeric "$BITS"; then
|
||||
throw-badval 1 "$BITS" '-b|--bits'
|
||||
fi
|
||||
|
||||
if [[ "$HWKEY" == true ]]; then
|
||||
if [[ "$TYPE" == "rsa" ]]; then
|
||||
echo -e "${BOLD}${MAGENTA}-H|--hardware-key${RESET} flag is not valid for ${BOLD}${MAGENTA}RSA keys${RESET} ${BOLD}${CYAN}(use Ed25519 instead)${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
TYPE="$TYPE-sk"
|
||||
fi
|
||||
|
||||
if [[ -z "$OUT" ]]; then
|
||||
# fallback to ssh-keygen's default file (for chmod later)
|
||||
OUT="$HOME/.ssh/id_$TYPE"
|
||||
fi
|
||||
|
||||
# permit error during key generation
|
||||
set +e
|
||||
echo -e "${BOLD}${GREEN}[+] ssh-keygen -t $TYPE -a$ROUNDS -b$BITS -C '$COMMENT' -f '$OUT' $NOPASSWD${RESET}"
|
||||
ssh-keygen -t $TYPE -a "$ROUNDS" -b "$BITS" -C "$COMMENT" -f "$OUT" $NOPASSWD
|
||||
chmod 600 $OUT
|
||||
chmod 644 $OUT.pub
|
||||
# reset state
|
||||
set -e
|
||||
|
||||
unset TYPE ROUNDS BITS COMMENT OUT NOPASSWD HWKEY \
|
||||
DEFAULT_TYPE DEFAULT_ROUNDS DEFAULT_BITS_ECDSA DEFAULT_BITS_RSA DEFAULT_BITS_ED25519
|
||||
96
ceru.bak/subcmds/new/wg-key
Executable file
96
ceru.bak/subcmds/new/wg-key
Executable file
|
|
@ -0,0 +1,96 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2025-2026 _cry64 (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 cache-key [option...]${RESET}
|
||||
|
||||
${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}${MAGENTA}.pub${RESET})
|
||||
${BOLD}${MAGENTA}-j, --json${RESET} Output in JSON format
|
||||
${BOLD}${MAGENTA}-f, --force${RESET} Ignores all warnings!!"
|
||||
|
||||
# ==== Argument Values ====
|
||||
OUT=''
|
||||
JSON=false
|
||||
FORCE=false
|
||||
# ==== 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
|
||||
;;
|
||||
-f|--force)
|
||||
shift
|
||||
FORCE=true
|
||||
;;
|
||||
-*)
|
||||
throw-badflag 1 "$ARG"
|
||||
;;
|
||||
*)
|
||||
throw-badarg 1 "$ARG"
|
||||
;;
|
||||
esac
|
||||
done; unset -v ARG
|
||||
|
||||
|
||||
|
||||
# generate our keypair
|
||||
PRIV_KEY=$(wg genkey)
|
||||
PUB_KEY=$(wg pubkey <<<"$PRIV_KEY")
|
||||
|
||||
# NOTE: same logic as `subcmds/new/cache-key`
|
||||
# result defaults to unset (only stays unset if we intend on writing to a file)
|
||||
RESULT=""
|
||||
# JSON formatting
|
||||
if [[ "$JSON" = true ]]; then
|
||||
RESULT="{
|
||||
\"privateKey\": \"${PRIV_KEY}\"
|
||||
\"publicKey\": \"${PUB_KEY}\"
|
||||
}"
|
||||
if [[ -n "$OUT" ]]; then
|
||||
# confirm the user understands files will be overwritten
|
||||
[[ "$FORCE" = true ]] || confirm-file-overwrite "$OUT" || exit 0
|
||||
echo -e "$RESULT" > "$OUT"
|
||||
else
|
||||
echo -e "$RESULT"
|
||||
fi
|
||||
# standard formatting (stdout)
|
||||
elif [[ -z "$OUT" ]]; then
|
||||
echo -e "${BOLD}${UNDERLINE}${RED}Private Key${RESET}
|
||||
${BOLD}${GREEN}${PRIV_KEY}${RESET}
|
||||
${BOLD}${UNDERLINE}${RED}Public Key${RESET}
|
||||
${BOLD}${GREEN}${PUB_KEY}${RESET}"
|
||||
# standard formatting (files)
|
||||
else
|
||||
# confirm the user understands files will be overwritten
|
||||
[[ "$FORCE" = true ]] || confirm-file-overwrite "$OUT" "$OUT.pub" || exit 0
|
||||
echo "$PRIV_KEY" > "$OUT"
|
||||
echo "$PUB_KEY" > "$OUT.pub"
|
||||
fi;
|
||||
unset -v OUT PRIV_KEY PUB_KEY RESULT
|
||||
Loading…
Add table
Add a link
Reference in a new issue