Skip to content

Commit 7d84b89

Browse files
committed
new pa-rekey
1 parent ffdc1ce commit 7d84b89

File tree

3 files changed

+79
-25
lines changed

3 files changed

+79
-25
lines changed

contrib/pa-rekey

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/bin/sh
2+
#
3+
# rotate keys and reencrypt passwords
4+
5+
set -e
6+
7+
# Complete any pending
8+
# recipients synchronization.
9+
pa l >/dev/null
10+
11+
: "${PA_DIR:=${XDG_DATA_HOME:-$HOME/.local/share}/pa}"
12+
13+
suffix=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom |
14+
dd ibs=1 obs=1 count=10 2>/dev/null)
15+
16+
[ "$suffix" ]
17+
18+
tmpdir=$PA_DIR/rekey.$suffix
19+
20+
mkdir "$tmpdir"
21+
22+
trap 'rm -rf "$tmpdir/identities" "$tmpdir/recipients" "$tmpdir/passwords"
23+
rmdir "$tmpdir"' EXIT
24+
25+
cp -p "$PA_DIR/identities" "$tmpdir/identities"
26+
27+
# Generate recipients for current identities.
28+
PA_DIR=$tmpdir PA_NOGIT='' pa l >/dev/null
29+
30+
grep -v '^#' "$tmpdir/recipients" >"$tmpdir/passwords/.recipients"
31+
32+
# Filter out recipients corresponding to current identities.
33+
awk 'FNR==NR{f[$0]=1;next}f[$0]{if(r!~/^#/&&r)print r;r="";next}r{print r}{r=$0}
34+
END{if(r)print r}' "$tmpdir/passwords/.recipients" "$PA_DIR/recipients" \
35+
>"$tmpdir/recipients"
36+
37+
rm -f "$tmpdir/identities"
38+
39+
# Generate a brand new key pair.
40+
PA_DIR=$tmpdir PA_NOGIT='' pa l >/dev/null
41+
42+
# Ensure that recipients for identites to be retired were removed.
43+
grep -v '^#' "$PA_DIR/recipients" | grep -Fxvf "$tmpdir/recipients" - >/dev/null
44+
45+
# Recipients are now ready to use.
46+
mv "$tmpdir/recipients" "$PA_DIR/recipients"
47+
48+
# Reencrypt all passwords
49+
# for new recipients.
50+
printf n | pa l >/dev/null 2>&1
51+
52+
# New identity is now ready to use.
53+
mv "$tmpdir/identities" "$PA_DIR/identities"

pa

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,12 @@ pw_list() {
9191
find . -type f -name \*.age | sed 's/..//;s/\.age$//' | sort
9292
}
9393

94-
change_keys() {
94+
change_recipients() {
9595
cmp -s "$recipients_file" "$recipients_sync" && return
9696

9797
# Compare recipients files to show new remotely added recipients.
98-
grep -Fxvf "$recipients_file" "$recipients_sync" 2>/dev/null &&
99-
if yn "add these new recipients?"; then
98+
grep -Fxvf "$recipients_file" "$recipients_sync" >&2 2>/dev/null &&
99+
if yn "do you trust these recipients?"; then
100100
tmp=$(mkstemp) ||
101101
die "couldn't create a shared memory filename"
102102

@@ -108,7 +108,7 @@ change_keys() {
108108

109109
rm -f "$tmp"
110110
else
111-
# Purge git repository to prevent leaks for unwanted recipients.
111+
# Purge git repository to prevent leaks for untrusted recipients.
112112
[ -d .git ] && find .git ! -name config -type f -exec rm -f {} +
113113
fi
114114

@@ -128,7 +128,7 @@ change_keys() {
128128
cp "$recipients_file" "$recipients_sync"
129129

130130
if $git_enabled && [ -d .git ]; then
131-
git init -q && git_add_and_commit . "change keys"
131+
git init -q && git_add_and_commit . "change recipients"
132132
fi
133133
}
134134

@@ -146,7 +146,7 @@ mkstemp() {
146146
# have non-standard methods of setup/access.
147147
[ -w /dev/shm ] || tmpdir=/tmp
148148

149-
printf '%s/pa.%s' $tmpdir "$(rand_chars 10 'A-Za-z0-9')"
149+
printf "$tmpdir/pa.%s" "$(rand_chars 10 'A-Za-z0-9')"
150150
}
151151

152152
rand_chars() {
@@ -165,7 +165,7 @@ rand_chars() {
165165
}
166166

167167
yn() {
168-
printf '%s [y/N]: ' "$1"
168+
printf '%s [y/N]: ' "$1" >&2
169169

170170
# Enable raw input to allow for a single byte to be read from
171171
# stdin without needing to wait for the user to press Return.
@@ -179,7 +179,7 @@ yn() {
179179
# have found it.
180180
[ -t 0 ] && stty echo icanon
181181

182-
printf '%s\n' "$answer"
182+
printf '%s\n' "$answer" >&2
183183

184184
# Handle the answer here directly, enabling this function's
185185
# return status to be used in place of checking for '[yY]'
@@ -188,7 +188,7 @@ yn() {
188188
}
189189

190190
sread() {
191-
printf '%s: ' "$2"
191+
printf '%s: ' "$2" >&2
192192

193193
# Disable terminal printing while the user inputs their
194194
# password. POSIX 'read' has no '-s' flag which would
@@ -197,7 +197,7 @@ sread() {
197197
read -r "$1"
198198
[ -t 0 ] && stty echo
199199

200-
printf '\n'
200+
printf '\n' >&2
201201
}
202202

203203
glob() {
@@ -269,10 +269,10 @@ main() {
269269
[ -s "$identities_file" ] ||
270270
if command -v age-plugin-yubikey >/dev/null &&
271271
yn "generate yubikey identity?"; then
272-
age-plugin-yubikey -g --pin-policy never --touch-policy always \
273-
--name "pa identity" >"$identities_file"
272+
age-plugin-yubikey --generate --name "pa identity" \
273+
--pin-policy never --touch-policy always >"$identities_file"
274274
else
275-
$age_keygen -o "$identities_file" 2>/dev/null
275+
$age_keygen >"$identities_file" 2>/dev/null
276276
fi ||
277277
die "couldn't generate an identity"
278278

@@ -286,9 +286,10 @@ main() {
286286
fi ||
287287
die "couldn't generate recipients"
288288

289-
: "${PA_EMAIL:=$(id -u -n)@$(uname -n)}"
289+
: "${PA_EMAIL:=${EMAIL:-$(id -un)@$(uname -n)}}"
290290

291-
printf '# %s\n%s\n' "$PA_EMAIL" "$new_recipients" >>"$recipients_file"
291+
printf '# %s %s\n%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$PA_EMAIL" \
292+
"$new_recipients" >>"$recipients_file"
292293
}
293294

294295
git_enabled=false
@@ -297,7 +298,7 @@ main() {
297298
$git_enabled && [ ! -d .git ] && {
298299
# Make sure recipients synchronization
299300
# file is included in the initial commit.
300-
change_keys
301+
change_recipients
301302

302303
git init -q
303304

@@ -306,7 +307,7 @@ main() {
306307
# Put something in user config if it's not set globally,
307308
# because git doesn't allow to commit without it.
308309
git config user.email >/dev/null ||
309-
git config user.email "${PA_EMAIL:-$(id -u -n)@$(uname -n)}"
310+
git config user.email "${PA_EMAIL:-${EMAIL:-$(id -un)@$(uname -n)}}"
310311

311312
# Configure diff driver for age encrypted files that treats them as
312313
# binary and decrypts them when a human-readable diff is requested.
@@ -326,11 +327,11 @@ main() {
326327
shift
327328

328329
glob "$command" 'g*' && {
329-
git "$@" && change_keys
330+
git "$@" && change_recipients
330331
exit $?
331332
}
332333

333-
change_keys
334+
change_recipients
334335

335336
# Combine the rest of positional arguments into
336337
# a name and remove control characters from it

test

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,13 @@ test "$(./pa show test)" = "secret" ||
9292
cmp -s "$PA_DIR/recipients" "$PA_DIR/passwords/.recipients" ||
9393
fail "both recipients files should be equal"
9494

95-
git -C "$PA_DIR/passwords" log | grep -q "change keys" ||
96-
fail "git log should have line: change keys"
95+
git -C "$PA_DIR/passwords" log | grep -q "change recipients" ||
96+
fail "git log should have line: change recipients"
9797

9898
# incoming recipients sync
9999
ex -sc '1,$-1d|x' "$PA_DIR/recipients"
100100

101-
printf y | ./pa list 2>&1 | grep -q "add these new recipients?" ||
101+
printf y | ./pa list 2>&1 | grep -q "do you trust these recipients?" ||
102102
fail "pa should ask to add incoming recipients"
103103

104104
cmp -s "$PA_DIR/recipients" "$PA_DIR/passwords/.recipients" ||
@@ -109,7 +109,7 @@ test "$(grep -c ^age1 "$PA_DIR/recipients")" = 2 ||
109109

110110
ex -sc '1,$-1d|x' "$PA_DIR/recipients"
111111

112-
printf n | ./pa list 2>&1 | grep -q "add these new recipients?" ||
112+
printf n | ./pa list 2>&1 | grep -q "do you trust these recipients?" ||
113113
fail "pa should ask to add incoming recipients"
114114

115115
cmp -s "$PA_DIR/recipients" "$PA_DIR/passwords/.recipients" ||
@@ -118,8 +118,8 @@ cmp -s "$PA_DIR/recipients" "$PA_DIR/passwords/.recipients" ||
118118
test "$(grep -c ^age1 "$PA_DIR/recipients")" = 1 ||
119119
fail "a recipients file should contain 1 recipient"
120120

121-
test "$(git -C "$PA_DIR/passwords" log --format=%s)" = "change keys" ||
122-
fail "git repository should have a single commit titled change keys"
121+
test "$(git -C "$PA_DIR/passwords" log --format=%s)" = "change recipients" ||
122+
fail "git repository should have a single commit titled change recipients"
123123

124124
# print info & exit w/ correct status
125125
printf "\ntotal failures: %d\n" "$failures"

0 commit comments

Comments
 (0)