aboutsummaryrefslogtreecommitdiffstats
path: root/modules/security/gpg/gpg-backup.sh
blob: 57c216badd511c1e915d942d30cf10ef89ed6268 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/env bash
set -euo pipefail

WORKDIR="$(mktemp -d)"

cleanup() {
  if [[ -d "$WORKDIR" ]]; then
    find "$WORKDIR" -type f -exec shred -u -z {} \; 2>/dev/null || true
    rm -rf "$WORKDIR"
  fi
}
trap cleanup EXIT

usage() {
  echo "Usage:"
  echo "  $0 export <filename>   Export all GPG keys to an encrypted file"
  echo "  $0 import <filename>   Decrypt and import keys from a backup file"
  exit 1
}

do_export() {
  local outfile="$1"

  if [[ -e "$outfile" ]]; then
    echo "!! Refusing to overwrite existing file: $outfile" >&2
    exit 1
  fi

  echo "==> Exporting public keys..."
  gpg --export --armor > "$WORKDIR/public-keys.asc"

  echo "==> Exporting secret keys..."
  gpg --export-secret-keys --armor > "$WORKDIR/secret-keys.asc"

  echo "==> Exporting secret subkeys (if any)..."
  gpg --export-secret-subkeys --armor > "$WORKDIR/secret-subkeys.asc" || true

  echo "==> Exporting owner trust database..."
  gpg --export-ownertrust > "$WORKDIR/ownertrust.txt"

  echo "==> Exporting revocation certificates..."
  mkdir -p "$WORKDIR/revocation-certs"
  if [[ -d "$HOME/.gnupg/openpgp-revocs.d" ]]; then
    cp "$HOME"/.gnupg/openpgp-revocs.d/*.rev "$WORKDIR/revocation-certs/" 2>/dev/null || true
  fi

  echo "==> Bundling everything into a single archive..."
  tar -C "$WORKDIR" -cf "$WORKDIR/gpg-full-backup.tar" \
    public-keys.asc \
    secret-keys.asc \
    secret-subkeys.asc \
    ownertrust.txt \
    revocation-certs

  echo "==> Encrypting with GPG (AES256, SHA512, max S2K iteration count)..."
  echo "  You will be prompted for a passphrase — use a strong one."
  gpg --symmetric \
    --cipher-algo AES256 \
    --digest-algo SHA512 \
    --s2k-mode 3 \
    --s2k-digest-algo SHA512 \
    --s2k-count 65011712 \
    --output "$outfile" \
    "$WORKDIR/gpg-full-backup.tar"

  echo "==> Verifying: attempting decryption to confirm it works..."
  if gpg --decrypt "$outfile" > "$WORKDIR/verify.tar" 2>/dev/null; then
    if cmp -s "$WORKDIR/gpg-full-backup.tar" "$WORKDIR/verify.tar"; then
      echo "==> Verification succeeded: backup decrypts correctly."
    else
      echo "!! WARNING: decrypted content does not match original. Investigate before trusting this backup." >&2
      exit 1
    fi
  else
    echo "!! WARNING: decryption test failed." >&2
    exit 1
  fi

  echo
  echo "==> Done."
  echo "  Encrypted backup: $outfile"
  echo "  Store this file somewhere safe (offline media, encrypted drive)."
  echo "  The S2K iteration count only helps if your passphrase itself"
  echo "  has real entropy (e.g. a long diceware passphrase)."
}

do_import() {
  local infile="$1"

  if [[ ! -f "$infile" ]]; then
    echo "!! File not found: $infile" >&2
    exit 1
  fi

  echo "==> Decrypting $infile ..."
  echo "  You will be prompted for the backup's passphrase."
  echo "  Note: this may take a while due to the high S2K iteration count."
  gpg --decrypt "$infile" > "$WORKDIR/gpg-full-backup.tar"

  echo "==> Extracting archive..."
  tar -C "$WORKDIR" -xf "$WORKDIR/gpg-full-backup.tar"

  echo "==> Importing public keys..."
  gpg --import "$WORKDIR/public-keys.asc"

  echo "==> Importing secret keys..."
  gpg --import "$WORKDIR/secret-keys.asc"

  if [[ -s "$WORKDIR/secret-subkeys.asc" ]]; then
    echo "==> Importing secret subkeys..."
    gpg --import "$WORKDIR/secret-subkeys.asc" || true
  fi

  if [[ -f "$WORKDIR/ownertrust.txt" ]]; then
    echo "==> Importing owner trust database..."
    gpg --import-ownertrust "$WORKDIR/ownertrust.txt"
  fi

  if [[ -d "$WORKDIR/revocation-certs" ]] && [[ -n "$(ls -A "$WORKDIR/revocation-certs" 2>/dev/null)" ]]; then
    echo "==> Restoring revocation certificates..."
    mkdir -p "$HOME/.gnupg/openpgp-revocs.d"
    cp "$WORKDIR"/revocation-certs/*.rev "$HOME/.gnupg/openpgp-revocs.d/" 2>/dev/null || true
  fi

  echo
  echo "==> Done. Keys imported into your GPG keyring."
  echo "  Run 'gpg --list-secret-keys' to confirm."
}

# Main

if [[ $# -ne 2 ]]; then
  usage
fi

command="$1"
filename="$2"

case "$command" in
  export)
    do_export "$filename"
    ;;
  import)
    do_import "$filename"
    ;;
  *)
    usage
    ;;
esac