This repository has no description
3.9 kB
109 lines
1#!/usr/bin/env bash
2set -euo pipefail
3
4# age-detect: Detects connected age-compatible security keys
5# Returns identity file path and detected key information
6
7# Function to detect connected security keys and their age identities
8detect_age_keys() {
9 local temp_identities
10 temp_identities=$(mktemp)
11
12 # Check for Secure Enclave on macOS (highest priority)
13 if [[ "$(uname)" == "Darwin" ]] && command -v age-plugin-se >/dev/null 2>&1; then
14 rm -f "$temp_identities"
15 echo "DETECTED_KEY=se"
16 echo "KEY_TYPE=se"
17 return
18 fi
19
20 # Check for TPM on Linux (second priority - built-in, no USB needed)
21 # Requires: TPM device, hostname.age with age1tpm1 recipient, and identity in ~/.config/age/
22 if [[ -c /dev/tpmrm0 ]] && [[ -f ~/.config/age/tpm-identity.txt ]]; then
23 local hostname_key
24 hostname_key="${AGE_DETECT_KEYS_DIR:-}/age/$(hostname).age"
25 if [[ -f "$hostname_key" ]] && grep -q "^age1tpm1" "$hostname_key" 2>/dev/null; then
26 rm -f "$temp_identities"
27 echo "DETECTED_KEY=tpm"
28 echo "KEY_TYPE=tpm"
29 return
30 fi
31 fi
32
33 # Check for gpg-agent with an encryption subkey (third priority).
34 # Frictionless after the first decrypt because gpg-agent caches the
35 # passphrase / smartcard PIN, so we prefer it over yubikey/solo2.
36 if command -v gpg-connect-agent >/dev/null 2>&1 \
37 && gpg-connect-agent /bye >/dev/null 2>&1 \
38 && gpg --list-secret-keys --with-colons 2>/dev/null \
39 | awk -F: '$1 == "ssb" && $12 ~ /e/ { found=1 } END { exit !found }'; then
40 echo "AGE-PLUGIN-GPG-1H40XPM" > "$temp_identities"
41 echo "IDENTITY_FILE=$temp_identities"
42 echo "DETECTED_KEY=gpg"
43 echo "KEY_TYPE=gpg"
44 return
45 fi
46
47 # Check for YubiKeys (fourth priority)
48 if command -v ykman >/dev/null 2>&1; then
49 local yubikey_serial
50 yubikey_serial=$(ykman info 2>/dev/null | grep "Serial number:" | cut -d: -f2 | tr -d ' ' || true)
51 if [[ -n "$yubikey_serial" ]]; then
52 echo "AGE-PLUGIN-YUBIKEY-1M2PLUQYZ242EPUSSHDFWJ" > "$temp_identities"
53 echo "IDENTITY_FILE=$temp_identities"
54 case "$yubikey_serial" in
55 "16679898")
56 echo "DETECTED_KEY=yubi1"
57 ;;
58 "11342136")
59 echo "DETECTED_KEY=yubi2"
60 ;;
61 *)
62 echo "DETECTED_KEY=yubikey-${yubikey_serial}"
63 ;;
64 esac
65 echo "KEY_TYPE=yubikey"
66 return
67 fi
68 fi
69
70 # Check for Solo2 (lowest priority)
71 if command -v fido2-token >/dev/null 2>&1; then
72 if fido2-token -L 2>/dev/null | grep -q "Solo 2"; then
73 # FIDO2-HMAC doesn't use identity file
74 rm -f "$temp_identities"
75 echo "IDENTITY_FILE="
76 echo "DETECTED_KEY=solo2"
77 echo "KEY_TYPE=solo2"
78 return
79 fi
80 fi
81
82 # No keys detected
83 rm -f "$temp_identities"
84 echo "IDENTITY_FILE="
85 echo "DETECTED_KEY="
86 echo "KEY_TYPE="
87}
88
89# Parse command line arguments
90case "${1:-detect}" in
91 detect)
92 detect_age_keys
93 ;;
94 --help|-h)
95 echo "Usage: age-detect [detect]"
96 echo ""
97 echo "Detects the first available age-compatible security key and outputs:"
98 echo " IDENTITY_FILE=<path> - Path to temporary identity file for age (empty for Solo2)"
99 echo " DETECTED_KEY=<name> - Name of the detected key (e.g., yubi1, solo2, se)"
100 echo " KEY_TYPE=<type> - Type of key (yubikey, solo2, se)"
101 echo ""
102 echo "Priority order: se > tpm > gpg > yubikey > solo2"
103 ;;
104 *)
105 echo "Unknown command: $1" >&2
106 echo "Use --help for usage information" >&2
107 exit 1
108 ;;
109esac