This commit is contained in:
2026-06-10 01:12:51 -07:00
parent b86289a348
commit 0c5b9b27b7
4 changed files with 58 additions and 11 deletions
Vendored
BIN
View File
Binary file not shown.
+6 -1
View File
@@ -95,6 +95,11 @@ TEMPLATES=(
| `{num:N}` | N digits | ~3.3 bits/char | | `{num:N}` | N digits | ~3.3 bits/char |
| `{special:N}` | N from `!?@#%~=+` | ~3.0 bits/char | | `{special:N}` | N from `!?@#%~=+` | ~3.0 bits/char |
| `{NAME:N-M}` | N to M chars from charset NAME | log2(Σ size^L) | | `{NAME:N-M}` | N to M chars from charset NAME | log2(Σ size^L) |
| `{hex:N}` | `3f9a` | 4.0 bits/char |
| `{b58:N}` | `Vk2x` (Base58, no 0OIl) | ~5.9 bits/char |
| `{b64:N}` | `hG_4` (URL-safe base64) | 6.0 bits/char |
| `{alnum:N}` | `xR4x` | ~6.0 bits/char |
| `{ascii:N}` | `p/(!` (all printable, no space) | ~6.5 bits/char |
\* Word entropy depends on pool size. Values shown assume all built-in lists enabled (~14k words, ~13.8 bits/word). \* Word entropy depends on pool size. Values shown assume all built-in lists enabled (~14k words, ~13.8 bits/word).
@@ -109,7 +114,7 @@ CHARSETS=(
) )
``` ```
Names must match `[a-z][a-z0-9_]*` and override the built-in four (alpha, safe, num, special). Avoid duplicate characters; himi warns if it sees them, since they skew the distribution and overstate entropy. Names must match `[a-z][a-z0-9_]*` and override any built-in of the same name (alpha, safe, num, special, hex, b58, b64, alnum, ascii). Avoid duplicate characters; himi warns if it sees them, since they skew the distribution and overstate entropy.
Range tokens pick the length weighted by `size^L`, so every possible output is equally likely. `{num:1-2}` covers 0-99 plus 0-9 (110 outcomes, ~6.8 bits), not a 50/50 coin flip on length, which would tank min-entropy. Range tokens pick the length weighted by `size^L`, so every possible output is equally likely. `{num:1-2}` covers 0-99 plus 0-9 (110 outcomes, ~6.8 bits), not a 50/50 coin flip on length, which would tank min-entropy.
+2 -1
View File
@@ -42,7 +42,8 @@ WORD_MAX_LENGTH=9
# {Word} — random word (Title Case) # {Word} — random word (Title Case)
# {word!} — random lower or UPPER (+1 bit entropy) # {word!} — random lower or UPPER (+1 bit entropy)
# {Word!} — random lower/Title/UPPER (+1.58 bits entropy) # {Word!} — random lower/Title/UPPER (+1.58 bits entropy)
# {NAME:N} — N chars from charset NAME (alpha/safe/num/special/custom) # {NAME:N} — N chars from charset NAME
# built-in: alpha safe num special hex b58 b64 alnum ascii
# {NAME:N-M} — N to M chars, weighted so every output is equally likely # {NAME:N-M} — N to M chars, weighted so every output is equally likely
# (entropy = log2 of total outcomes, e.g. {num:1-2} ≈ 6.8 bits) # (entropy = log2 of total outcomes, e.g. {num:1-2} ≈ 6.8 bits)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
+50 -9
View File
@@ -28,6 +28,9 @@ readonly CONFIG_SCHEMA_VERSION=2
# Generic charset token: {name:N} or {name:N-M} # Generic charset token: {name:N} or {name:N-M}
readonly TOKEN_RE='\{([a-z][a-z0-9_]*):([0-9]+)(-([0-9]+))?\}' readonly TOKEN_RE='\{([a-z][a-z0-9_]*):([0-9]+)(-([0-9]+))?\}'
# Anything that still looks like a token after expansion (typo'd or unknown).
# Shape-based so charsets containing { } (e.g. ascii) don't false-positive.
readonly LEFTOVER_RE='\{[A-Za-z][A-Za-z0-9_]*(!|:[0-9]+(-[0-9]+)?)?\}'
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@@ -37,6 +40,11 @@ ALPHA_CHARSET='0123456789ABCDEFGHJKMNPQRSTVWXYZ' # Crockford Base32 (32 chars,
SAFE_CHARSET='ACDEFGHJKMNPQRTWXY234679' # Ultra-safe (24 chars, 4.6 bits) - no B/8, 0/O, 1/I/L, 5/S SAFE_CHARSET='ACDEFGHJKMNPQRTWXY234679' # Ultra-safe (24 chars, 4.6 bits) - no B/8, 0/O, 1/I/L, 5/S
NUM_CHARSET='0123456789' NUM_CHARSET='0123456789'
SPECIAL_CHARSET='!?@#%~=+' SPECIAL_CHARSET='!?@#%~=+'
HEX_CHARSET='0123456789abcdef' # 16 chars, 4.0 bits
B58_CHARSET='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' # Base58: no 0OIl (58 chars, 5.86 bits)
B64_CHARSET='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_' # URL-safe base64 (64 chars, 6.0 bits)
ALNUM_CHARSET='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' # 62 chars, 5.95 bits
ASCII_CHARSET='!"#$%&'\''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~' # all printable, no space (94 chars, 6.55 bits)
WORD_MIN_LENGTH=4 WORD_MIN_LENGTH=4
WORD_MAX_LENGTH=8 WORD_MAX_LENGTH=8
@@ -200,6 +208,11 @@ get_charset() {
safe) printf '%s' "$SAFE_CHARSET" ;; safe) printf '%s' "$SAFE_CHARSET" ;;
num) printf '%s' "$NUM_CHARSET" ;; num) printf '%s' "$NUM_CHARSET" ;;
special) printf '%s' "$SPECIAL_CHARSET" ;; special) printf '%s' "$SPECIAL_CHARSET" ;;
hex) printf '%s' "$HEX_CHARSET" ;;
b58) printf '%s' "$B58_CHARSET" ;;
b64) printf '%s' "$B64_CHARSET" ;;
alnum) printf '%s' "$ALNUM_CHARSET" ;;
ascii) printf '%s' "$ASCII_CHARSET" ;;
*) return 1 ;; *) return 1 ;;
esac esac
} }
@@ -521,6 +534,9 @@ cmd_list() {
# Uninstall: remove himi and all data # Uninstall: remove himi and all data
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
cmd_uninstall() { cmd_uninstall() {
local purge="false"
[[ "${1:-}" == "--purge" ]] && purge="true"
read -p "Are you sure you want to remove himi and all custom wordlists? (y/N) " -n 1 -r read -p "Are you sure you want to remove himi and all custom wordlists? (y/N) " -n 1 -r
echo echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then if [[ ! $REPLY =~ ^[Yy]$ ]]; then
@@ -543,23 +559,43 @@ cmd_uninstall() {
# Linux/GNU # Linux/GNU
sed -i.bak "/$start_marker/,/$end_marker/d" "$HOME/$p" sed -i.bak "/$start_marker/,/$end_marker/d" "$HOME/$p"
fi fi
ok "Cleaned up $p (backup created as $p.bak)" if [[ "$purge" == "true" ]]; then
rm -f "$HOME/$p.bak"
ok "Cleaned up $p (no backup, --purge)"
else
ok "Cleaned up $p (backup created as $p.bak)"
fi
fi fi
fi fi
done done
# 2. Remove completions # 2. Remove completions (and the .zfunc dir if we leave it empty)
rm -f "${HOME}/.zfunc/_himi" rm -f "${HOME}/.zfunc/_himi"
rmdir "${HOME}/.zfunc" 2>/dev/null || true
rm -f "${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions/himi" rm -f "${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions/himi"
# 3. Remove data and binary # 3. Remove data and binary
rm -rf "$CONFIG_DIR" rm -rf "$CONFIG_DIR"
ok "Removed configuration and wordlists." ok "Removed configuration and wordlists."
# Final step: Binary deletes itself # Final step: binary deletes itself. Prefer the file actually running
local self # (works for ./himi too); fall back to PATH lookup without tripping set -e.
self=$(command -v himi) local self="${BASH_SOURCE[0]}"
rm -f "$self" if [[ ! -f "$self" ]]; then
self=$(command -v himi) || self=""
fi
[[ -n "$self" ]] && rm -f "$self"
# Flag any other copies still lurking in PATH
local leftover
if leftover=$(command -v himi 2>/dev/null) && [[ -n "$leftover" ]]; then
warn "another himi remains at: $leftover — remove it manually"
fi
if [[ "$purge" != "true" ]]; then
dim "Profile backups (*.bak) were kept; use 'himi uninstall --purge' for none."
printf "\n"
fi
ok "himi has been uninstalled." ok "himi has been uninstalled."
} }
@@ -953,7 +989,7 @@ cmd_test() {
local example leftovers="" local example leftovers=""
for ((i = 0; i < 5; i++)); do for ((i = 0; i < 5; i++)); do
example=$(expand_template "$pattern") example=$(expand_template "$pattern")
[[ "$example" == *'{'* && "$example" == *'}'* ]] && leftovers="true" [[ "$example" =~ $LEFTOVER_RE ]] && leftovers="true"
printf " %s\n" "$example" printf " %s\n" "$example"
done done
printf "\n" printf "\n"
@@ -1012,7 +1048,7 @@ cmd_generate() {
secrets+=$(expand_template "$pattern") secrets+=$(expand_template "$pattern")
done done
if [[ "$secrets" == *'{'* && "$secrets" == *'}'* ]]; then if [[ "$secrets" =~ $LEFTOVER_RE ]]; then
warn "output contains unexpanded {token}s — check the template ('himi test' helps)" warn "output contains unexpanded {token}s — check the template ('himi test' helps)"
fi fi
@@ -1054,7 +1090,7 @@ Usage:
himi entropy [name] Show entropy for template(s) himi entropy [name] Show entropy for template(s)
himi test '{pattern}' Test a custom pattern himi test '{pattern}' Test a custom pattern
himi config Edit config in \$EDITOR himi config Edit config in \$EDITOR
himi uninstall Remove himi and all data himi uninstall Remove himi and all data (--purge: no profile backups)
himi -h, --help Show this help himi -h, --help Show this help
Templates (configure in $CONFIG_FILE): Templates (configure in $CONFIG_FILE):
@@ -1082,6 +1118,11 @@ EOF
printf " %-12s %s\n" "safe" "$SAFE_CHARSET" printf " %-12s %s\n" "safe" "$SAFE_CHARSET"
printf " %-12s %s\n" "num" "$NUM_CHARSET" printf " %-12s %s\n" "num" "$NUM_CHARSET"
printf " %-12s %s\n" "special" "$SPECIAL_CHARSET" printf " %-12s %s\n" "special" "$SPECIAL_CHARSET"
printf " %-12s %s\n" "hex" "$HEX_CHARSET"
printf " %-12s %s\n" "b58" "$B58_CHARSET"
printf " %-12s %s\n" "b64" "$B64_CHARSET"
printf " %-12s %s\n" "alnum" "$ALNUM_CHARSET"
printf " %-12s %s\n" "ascii" "$ASCII_CHARSET"
for entry in ${CHARSETS[@]+"${CHARSETS[@]}"}; do for entry in ${CHARSETS[@]+"${CHARSETS[@]}"}; do
IFS='|' read -r name pattern <<< "$entry" IFS='|' read -r name pattern <<< "$entry"
printf " %-12s %s\n" "$name" "$pattern" printf " %-12s %s\n" "$name" "$pattern"