├── .gitignore
├── LICENCE
├── Makefile
├── README.md
├── config-sample
├── copy-secrets-to-usb.sh
├── copy-to-transfer-usb.sh
├── export-public-keys-and-shadows.sh
├── genkey-signing+encryption.conf-sample
├── genkey-signing.conf-sample
├── gpg.conf-sample
└── yubikey-reset.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | /20*_signing
2 | /20*_signing+encryption
3 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | Copyright 2017 Jinn Koriech
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # vim:set ts=2 sw=2 sts=2 noexpandtab:
2 |
3 | .PHONY: help
4 | help:
5 | @echo "Usage: [GPGHOME=/path/to/gnupghome] (g)make [TARGET]"
6 | @echo "Where:"
7 | @echo " GPGHOME the path to a folder for new keyrings, or containing existing keyrings"
8 | @echo " will be created if it doesn't exist, and will contain subfolders for"
9 | @echo " each smartcard/yubikey detected"
10 | @echo
11 | @echo "Targets:"
12 | @echo " default create a soft master key and encryption subkey, then"
13 | @echo " initialise two smartcards with signing and auth subkeys"
14 | @echo " and the soft encryption key."
15 | @echo
16 | @echo " softkeys (default) create a full set of keys: master, soft signing"
17 | @echo " subkey, soft encryption subkey, soft auth key."
18 | @echo
19 | @echo " master create a master key used to sign subkeys."
20 | @echo " signing a soft subkey used for signing only"
21 | @echo " encryption a soft subkey used for encryption only"
22 | @echo " auth a soft subkey used for authentication only"
23 | @echo
24 | @echo " id_rsa.pub extract the SSH public key from the authentication key, placing the"
25 | @echo " resulting file at \$$(GPGHOME)/DATA-transferred/id_rsa-\$${SSH_KEY}.pub"
26 | @echo
27 | @echo " import-secrets delete the master key and subkeys, then re-import the master key"
28 | @echo " and the encryption subkey"
29 | @echo
30 | @echo " show Display secret key information for temporary working path,"
31 | @echo " and card status."
32 | @echo
33 | @echo " remove-master Backup and remove the private master key from the working keychain"
34 | @echo
35 | @echo " import-ssb Import secret subkeys in FILE and link to SmartCard, requires"
36 | @echo " that you set SHADOWS=/path/to/DATA-transfered/subkey-shadows.asc"
37 | @echo
38 | @echo " test Perform a sign and encrypt, and a decrypt and verify to confirm"
39 | @echo " all is functioning as expected."
40 | @echo
41 | @echo " reset-yubikey Reset the GPG applet on a yubikey. WARNING: this will"
42 | @echo " irrevicoable wipe your smartcard."
43 | @echo
44 | @echo " clean Clean up previously created GNUPGHOME paths"
45 | @echo
46 | @echo "Requirements:"
47 | @echo " Only GPG >= 2.1 is supported."
48 |
49 | KEYSET := $(MAKECMDGOALS)
50 | ifeq ($(findstring signing, $(MAKECMDGOALS)),signing)
51 | KEYSET := sign
52 | KEYTAG := S
53 | else ifeq ($(findstring encryption, $(MAKECMDGOALS)),encryption)
54 | KEYSET := encr
55 | KEYTAG := E
56 | else ifeq ($(findstring auth, $(MAKECMDGOALS)),auth)
57 | KEYSET := auth
58 | KEYTAG := A
59 | else
60 | KEYSET := UNSET
61 | KEYTAG := UNSET
62 | endif
63 |
64 | # Use the GPGHOME environment variable to define a fixed gnupg home folder.
65 | GPGHOME ?= GPGHOME
66 | GPGBIN ?= gpg
67 | CARDNO := $(shell $(GPGBIN) --no-keyring --card-status 2>/dev/null | awk -F: '/^Serial/ {gsub("[ ]+", "", $$2); print $$2}')
68 |
69 | ifeq ($(GPGHOME),GPGHOME)
70 | ifeq ($(CARDNO),)
71 | GNUPGHOME := $(shell /bin/date '+%F_%H%M')/gnupg
72 | else
73 | GNUPGHOME := $(shell /bin/date '+%F_%H%M')/gnupg-$(CARDNO)
74 | endif
75 | else
76 | ifeq ($(CARDNO),)
77 | GNUPGHOME := $(GPGHOME)/gnupg
78 | else
79 | GNUPGHOME := $(GPGHOME)/gnupg-$(CARDNO)
80 | endif
81 | endif
82 |
83 | GPGCMD ?= $(GPGBIN) --no-default-keyring --homedir $(GNUPGHOME)
84 | GPG_VERSION_MAJOR = $(shell $(GPGCMD) --version 2>&1 | awk '/^gpg.*[0-9\.]+$$/ {print $$3}' | grep -Eo '^[0-9]')
85 | GPG_VERSION_MINOR = $(shell $(GPGCMD) --version 2>&1 | awk '/^gpg.*[0-9\.]+$$/ {print $$3}' | sed -e 's/^[0-9]\.//' -e 's/\.[0-9]*$$//')
86 |
87 | ###
88 | # Public goals
89 | ###
90 | .PHONY: default
91 | default: master sckey $(GPGHOME)/DATA-transferred/subkey-shadows.asc id_rsa.pub test
92 | @echo "🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 🔑 "
93 | @echo "Your keys have been generated."
94 | @echo
95 | @echo "To prepare another smartcard/yubikey with subkeys off this master key do the following:"
96 | @echo
97 | @echo " 1. '$(MAKE) import-secrets sckey'"
98 | @echo " 2. Follow the below instructions to backup up all public and private components"
99 | @echo
100 | @echo "💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 💾 "
101 | @echo "You'll should now back up $(GNUPGHOME) to an encrypted SLC USB stick or"
102 | @echo "two. Remember this USB stick will contain your original master key,"
103 | @echo "revocation certificates, and original soft subkeys, such as your"
104 | @echo "encryption key."
105 | @echo
106 | @echo "🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 🖨 "
107 | @echo "You can also print out your key backups and store them in a safe"
108 | @echo "place. The files are at:"
109 | @echo " - $(GPGHOME)/DATA-airgapped/paperkey-master.txt"
110 | @echo " - $(GPGHOME)/DATA-airgapped/paperkey-subkey-*.txt"
111 | @echo
112 | @echo "⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ ⏳ "
113 | @echo "Your keys, both master and subkeys, are set to expire. To extend their"
114 | @echo "validity you'll need to use the master key."
115 | @echo
116 | @echo "🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 🔐 "
117 | @echo "Finally, you'll want to transfer the following files to your regular"
118 | @echo "device and import the subkeys into your regular ~/.gnupg keychain."
119 | @echo " - $(GPGHOME)/DATA-transferred/subkey-shadows.asc"
120 | @echo " - $(GPGHOME)/DATA-transferred/id_rsa.pub"
121 | @echo
122 |
123 | .PHONY: softkeys
124 | softkeys: master $(GPGHOME)/DATA-airgapped/paperkey-subkey-sign.txt $(GPGHOME)/DATA-airgapped/paperkey-subkey-auth.txt
125 | $(MAKE) signing
126 | $(MAKE) encryption
127 | $(MAKE) auth
128 |
129 | .PHONY: master
130 | master: $(GPGHOME)/DATA-airgapped/paperkey-master.txt
131 | $(MAKE) encryption
132 |
133 | .PHONY: signing
134 | signing: $(GPGHOME)/DATA-airgapped/subkey-sign.asc
135 |
136 | .PHONY: encryption
137 | encryption: $(GPGHOME)/DATA-airgapped/subkey-encr.asc $(GPGHOME)/DATA-airgapped/paperkey-subkey-encr.txt
138 |
139 | .PHONY: auth
140 | auth: id_rsa.pub
141 |
142 | # reset
143 | reset-yubikey:
144 | @echo "Are you sure you want to reset the yubikey GPG applet? This is a destructive"
145 | @echo "action and not reversible."
146 | @bash -c "read -r -p \"Type 'reset-yubikey' if you're sure you want to continue: \" ANS; \
147 | if [ \$$ANS = 'reset-yubikey' ]; then \
148 | gpg-connect-agent -r yubikey-reset.txt; \
149 | else \
150 | echo 'You entered '\$$ANS', so the card was NOT reset.'; \
151 | fi"
152 |
153 | # clean
154 | .PHONY: clean
155 | clean:
156 | @if [ -d "$(GPGHOME)" ]; then \
157 | echo "Will remove following path: $(GPGHOME)"; \
158 | echo "Continue (y/N)?"; \
159 | read -r REPLY; \
160 | if [ $${REPLY} = 'y' ]; then \
161 | rm -rf "$(GPGHOME)"; \
162 | echo "Done."; \
163 | else \
164 | echo "Aborted."; \
165 | fi \
166 | fi
167 | @if [ $$(ls -1d $$(/bin/date '+%F_')* 2>/dev/null | wc -l) -eq 0 ]; then \
168 | echo "No auto-generated folders to clean."; \
169 | else \
170 | @echo "Will remove following paths:"; \
171 | RMPATHS=$$(/bin/date '+%F_%H'); \
172 | ls -1d $${RMPATHS}*; \
173 | echo "Continue (y/N)?" \
174 | read -r REPLY; \
175 | if [ $${REPLY} = 'y' ]; then \
176 | rm -rf "$${RMPATHS}*"; \
177 | echo "Done."; \
178 | else \
179 | echo "Aborted."; \
180 | fi \
181 | fi
182 |
183 | .PHONY: show
184 | show:
185 | cd $(GNUPGHOME) && \
186 | $(GPGCMD) --list-secret-keys
187 | $(GPGBIN) --card-status
188 |
189 | ###
190 | # master key
191 | ###
192 | $(GNUPGHOME)/private-keys-v1.d: $(GNUPGHOME)/gpg.conf $(GPGHOME)/DATA-airgapped/config
193 | cd $(GNUPGHOME) && \
194 | . $(GPGHOME)/DATA-airgapped/config && \
195 | $(GPGCMD) --quick-generate-key "$${GPG_USER_ID}" "$${MASTER_ALGO}" cert "$${MASTER_VALIDITY}" || :
196 |
197 | $(GPGHOME)/DATA-transferred/keyid-master.txt: $(GNUPGHOME)/private-keys-v1.d
198 | cd $(GNUPGHOME) && \
199 | $(GPGCMD) --list-secret-keys | awk '/^sec/ {gsub("(rsa|elg|dsa|ecdh|ecdsa|eddsa)*[0-9]+R?/", "", $$2); print $$2}' > $(GPGHOME)/DATA-transferred/keyid-master.txt
200 |
201 | $(GPGHOME)/DATA-transferred/keyfp-master.txt: $(GPGHOME)/DATA-transferred/keyid-master.txt
202 | cd $(GNUPGHOME) && \
203 | $(GPGCMD) --list-secret-keys | awk -F= '/Key fingerprint/ {gsub(" ", "", $$2); print $$2}' > $(GPGHOME)/DATA-transferred/keyfp-master.txt
204 |
205 | $(GPGHOME)/DATA-airgapped/key-master.asc: $(GPGHOME)/DATA-transferred/keyfp-master.txt
206 | cd $(GNUPGHOME) \
207 | && $(GPGCMD) --armor --output $(GPGHOME)/DATA-airgapped/key-master.asc --export-secret-keys $(shell head -n1 $(GPGHOME)/DATA-transferred/keyid-master.txt) \
208 | && $(GPGCMD) --armor --output $(GPGHOME)/DATA-transferred/key-master.pub --export $(shell head -n1 $(GPGHOME)/DATA-transferred/keyid-master.txt)
209 |
210 | $(GPGHOME)/DATA-airgapped/paperkey-master.txt: $(GPGHOME)/DATA-airgapped/key-master.asc
211 | cd $(GNUPGHOME) \
212 | && $(GPGCMD) --export-secret-keys | paperkey -o $(GPGHOME)/DATA-airgapped/paperkey-master.txt
213 |
214 | ###
215 | # subkey
216 | ###
217 | $(GPGHOME)/DATA-transferred/subkeyid-$(KEYSET).txt: $(GNUPGHOME)/gpg.conf $(GPGHOME)/DATA-airgapped/config
218 | cd $(GNUPGHOME) \
219 | && . $(GPGHOME)/DATA-airgapped/config \
220 | && $(GPGCMD) --quick-add-key "$(shell head -n1 $(GPGHOME)/DATA-transferred/keyfp-master.txt)" "$${SUBKEY_ALGO}" "$(KEYSET)" "$${SUBKEY_VALIDITY}"
221 | cd $(GNUPGHOME)\
222 | && $(GPGCMD) --list-secret-keys | awk '/^ssb.*\[$(KEYTAG)\]/ {gsub("(rsa|elg|dsa|ecdh|ecdsa|eddsa|ed)*[0-9]+R?/", "", $$2); print $$2}' | head -n1 > $(GPGHOME)/DATA-transferred/subkeyid-$(KEYSET).txt
223 |
224 |
225 | $(GPGHOME)/DATA-airgapped/subkey-$(KEYSET).asc: $(GPGHOME)/DATA-transferred/subkeyid-$(KEYSET).txt
226 | cd $(GNUPGHOME) \
227 | && $(GPGCMD) --armor --output $(GPGHOME)/DATA-airgapped/subkey-$(KEYSET).asc --export-secret-subkeys "$(shell head -n1 $(GPGHOME)/DATA-transferred/subkeyid-$(KEYSET).txt)" \
228 | && $(GPGCMD) --armor --output $(GPGHOME)/DATA-transferred/subkey-$(KEYSET).pub --export "$(shell head -n1 $(GPGHOME)/DATA-transferred/subkeyid-$(KEYSET).txt)"
229 |
230 | $(GPGHOME)/DATA-airgapped/paperkey-subkey-$(KEYSET).txt: $(GPGHOME)/DATA-airgapped/subkey-$(KEYSET).asc
231 | cd $(GNUPGHOME) \
232 | && $(GPGCMD) --export-secret-subkeys "$(shell head -n1 $(GPGHOME)/DATA-transferred/subkeyid-$(KEYSET).txt)" | paperkey -o $(GPGHOME)/DATA-airgapped/paperkey-subkey-$(KEYSET).txt
233 |
234 | .PHONY: id_rsa.pub
235 | id_rsa.pub:
236 | cd $(GNUPGHOME) && \
237 | $(GPGCMD) --list-secret-keys | awk '/\[A\]/ {gsub(".*/", "", $$2); print $$2}' | while read -r SSH_KEY; do \
238 | if [ ! -r $(GPGHOME)/DATA-transferred/id_rsa-$${SSH_KEY}.pub ]; then \
239 | $(GPGCMD) --export-ssh-key "$${SSH_KEY}" > $(GPGHOME)/DATA-transferred/id_rsa-$${SSH_KEY}.pub; \
240 | fi; \
241 | done
242 |
243 | ###
244 | # perform smartcard actions
245 | ###
246 | .PHONY: sckey
247 | sckey: cardedit id_rsa.pub $(GPGHOME)/DATA-transferred/subkey-shadows.asc
248 |
249 | .PHONY: cardedit
250 | cardedit: $(GPGHOME)/DATA-transferred/keyid-master.txt
251 | @echo "==================================================================================="
252 | @echo
253 | @echo "Unfortunately GnuPG 2.1 doesn't yet support automation of the SmartCard functions"
254 | @echo "so you'll have to carry out the following steps manually."
255 | @echo
256 | @echo "To create signing and and authentication keys directly on the SmartCard you'll"
257 | @echo "need the following commands:"
258 | @echo
259 | @echo " 1. 'toggle' to enter admin mode"
260 | @echo
261 | @echo " 2. 'key 1' to select the encryption subkey designated by 'usage: E'. Make sure"
262 | @echo " select the correct subkey, it may be 'key 3' for example."
263 | @echo " 3. 'keytocard' to move the selected encryption key to the card. Follow the"
264 | @echo " instructions to complete this step. When done you should see 'ssb>' next to"
265 | @echo " the key indicating you have a stub locally"
266 | @echo
267 | @echo " 4. 'addcardkey' and follow the instructions to generate a signing key on the card"
268 | @echo " 5. Repeat previous step to create an auth subkey"
269 | @echo
270 | @echo " 6. 'quit' to leave the edit-key mode."
271 | @echo
272 | @echo "If you want to prepare a second SmartCard with the same encryption key you'll"
273 | @echo "have to repeat this step with '$(MAKE) import-secrets sckey' to re-import the encryption"
274 | @echo "key and put it on the second SmartCard."
275 | @echo
276 | @echo "==================================================================================="
277 | cd $(GNUPGHOME) && \
278 | $(GPGCMD) --edit-key "$(shell head -n1 $(GPGHOME)/DATA-transferred/keyid-master.txt)" || :
279 | # There seems to be a bug in at least GPG-2.1.23 that results in occasional
280 | # non-zero exit codes when finishing the --edit-key interaction, so we explicitly
281 | # return true at this point.
282 |
283 | ###
284 | # import encryption key following moving it to a SmartCard
285 | ###
286 | .PHONY: import-secrets
287 | import-secrets: $(GNUPGHOME)/gpg.conf
288 | gpgconf --kill gpg-agent scdaemon
289 | cd $(GNUPGHOME) && \
290 | $(GPGCMD) --batch --delete-secret-and-public-keys "$(shell head -n1 $(GPGHOME)/DATA-transferred/keyid-master.txt)" || :; \
291 | $(GPGCMD) --import $(GPGHOME)/DATA-airgapped/key-master.asc && \
292 | $(GPGCMD) --import $(GPGHOME)/DATA-airgapped/subkey-encr.asc
293 |
294 | ###
295 | # remove master key
296 | ###
297 | .PHONY: remove-master
298 | remove-master: $(GPGHOME)/DATA-transferred/subkey-shadows.asc
299 | cd $(GNUPGHOME) && \
300 | $(GPGCMD) --delete-secret-keys $(shell head -n1 $(GPGHOME)/DATA-transferred/keyid-master.txt) && \
301 | $(GPGCMD) --import $(GPGHOME)/DATA-transferred/subkey-shadows.asc
302 |
303 | ###
304 | # import secret subkeys & link to smartcard
305 | ###
306 |
307 | .PHONY: $(GPGHOME)/DATA-transferred/subkey-shadows.asc
308 | $(GPGHOME)/DATA-transferred/subkey-shadows.asc:
309 | cd $(GNUPGHOME) && \
310 | CARDNO=$(shell $(GPGCMD) --card-status | awk -F: '/^Serial/ {gsub("[ ]+", "", $$2); print $$2}') && \
311 | $(GPGCMD) --export-secret-subkeys --armor > $(GPGHOME)/DATA-transferred/subkey-shadows-$${CARDNO}.asc
312 | $(GPGCMD) --export --armor > $(GPGHOME)/DATA-transferred/subkey-shadows-$${CARDNO}.pub
313 |
314 | .PHONY: import-ssb
315 | import-ssb:
316 | @if [ -z "$(SHADOWS)" ]; then \
317 | echo "ERROR: You must set SHADOWS=/path/to/DATA-transfered/subkey-shadows.asc for this to work."; \
318 | exit 1; \
319 | fi
320 | gpgconf --kill gpg-agent scdaemon
321 | $(GPGBIN) --import $(SHADOWS)
322 | $(GPGBIN) --card-status
323 |
324 | ###
325 | # test
326 | ###
327 | .PHONY: test
328 | test:
329 | gpgconf --kill gpg-agent scdaemon
330 | @echo '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%'
331 | @echo 'Testing that we can encrypt & sign, then decrypt and validate a message.'
332 | @cd $(GNUPGHOME) && \
333 | { echo '🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐🔐'; \
334 | echo 'If you can read this the encryption and decryption have worked!'; \
335 | echo '🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉'; \
336 | } | \
337 | $(GPGCMD) --encrypt --sign -a -r $(shell head -n1 $(GPGHOME)/DATA-transferred/keyfp-master.txt) | \
338 | $(GPGCMD) --decrypt || echo 'FAILED. Check that you have a working pinentry program configured'
339 | @echo '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%'
340 |
341 | ###
342 | # Common, config and other phonies
343 | ###
344 | $(GPGHOME)/DATA-transferred/.keep:
345 | mkdir -m 0700 -p $(GPGHOME)/DATA-transferred
346 | touch "$(GPGHOME)/DATA-transferred/.keep"
347 |
348 | $(GPGHOME)/DATA-airgapped/.keep: $(GPGHOME)/DATA-transferred/.keep
349 | mkdir -m 0700 -p $(GPGHOME)/DATA-airgapped
350 | touch "$(GPGHOME)/DATA-airgapped/.keep"
351 |
352 | $(GNUPGHOME):
353 | gpgconf --kill gpg-agent scdaemon
354 | mkdir -m 0700 -p $(GNUPGHOME)
355 |
356 | $(GPGHOME)/DATA-airgapped/gpg-version: $(GNUPGHOME) $(GPGHOME)/DATA-airgapped/.keep
357 | @if [ $(GPG_VERSION_MAJOR) -ge 2 ] && [ $(GPG_VERSION_MINOR) -ge 1 ]; then \
358 | echo "Found GnuPG $(GPG_VERSION_MAJOR).$(GPG_VERSION_MINOR)"; \
359 | $(GPGBIN) --version > $(GPGHOME)/DATA-airgapped/gpg-version 2>&1; \
360 | else \
361 | echo "GnuPG >= 2.1 is required, found GnuPG $(GPG_VERSION_MAJOR).$(GPG_VERSION_MINOR)"; \
362 | exit 1; \
363 | fi
364 |
365 | $(GNUPGHOME)/gpg-agent.conf:
366 | @if [ $(shell uname) == 'FreeBSD' ] && [ ! -r $(GNUPGHOME)/gpg-agent.conf ]; then \
367 | for PINENTRY in pinentry-gnome3 pinentry-gtk; do \
368 | if [ -x /usr/local/bin/$${PINENTRY} ]; then \
369 | echo "pinentry-program /usr/local/bin/$${PINENTRY}" > $(GNUPGHOME)/gpg-agent.conf; \
370 | break; \
371 | elif [ -x /usr/bin/$${PINENTRY} ]; then \
372 | echo "pinentry-program /usr/local/bin/$${PINENTRY}" > $(GNUPGHOME)/gpg-agent.conf; \
373 | break; \
374 | fi; \
375 | done; \
376 | echo "default-cache-ttl 3600" >> $(GNUPGHOME)/gpg-agent.conf; \
377 | echo "enable-extended-key-format" >> $(GNUPGHOME)/gpg-agent.conf; \
378 | fi
379 |
380 | $(GNUPGHOME)/gpg.conf: $(GPGHOME)/DATA-airgapped/gpg-version $(GNUPGHOME)/gpg-agent.conf
381 | cp gpg.conf-sample $(GNUPGHOME)/gpg.conf
382 |
383 | $(GPGHOME)/DATA-airgapped/config: $(GPGHOME)/DATA-airgapped/gpg-version
384 | if [ ! -f $(GPGHOME)/DATA-airgapped/config ]; then \
385 | cp config-sample $(GPGHOME)/DATA-airgapped/config; \
386 | $(EDITOR) $(GPGHOME)/DATA-airgapped/config; \
387 | fi
388 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Automate GPG keyset creation, optionally on Yubikey(s) and/or SmartCard(s)
2 |
3 | TL;DR: jump to the [Instructions for creating an airgapped key set with two
4 | yubikeys](#scenario1)
5 |
6 | ## Contents
7 |
8 | - [Overview](#overview)
9 | - [What to expect](#what-to-expect)
10 | - [Requirements](#requirements)
11 | - [Usage](#usage)
12 | - [Scenario 1: Instructions for creating an airgapped key set with two yubikeys](#scenario1)
13 | - [Scenario 2: Instructions for creating an airgapped soft key set](#scenario2)
14 | - [Key practices followed in this repository](#key-practices)
15 | - [Using ZFS and EncFS](#zfs)
16 | - [Expired Key Renewals](#renewal)
17 | - [To do](#to-do)
18 | - [Known issues](#known-issues)
19 |
20 | ## Overview
21 |
22 | There are numerous well written guides that describe how to manually generate
23 | a GPG key set, both for soft keys and for Yubikeys or other PGP/GPG
24 | SmartCards. The aim of this work is to automate the GPG commands so you can
25 | generate and create your key set without having to become a GPG veteran.
26 |
27 | There are a couple of scenarios currently supported:
28 |
29 | 1. An offline soft master key, and subkeys stored on one or more smartcards or
30 | yubikeys.
31 | 2. An offline soft master key, and soft subkeys that can be imported into your
32 | main devices.
33 | 3. An offline soft master key, and several sets of soft subkeys, with a common
34 | encryption key to be used across devices.
35 |
36 | You can, of course, choose not to keep your master key offline or airgapped.
37 |
38 | Additionally you can keep your master key on a smartcard to aide with sub key
39 | renewals. If you have a spare smartcard or are willing to purchase one, this
40 | is highly recommended as it massively simplifies renewals and certification of
41 | other keys. See the [instructions on the wiki](https://github.com/jinnko/gpg-smartcard-automation/wiki#using-a-separate-smartcard-for-the-master-certification-key) for how to do this.
42 |
43 | ### What to expect
44 |
45 | The goal is to have a structure as follows for your day to day use. The
46 | master secret key is:
47 |
48 | - generated on an airgapped device
49 | - only stored in cold storage
50 | - only capable of certifying subkeys
51 |
52 | All the private sub keys are either soft keys in your regular ~/.gnupg
53 | keyring, or are stored on a Yubikey or other PGP/GPG smartcard.
54 |
55 | sec# rsa4096 2017-06-26 [C] [expires: 2022-06-25]
56 | D49147668E74D68C108A83DB53E708995F9835CD
57 | uid [ultimate] Real Name (https://keybase.io/userid)
58 | ssb> rsa2048 2017-06-26 [E] [expires: 2019-06-26]
59 | ssb> rsa4096 2017-06-26 [S] [expires: 2019-06-26]
60 | ssb> rsa4096 2017-06-26 [A] [expires: 2019-06-26]
61 | ssb# rsa2048 2017-09-09 [S] [expires: 2019-06-26]
62 | ssb# rsa2048 2017-09-09 [A] [expires: 2019-06-26]
63 |
64 | In the above example we can make the following observations:
65 |
66 | - `#` in `sec#` indicates the private key part of the master key is not
67 | available locally
68 | - `>` in `ssb>` indicates the private key for each subkey is stored on
69 | a PGP/GPG SmartCard or Yubikey.
70 | - `#` in `ssb#` indicates the public key for these subkeys is available. This
71 | would usually be the case if you have the public keys for an additional
72 | smartcard in your keychain.
73 | - `[C]` indicates the key is only capable of certification
74 | - signing (`[S]`), encryption (`[E]`) and authentication (`[A]`) are all
75 | achieved by the corresponding individual subkeys
76 | - The encryption key is 2048 bit so that it can be shared between multiple
77 | smartcards or yubikeys, among which the lowest common support is 2048 bit
78 | keys. If your smartcards support higher sizes you can use the size you
79 | need.
80 | - The signing and authentication keys are 4096 bit as they're generated on the
81 | card at the maximum supported bits for that specific card.
82 |
83 | ## Requirements
84 |
85 | 1. GnuPG >= 2.1
86 | 2. GNU Make
87 | 3. [Paperkey](http://www.jabberwocky.com/software/paperkey/)
88 |
89 | ### Recommended / optional extras
90 |
91 | - Air gapped device. Either a system that is not online, or make use of
92 | a live system image, with all connectivity disabled, possibly after
93 | downloading further requirements.
94 | - One or two PGP/GPG smartcards, such as Yubikeys.
95 | - At least two USB thumb drives to keep your master key safe. You may want to
96 | consider using SLC sticks, and/or using [ZFSonLinux](http://zfsonlinux.org/)
97 | / [O3X](https://openzfsonosx.org/) to keep the keys safe from bitrot and
98 | device failure. For a live OS image, [GhostBSD](http://ghostbsd.org/) works
99 | well, though it didn't boot on my 2013 Macbook Pro, so I used another old PC
100 | laptop.
101 | - An additional USB thumb drive to transfer subkey shadows if using a
102 | SmartCard, or private subkeys if not using a smartcard, and public keys to
103 | your regular device.
104 | - Access to a printer for creating your hard copies of your master key.
105 |
106 | ## Usage
107 |
108 | Use the built-in help for additional targets and usage.
109 |
110 | gmake help
111 |
112 | ## Scenario 1: Instructions for creating an airgapped key set with two yubikeys
113 |
114 | These steps will take you through an example scenario. Adjust as needed.
115 |
116 | ### Checklist
117 |
118 | 1. Two Yubikeys, one is a Neo (max 2048bit keys), and the other a Nano (max
119 | 4096bit keys).
120 | 2. Four USB sticks:
121 | 1. First a USB stick for the live OS image.
122 | 2. Two that will store your secrets offline and should never be used in
123 | a networked device. Ideally these are industrial quality, possibly
124 | SLC.
125 | 3. One for transferring data across the air gap.
126 | 3. GhostBSD live image. At the time of writing, the GhostBSD 11 beta images
127 | worked well, supports ZFS, and has up to date GPG packages. You could also
128 | use any linux live system, such as [Tails](https://tails.boum.org), however
129 | be aware you won't get ZFS with that. See the [Cold storage of the secret
130 | keys and revocation certificates](#cold-storage) section below for why ZFS
131 | would be of value.
132 | 4. Passwords you'll be using to protect your keys, and if you choose to use
133 | EncFS, then also for the encrypted mount.
134 | 5. Printer for the paper keys.
135 |
136 | ### Preparation
137 |
138 | 1. Prepare the USB drives for the OS, storing secrets, and for transferring
139 | keys. If your primary workstation is Linux or Mac and you want to use ZFS,
140 | prepare the sticks before moving to the air gapped system.
141 |
142 | 1. First, prepare your live OS image following the instructions for the
143 | live system you've chosen.
144 |
145 | 2. At least two USB thumb drives for storing your secrets. We'll
146 | refer to these as the *secure USB sticks* as they should ideally only
147 | be used on an air-gapped device. See the section below on [Creating
148 | ZFS pools](#zfs) if that's how you'll be proceeding.
149 |
150 | 3. One USB thumb drive for transferring data between workstation and the
151 | air-gapped device. We'll refer to this as the *transfer USB stick*.
152 | It will contain a copy of this repository, GPG public keys and shadow
153 | keys. No secrets will be stored on this, and any data on here can be
154 | recreated from the *secure USB sticks*. See the section below on
155 | [Creating ZFS pools](#zfs) if that's how you'll be proceeding.
156 |
157 | 3. Clone this repository to the *transfer USB stick*.
158 |
159 | ### Generate the keys
160 |
161 | These steps will prepare two SmartCards with secret subkeys, paperkey backups
162 | that should be printed, and a revocation certificate in case the master key is
163 | damaged or compromised.
164 |
165 | 1. Boot up the air-gapped system.
166 |
167 | 2. Get the live OS network connected, then get all the packages you'll need to
168 | complete this task, and finally disable networking. For GhostBSD these are
169 | the steps:
170 |
171 | 1. Establish a network connection, whether wired or wireless, using the
172 | GUI.
173 | 2. In a terminal, get the necessary packages:
174 |
175 | sudo pkg install gnupg paperkey fusefs-encfs gmake
176 |
177 | 3. Turn off networking:
178 |
179 | sudo service netif stop
180 |
181 | 3. Insert the *transfer USB stick*, mount it, and copy the
182 | gpg-smartcard-automation repository off the USB stick. I recommend *not*
183 | working directly from the stick as I've experienced odd behaviours with the
184 | filesystems when doing this.
185 |
186 | mkdir ~ghostbsd/zpools
187 | sudo zpool import -R ~ghostbsd/zpools airgap-transfers
188 | sudo zpool scrub airgap-transfers
189 | cp -a ~/zpools/airgap-transfers/gpg-smartcard-automation ~/
190 | sudo zpool export airgap-transfers
191 |
192 | 4. Create your keyset:
193 |
194 | 1. Prepare your working environment
195 |
196 | setenv GPGHOME $HOME/gpg-20170903
197 | cd ~/gpg-smartcard-automation
198 |
199 | 2. Insert the first yubikey. If you've not used the key before, or if
200 | you're recently reset it (try `gmake reset-yubikey`), then the default
201 | admin PIN is `12345678`, and regular PIN is `123456`. Remember to change
202 | the default PIN as soon as possible (hint: `gpg --card-edit`).
203 |
204 | gmake default
205 |
206 | You'll be prompted for your password many times. Follow the
207 | instructions in the output to complete all the parts of this step.
208 |
209 | 3. If you'll be using a second yubikey, insert it now. The tooling will
210 | prepare another GPG homedir and keychain to work around a [GnuPG
211 | limitation that will be fixed by T2291](https://dev.gnupg.org/T2291).
212 |
213 | gmake import-secrets sckey
214 |
215 | You can skip this step if you're only preparing a single Yubikey.
216 |
217 | 4. Copy data to USB sticks. A series of helper scripts make this easy for
218 | you.
219 |
220 | 1. Insert first *secure USB stick*, then
221 |
222 | ./copy-secrets-to-usb.sh $GPGHOME secure-1
223 |
224 | In this case, `secure-1` is the pool name for this USB stick. This
225 | scripts assume you're using ZFS and EncFS.
226 |
227 | 2. Repeat step 1 for additional *secure USB sticks*, adjusting the pool
228 | name as appropriate.
229 |
230 | 3. Insert the *transfer USB stick* to copy the data destined for your
231 | main device. As before, the second argument is the ZFS pool name.
232 | In this case there's no EncFS as there's nothing to be hidden here.
233 |
234 | ./copy-to-transfer-usb.sh $GPGHOME airgap-transfers
235 |
236 | 5. Print the paper master key. Ideally this will be via a USB connected
237 | printer rather than something wireless or networked. The file to print
238 | is:
239 |
240 | $(GPGHOME)/DATA-airgapped/paperkey-master.txt
241 |
242 | At this point you're done with the air gapped live OS work!
243 |
244 | 5. Import your new keys to your main devices.
245 |
246 | 1. Insert the *transfer USB stick* into your main device and copy the
247 | `$GPGHOME` path across to your system. For example
248 |
249 | cp -a /Volumes/airgap-transfers/gpg-20170903 ~/ownCloud/
250 |
251 | 2. If you prepared multiple Yubikeys, some files will be suffixed with the
252 | Yubikey serial number, for example
253 | `$GPGHOME/DATA-transferred/subkey-shadows-03821903.asc`. Choose the one
254 | you want to import and pull it in.
255 |
256 | gpg --import < ~/ownCloud/gpg-20170903/DATA-transferred/subkey-shadows-03821903.asc
257 |
258 | 3. Create the link between the imported shadows keys and your Yubikey:
259 |
260 | gpg --card-status
261 |
262 | 4. If you want to use your other Yubikey with your mobile you'll need to
263 | import the other `subkey-shadows-*.asc` file into something like
264 | OpenKeyChain (for Android), then register your Yubikey with the app.
265 |
266 | ### What next
267 |
268 | You may want to do a few further things to strengthen your key and web of
269 | trust:
270 |
271 | - Sign your new key with your old key.
272 |
273 | gpg --local-user 0xOLD_KEY_ID --sign-key NEW_KEY_ID
274 |
275 | - Pull in the public keys for your second set of keys.
276 |
277 | gpg --import /path/to/DATA-transferred/key-master.pub
278 | gpg --import /path/to/DATA-transferred/subkey*.pub
279 |
280 | - Upload your new public key to https://keybase.io
281 |
282 | ## Scenario 2: Instructions for creating an airgapped soft key set
283 |
284 | If you don't have a smartcard, you can generate only soft keys, with the
285 | master key kept in cold storage, and your subkeys available to your every day
286 | devices. The benefit of this is that if your subkeys are compromised for any
287 | reason, your web of trust doesn't break down due to the trust being anchored
288 | to your master key.
289 |
290 | The main difference here is that in step 4.2 of [the first
291 | scenario](#scenario1), rather than running `gmake default`, you need to `gmake
292 | softkeys`. There are some further considerations for copying your secrets
293 | across to the your main device, and those steps will be detailed soon. Short
294 | explanation is that you'll likely want to use an EncFS on your *transfer USB
295 | stick* to keep those secrets less exposed.
296 |
297 | ## Key practices followed in this repository
298 |
299 | ### Offline master key
300 |
301 | The master key is a soft key only capable of certification and should not be
302 | stored on a device used daily. It's a good idea to keep this safe on an
303 | encrypted USB device, and even better to have a print out of it, as is
304 | prepared by paperkey.
305 |
306 | The generated master key backups are stored at:
307 |
308 | - $(GPGHOME)/DATA-airgapped/key-master.asc
309 | - $(GPGHOME)/DATA-airgapped/paperkey-master.txt
310 |
311 | ### Separate signing, encryption and authentication sub keys
312 |
313 | When using Yubikeys or PGP/GPG smartcards, we want to have different signing
314 | and authentication keys on each smartcard so that in the event one is lost we
315 | only need to revoke that single key set of that token, plus the shared
316 | encryption key.
317 |
318 | The use of a shared encryption key is primarily for convenience: to have the
319 | same encryption key used on both smartcards making it easy for others to send
320 | us encrypted messages and not require them to have any knoweldge of our
321 | PGP/GPG key structure.
322 |
323 | For these reasons we generate the encryption key as a soft key that is
324 | transferred to the smartcard, while we generate the signing and authentication
325 | keys directly on the cards.
326 |
327 | If you don't have a smartcard then you can generate all the keys as soft keys
328 | using [scenario 2](#scenario2).
329 |
330 | Storing and keeping the soft keys requires a more prudence than when
331 | using a smartcard.
332 |
333 | ### Cold storage of the secret keys and revocation certificates
334 |
335 | Ideally all your key material will be generated on an air gapped device, and
336 | even better on a live OS so there's no persistence. If you use an ephemeral
337 | system for this, such as [Tails](https://tails.boum.org) or
338 | [GhostBSD](https://ghostbsd.org), you'll need to keep your master key and
339 | revocation certificates on a couple of USB thumb drives.
340 |
341 | You should consider using SLC (Single Layer Cell) USB sticks as they should be
342 | more resilient, and using a filesystem capable of bitrot detection and
343 | recovery, such as ZFS.
344 |
345 | You should also print out the paperkey representations of all private key
346 | material as paper generally lasts a lot longer than USB thumb drives under
347 | normal conditions.
348 |
349 | ## Using ZFS and EncFS
350 |
351 | Using ZFS offers some benefits:
352 |
353 | - Interoperability between unix and macOs. On Linux this is availabe from the
354 | [ZFSonLinux](http://zfsonlinux.org/) project, and on macOS use
355 | [O3X](https://openzfsonosx.org/). BSDs generally come with ZFS by default.
356 |
357 | - Support for multiple copies of the data on a single drive. If your USB
358 | thumbdrive were to experience bitflips/bitrot ZFS will return the correct
359 | data silently, though there are some caveates. Details below.
360 |
361 | Using EncFS on top of ZFS has the benefit of interoperability between unix
362 | and macOS.
363 |
364 | ### Creating ZFS pools
365 |
366 | This section isn't comprehensive as there's enough documentation online,
367 | however here's a quick start to creating a pool with multipe data copies on
368 | a single stick:
369 |
370 | 1. Determine the device for your USB stick
371 |
372 | diskutil list
373 |
374 | Look for the device that corresponds to the characteristics of your stick
375 | and take node of the `/dev/diskX` title.
376 |
377 | 2. Create the new zpool. This is a destructive operation on the USB stick, so
378 | be sure you know what you're doing.
379 |
380 | sudo zpool create -f -o ashift=12 -O copies=3 -O normalization=formD airgap-transfers /dev/diskX
381 |
382 | This will create a volume with 3 copies of your data. To detect bitrot
383 | you'll need to perform scrubs of the disk as ZFS doesn't do this during
384 | normal operation. See
385 | [ZFSonLinux issue 1256](https://github.com/zfsonlinux/zfs/issues/1256)
386 | for more details.
387 |
388 | ### Mounting and unmounting ZFS volumes
389 |
390 | On macOS, if you have O3X installed, when you insert a ZFS USB stick it should
391 | get mounted automatically. If not, the follwing is what you need:
392 |
393 | sudo zpool import POOL_NAME
394 |
395 | You can get a list of available pools by omiting the `POOL_NAME`.
396 |
397 | Before you remove a USB stick from macOS you must *export* it:
398 |
399 | sudo zpool export POOL_NAME
400 |
401 |
402 | ## To doExpired key renewals
403 |
404 | If you're managing multiple smartcards you'll need to follow these steps for each smartcard.
405 |
406 | 1. Ensure you're working from a clean slate
407 |
408 | gpgconf --kill gpg-agent scdaemon
409 | ps -ef | grep gpg
410 |
411 | 2. Set up your working environment
412 |
413 | export GNUPGHOME=/path/to/gpg/conf/path
414 |
415 | 3. Get the primary key ID
416 |
417 | gpg --card-status
418 |
419 | 4. Start GPG key editing
420 |
421 | gpg --edit-key $PRIMARY_KEY
422 |
423 | 5. Select the keys you want to update
424 |
425 | gpg> key 1
426 | gpg> key 4
427 | gpg> key 5
428 |
429 | 6. Update expiry on the keys
430 |
431 | gpg> expire
432 | ...
433 |
434 | 7. Save and exit
435 |
436 | gpg> save
437 |
438 | 8. Export the new subkey shadows
439 |
440 | for key in $subkey1 $subkey2 $subkey3; do
441 | gpg --export-secret-subkeys --export-options export-minimal --armor $key > $key.shadow
442 | done
443 |
444 | Note that the inspected exports will appear to contain the primary key, however GnuPG
445 | essentially supports this `--export-secret-subkeys` as an extension to the PGP standard, and
446 | the primary key in these exports has been rendered unusable.
447 |
448 | 9. Export the new public keys
449 |
450 | gpg --export --export-options export-minimal --armor $PRIMARY_KEY > $GNUPGHOME/keys.pub
451 |
452 | 10. Transfer the exported `*.shadow` files to your main systems and `gpg --import` the ones
453 | relevant to the smartcard you use on that device.
454 |
455 | ## To do
456 |
457 | - Manage key expiry extention. As of GPG 2.1 only master key expiry can be
458 | easily manipulated with --quick-set-expire.
459 | - Add tests to validate all resulting artefacts are as expected, for example
460 | the exported ASCII armored private key and subkeys are well structured and
461 | encrypted, and the SSH public key is exported correctly.
462 | - When GnuPG supports automation of card key generation this will need to be
463 | implemented.
464 | - For some reason a Makefile seemed a good idea at the outset due to the fact
465 | we're creating many files and it should handle idempotence for us. The code
466 | has probably grown beyond that and would benefit from a rewrite in something
467 | more suitable.
468 |
469 | ## Using Raspbian as an airgapped system
470 |
471 | With a Raspberry Pi I wasn't able to get a working ZFS set up, probably because the Pi I'm using
472 | is 32bit, but it could also be due to limited memory.
473 |
474 | ### Quick start
475 |
476 | - Raspberry Pi (tested on a Pi Zero)
477 | - Raspbian (tested with Debian Stretch)
478 | - Install the base system to an SD card
479 | - Install essential packages
480 |
481 | apt-get update
482 | apt-get install paperkey scdaemon
483 |
484 | - You may want to use LUKS encrypted volume, in which case you'll need:
485 |
486 | apt-get install cryptsetup
487 |
488 | - Update & upgrade the system
489 |
490 | apt-get update
491 | apt-get upgrade
492 |
493 | - Copy this repository on to the Raspberry Pi
494 |
495 | ## Known issues
496 |
497 | ### GPG max key size
498 |
499 | GPG has a hard max key size of 4096bit. Apparently the brew version has upped
500 | this to 8192bit. You can try that if you desire, however keep in mind that
501 | most SmartCards only support 2048bit, while a few now support 4096bit.
502 |
503 | ### gpg-agent socket path length limit
504 |
505 | As of GnuPG 2.1 the key handling logic has been entirely moved out of the old
506 | `gpg` binary and into `gpg-agent`. As a result `gpg-agent` will be invoked
507 | automatically as needed, and therefore the `gpg-agent` socket will always be
508 | used for communication between the `gpg` command line interface and the
509 | running `gpg-agent`. Unfortunately sockets on Unix systems have a hard limit
510 | of 108 characters in their path, including macOS. This is not a limitation of
511 | GnuPG but rather of the underlying OS, and `gpg` may fail slightly cryptically
512 | if the path is longer than 108 characters.
513 |
514 | ### GPG only supports one card pointer per shadow/stub key
515 |
516 | This is a [GnuPG limitation that will be fixed by T2291](https://dev.gnupg.org/T2291).
517 | Unfortunately this didn't make it into the GnuPG-2.2. We work around this limitation
518 | by generating seaprate $GNUPGHOME folders for each smartcard being used. You then
519 | only import the main one you would use day to day on a given device, but if the
520 | need arises you're still able to switch to using the other smartcard by deleting
521 | your key shadows/stubs (hint: `gpg --delete-secret-and-public-keys 0xKEY_ID`), then
522 | re-importing the other shadows/stubs and registering them with the `gpg --card-status`
523 | command.
524 |
--------------------------------------------------------------------------------
/config-sample:
--------------------------------------------------------------------------------
1 | #
2 | # This configuration will be used when generating your keys
3 | #
4 |
5 | ###
6 | # User ID
7 | ###
8 | GPG_REAL_NAME="Real Name"
9 | GPG_COMMENT="https://keybase.io/userid"
10 | GPG_EMAIL="email@addr.ess"
11 |
12 | # Use the appropriate GPG_USER_ID line below as needed depending on which
13 | # combination of above values is set.
14 |
15 | #GPG_USER_ID="${GPG_EMAIL}"
16 | #GPG_USER_ID="${GPG_REAL_NAME} <${GPG_EMAIL}>"
17 | GPG_USER_ID="${GPG_REAL_NAME} (${GPG_COMMENT}) <${GPG_EMAIL}>"
18 |
19 | ###
20 | # Key parameters
21 | ###
22 | MASTER_ALGO="rsa4096"
23 | MASTER_VALIDITY="5y"
24 |
25 | SUBKEY_ALGO="rsa2048"
26 | SUBKEY_VALIDITY="3m"
27 |
--------------------------------------------------------------------------------
/copy-secrets-to-usb.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | GPGHOME="${1%\/}"
4 | POOL="$2"
5 |
6 | usage() {
7 | echo "Usage: $(basename $0) GPGHOME ZFS_POOL"
8 | echo " GPGHOME Path to the GPG working directory"
9 | echo " ZFS_POOL The pool name to work with"
10 | exit 1
11 | }
12 | [ -n "$GPGHOME" ] || usage
13 | [ -n "$POOL" ] || usage
14 |
15 | set -eux
16 |
17 | [ -r ${HOME}/encfs ] || mkdir ${HOME}/encfs
18 |
19 | sudo zpool import -R ${HOME}/zpools $POOL
20 | sudo zpool scrub $POOL
21 | sudo encfs --public ${HOME}/zpools/$POOL ${HOME}/encfs
22 | sudo chown -R ${USER}: ${HOME}/encfs
23 | rsync -ca --progress --stats --exclude="S.gpg-agent*" --exclude="S.scdaemon" $GPGHOME ${HOME}/encfs/
24 | rsync -ca --delete-before --progress --stats ${HOME}/gpg-smartcard-automation ~/zpools/${POOL}/
25 | sudo umount ${HOME}/encfs
26 | sudo zpool scrub $POOL
27 | sudo zpool export ${POOL}
28 |
--------------------------------------------------------------------------------
/copy-to-transfer-usb.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -x
4 |
5 | GPGHOME="${1%\/}"
6 | POOL="$2"
7 |
8 | usage() {
9 | echo "Usage: $(basename $0) GPGHOME ZFS_POOL"
10 | echo " GPGHOME Path to the GPG working directory"
11 | echo " ZFS_POOL The pool name to work with"
12 | exit 1
13 | }
14 | [ -n "$GPGHOME" ] || usage
15 | [ -n "$POOL" ] || usage
16 |
17 | set -eu
18 |
19 | GPGDIR=$(basename ${GPGHOME})
20 |
21 | sudo zpool import -R ${HOME}/zpools ${POOL}
22 | sudo zpool scrub ${POOL}
23 |
24 | sudo chown -R ${USER}: ${HOME}/zpools/${POOL}
25 | rsync -ca --progress --stats --exclude="S.gpg-agent*" --exclude="S.scdaemon" ${GPGHOME}/DATA-transferred ${HOME}/zpools/${POOL}/${GPGDIR}
26 | rsync -ca --delete-before --progress --stats ${HOME}/gpg-smartcard-automation ${HOME}/zpools/${POOL}/
27 |
28 | sudo zpool scrub ${POOL}
29 | sudo zpool export ${POOL}
30 |
--------------------------------------------------------------------------------
/export-public-keys-and-shadows.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eE
4 | set -x
5 |
6 | PRIMARY_KEY=$(cat DATA-transferred/keyid-master.txt)
7 |
8 | for gnupg in gnupg-0{4,7}*; do
9 | gpgconf --kill gpg-agent scdaemon
10 | export GNUPGHOME=$(readlink -f "$gnupg")
11 | CARD_ID=${gnupg/gnupg-/}
12 |
13 | # Export public keys
14 | gpg --export --export-options export-minimal --armor \
15 | "$PRIMARY_KEY" \
16 | > "DATA-transferred/public-$CARD_ID-$(date -I).asc"
17 |
18 | # Export subkey shadows
19 | gpg --export-secret-subkeys --export-options export-minimal --armor \
20 | > "DATA-transferred/subkey-shadows-$CARD_ID-$(date -I).asc"
21 | done
22 |
23 | gpgconf --kill gpg-agent scdaemon
24 | unset GNUPGHOME
25 |
--------------------------------------------------------------------------------
/genkey-signing+encryption.conf-sample:
--------------------------------------------------------------------------------
1 | # This is the config file that will be used to generate your private keys.
2 | # Documentation on this file can be found at:
3 | # https://www.gnupg.org/documentation/manuals/gnupg-devel/Unattended-GPG-key-generation.html
4 |
5 | # Key-Type must come first. Only edit if you know what you're doing.
6 | Key-Type: RSA
7 |
8 | # BEGIN MAIN EDITABLE SECTION
9 | Name-Real: NAME
10 | Name-Comment: COMMENT
11 | Name-Email: EMAIL
12 | Key-Length: 2048
13 | Subkey-Length: 2048
14 | Expire-Date: 730
15 | # END MAIN EDITABLE SECTION
16 |
17 | # Other settings to get the right type of keys generated
18 | Key-Usage: sign
19 | Subkey-Type: RSA
20 | Subkey-Usage: encrypt
21 | Preferences: SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
22 |
23 | # FINISH
24 | %commit
25 | %echo done
26 |
--------------------------------------------------------------------------------
/genkey-signing.conf-sample:
--------------------------------------------------------------------------------
1 | # This is the config file that will be used to generate your private keys.
2 | # Documentation on this file can be found at:
3 | # https://www.gnupg.org/documentation/manuals/gnupg-devel/Unattended-GPG-key-generation.html
4 |
5 | # Key-Type must come first. Only edit if you know what you're doing.
6 | Key-Type: RSA
7 |
8 | # BEGIN MAIN EDITABLE SECTION
9 | Name-Real: NAME
10 | Name-Comment: COMMENT
11 | Name-Email: EMAIL
12 | Key-Length: 2048
13 | Expire-Date: 730
14 | # END MAIN EDITABLE SECTION
15 |
16 | # Other settings to get the right type of keys generated
17 | Key-Usage: sign
18 | Preferences: SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
19 |
20 | # FINISH
21 | %commit
22 | %echo done
23 |
--------------------------------------------------------------------------------
/gpg.conf-sample:
--------------------------------------------------------------------------------
1 | use-agent
2 | personal-cipher-preferences AES256 AES192 AES CAST5
3 | personal-digest-preferences SHA512 SHA384 SHA256 SHA224
4 | default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
5 | digest-algo SHA512
6 | cipher-algo AES256
7 | cert-digest-algo SHA512
8 | s2k-digest-algo SHA512
9 | s2k-cipher-algo AES256
10 | charset utf-8
11 | fixed-list-mode
12 | no-comments
13 | no-emit-version
14 | keyid-format 0xlong
15 | list-options show-uid-validity
16 | verify-options show-uid-validity
17 | with-fingerprint
18 | with-subkey-fingerprint
19 |
--------------------------------------------------------------------------------
/yubikey-reset.txt:
--------------------------------------------------------------------------------
1 | /hex
2 | scd serialno
3 | scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
4 | scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
5 | scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
6 | scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
7 | scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
8 | scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
9 | scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
10 | scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
11 | scd apdu 00 e6 00 00
12 | scd apdu 00 44 00 00
13 | /echo Card has been successfully reset.
14 | /bye
15 |
--------------------------------------------------------------------------------