Add a tutorial for using SSH keys with `derivepassphrase vault`
Marco Ricci

Marco Ricci commited on 2026-03-08 22:46:41
Zeige 10 geänderte Dateien mit 524 Einfügungen und 5 Löschungen.


The tutorial builds on the previous tutorial for setting up
`derivepassphrase vault` with a master passphrase, modifying the
existing configuration to use a master SSH key instead.  It covers SSH
agent installation, key generation, and reconfiguring `derivepassphrase
vault`.  Both tutorials link to each other.

The other tutorial (for setting up `derivepassphrase vault` with
a master passphrase) now also contains a short note on shell prompts as
well as operating system-specific instructions for the `pip` install
method.

For reproducibility, the new tutorial uses the standard Ed25519 SSH test
key as the master SSH key.  The test key is explicitly linked.  The
tutorial also includes a copy of the Pageant icon to help the reader
identify the correct icon in The Annoying OS's task bar.
... ...
@@ -0,0 +1,7 @@
1
+-----BEGIN OPENSSH PRIVATE KEY-----
2
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
3
+QyNTUxOQAAACCBeIFoJtYCSF8P/zJIb+TBMIncHGpFBgnpCQ/7whJpdgAAAKDweO7H8Hju
4
+xwAAAAtzc2gtZWQyNTUxOQAAACCBeIFoJtYCSF8P/zJIb+TBMIncHGpFBgnpCQ/7whJpdg
5
+AAAEAbM/A869nkWZbe2tp3Dm/L6gitvmpH/aRZt8sBII3ExYF4gWgm1gJIXw//Mkhv5MEw
6
+idwcakUGCekJD/vCEml2AAAAG3Rlc3Qga2V5IHdpdGhvdXQgcGFzc3BocmFzZQEC
7
+-----END OPENSSH PRIVATE KEY-----
... ...
@@ -0,0 +1,9 @@
1
+PuTTY-User-Key-File-2: ssh-ed25519
2
+Encryption: none
3
+Comment: test key without passphrase
4
+Public-Lines: 2
5
+AAAAC3NzaC1lZDI1NTE5AAAAIIF4gWgm1gJIXw//Mkhv5MEwidwcakUGCekJD/vC
6
+Eml2
7
+Private-Lines: 1
8
+AAAAIBsz8Dzr2eRZlt7a2ncOb8vqCK2+akf9pFm3ywEgjcTF
9
+Private-MAC: 38d6314253f27ccd4ee096a1396240d1f565a6dd
... ...
@@ -0,0 +1,38 @@
1
+-----BEGIN OPENSSH PRIVATE KEY-----
2
+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
38
+-----END OPENSSH PRIVATE KEY-----
... ...
@@ -0,0 +1,36 @@
1
+PuTTY-User-Key-File-2: ssh-rsa
2
+Encryption: none
3
+Comment: test key without passphrase
4
+Public-Lines: 9
5
+AAAAB3NzaC1yc2EAAAADAQABAAABgQCxoe7pezhxWy4NI0mUwKqg9WCYOAS+IjxN
6
+9eYcqpfcmQiojcuy9XsiN/xYJ1O94SrsKS5mEia2xHnYA4RUChTyYNcM2v6cnnBQ
7
+/N/VQhpGMN7SVxdbhKUXTWFCwbjBgO6rGyHB6WtoH8vd7TOEPt+NgcXwhsWyoaUU
8
+dYTA62V+GF9vEmxMaC4ubgDz+B0QkPnauSoNxmkhcIe0lsLNb1pClZyz88PDnKXC
9
+X/d0HuN/HJ+sbPg7dCvOyqFYSyKn3uY6bCXqoIdurxXzH3O7z0P8f5sbmKOrGGKN
10
+uNxVRbeVl/D/3uDL0nqsbfUc1qvkfwbJwtMXC4IV6kOZMSk2BAsqh7x48gQ+rhYe
11
+EVSi8F3CWs4HJQoqrGt7K9a3mCSlMBHP70u3w6ME7eumoryxlUofewTd17ZEkzdX
12
+08l2ZlKzZvwQUrc+xQZ2Uw8z2mfW6Ti4gi0pYGaig7Ke4PwuXpo/C5YAWfeXycsv
13
+JZ2uaYRjMdZeJGNAnHLUGLkBscw5aI8=
14
+Private-Lines: 21
15
+AAABf3DVWE/jpuMffkiLXM+WzgchiDo8llB/H/a0Y56+SC7sGmyA4LxWlRvsTNTt
16
+YVgA6szJ0hHJJx0ZE2BLKmD8pALcRb4zhRIe9OqkVCfn1WZKkRhGiN6FslikzoXA
17
+iGCJrTjb29Czr/x0Nh9gFwqHqo0k9aq/8UrpYwbXAfLsMbNxKmjtclO0KGaQ3DF/
18
+aZR+0ZnPZzX2XIK55LT5NnXl9vmPwObc3VYBtSHqmKeggTCzTLoS9Dj3Q3oYAaA7
19
+o8fq4ZGdYKaNcHFCyDPeDS+1UhwT19C4IfU/t49R1T/mJzAL4Xzr33LS5WydOvk2
20
+BkQDLhdizwgGTP5tBRMgLq0YFsQIy15vuJKdhgCV5cPNnXg5EPj/HBzU+rPWR0eW
21
+Est5aWd+efeGTDi0fCqlb4YCRheovdNfQYhfdUg8YxPrKenTRnzfFvuuLR3Q79mt
22
+DLKPvEI8se7E6YvvhIy1PCMu9/3uAfKm1zMoXsgP1FH4DLTSkoYerC5aw/YdkpvU
23
+RU7hAAAAwQDw1i+IdcvPV/3u/q9O/kzLpKO3tbT89sc1zhjZsDNjDAGluNr6n38i
24
+q/XYRZu7UTL9BG+EgFVfIUV7XsYT5e+BPf13VS94rzZ7maCsOlULX+VdMO2zBucH
25
+Ioec9RUlRZrfB21B2W7YGMhbpoa5lN3lKJQ7afHodXZUMp0cTFbOmbzJgSzO2/NE
26
+7BhVwmvcUzTDJGMMKuxBO6w99YKDKRKm0PNLFDz26rWm9LdNS2MVfVuPMTpzT26H
27
+QG4pFageq9cAAADBALzRBXdZF8kbSBa5MTUBVTTzgKQm1C772gJ8T01DJEXZsVtO
28
+v7mUC1/m/by6Hk4tPyvDBuGj9hHq4N7dPqGutHb1q5n0ADuoQjRW7BXw5QvC2EAD
29
+91xexdorIA5BgXU+qltBqzzBVzVtF7+jOZOjfzOlaTX9I5I5veyeTaTxZj1XXUzi
30
+btBNdMEJJp7ifucYmoYAAwE7K+VlWagDEK2y8Mte9y9E+N0uO2j+h85sQt/UIb2i
31
+E/vhcgBgp6142WnSCQAAAMAmHe7tZ+eOGEplhsBbhOw81cMysHT+3vHIBCAWaSjs
32
+l8RLKslIuDVw75Aw6IOAPvO5D1FKk0gp0+ycidrml6H3v2ZWNvYeCeKSPIkQKYGk
33
+t3BkqFfsyci+ArDmWdBcdugS1YJrU3nyXt+yvPBuL2kVS5Sn11KFc19+aVQKMNGx
34
+UtsrkLr6JohDIJYVgryZzp2k2Qig8p/5eD4QkIgVCghOp67fQZzX4z591lzaCGrR
35
+vpcu2GH+4cSvLxvrwzkqCUw=
36
+Private-MAC: 463c0253f0af2de8b3430834632c21a3d9baf4b8
... ...
@@ -40,13 +40,27 @@ We will assume the following three services with the following passphrase polici
40 40
 
41 41
 ## Installing `derivepassphrase`
42 42
 
43
+??? note "Note: Shell Notation"
44
+
45
+    `derivepassphrase` is a command-line application: it runs in the system shell, such as `/bin/sh` on UNIX and Powershell on Windows.
46
+
47
+    In the following shell session transcripts, `$` and '>' denote the <b>prompt</b> from the system shell for user input.
48
+    Type in *the remainder* of line, but not the prompt itself.
49
+    Other lines are *output lines*, which should appear on your shell.
50
+
51
+    For Windows-specific commands, we use the `PS>` prompt; otherwise, we use UNIX-style `$` and `>` prompts.
52
+
43 53
 You will need Python 3, and a package installer such as `pip` (bundled with Python), `pipx`, `uv`, etc.
44 54
 
45 55
 ---
46 56
 
47 57
 === "pip"
48 58
 
49
-    With `pip`, using a "virtual enviroment" at `~/.venv` to avoid clobbering our system configuration:
59
+    With `pip`, using a "virtual enviroment" in a user-specific configuration directory to avoid clobbering our system configuration:
60
+
61
+    === "UNIX (and Cygwin/MSYS, WSL, and Git for Windows)"
62
+
63
+        The configuration directory is `.venv` within our home directory, i.e., `~/.venv`.
50 64
 
51 65
         ~~~~ shell-session
52 66
         $ python3 -m venv ~/.venv
... ...
@@ -54,6 +68,19 @@ You will need Python 3, and a package installer such as `pip` (bundled with Pyth
54 68
         $ pip install derivepassphrase
55 69
         ~~~~
56 70
 
71
+        If the `pip` step complains that there is no such command, use `python3 -m pip install derivepassphrase` instead.
72
+
73
+    === "Windows"
74
+
75
+        The configuration directory is `.venv` within our application data directory, i.e., `C:\Users\<name>\AppData\Roaming\.venv`.
76
+
77
+        ~~~~ pwsh-session
78
+        PS> python -m venv %APPDATA%\.venv
79
+        PS> & %APPDATA%\.venv\Scripts\Activate.ps1
80
+        PS> python -m pip install derivepassphrase
81
+
82
+    Note that `derivepassphrase` will only be available in shell sessions where the virtual environment has been activated (in step 2 above).
83
+
57 84
 === "pipx"
58 85
 
59 86
     ~~~~ shell-session
... ...
@@ -207,7 +234,7 @@ r?9\XQR&
207 234
 Then we attempt to set the work passphrase to `r?9\XQR&`… but our employer's identity management system returns an error: `illegal character: &`.
208 235
 What happened?
209 236
 
210
-### Complication 1: What is a (permitted) "special character"?
237
+### Complication 1: What is a (permitted) "special character"?  {#special-character}
211 238
 
212 239
 `derivepassphrase` considers the characters `!"#$%&'()*+,./:;<=>?@[\]^{|}~-_'` to be permitted special characters.
213 240
 Other service providers may permit other characters (quite rare) or fewer characters (quite common).
... ...
@@ -229,7 +256,7 @@ it90-HPO
229 256
 This works.
230 257
 For now.
231 258
 
232
-### Complication 2: How to implement passphrase rotation?
259
+### Complication 2: How to implement passphrase rotation?  {#passphrase-rotation}
233 260
 
234 261
 `derivepassphrase` can only ever derive one passphrase per configuration, so passphrase rotation cannot be accomplished by reusing the same configuration.
235 262
 So some part of the configuration---generally the service name---needs to change upon each rotation.
... ...
@@ -274,3 +301,7 @@ Passphrase: [[I am an insecure master passphrase, but easy to type.]]
274 301
 ~~~~
275 302
 
276 303
 This completes the tutorial.
304
+
305
+As a next step, you may want to [configure the accounts to use a master SSH key instead][BASIC_SETUP_SSH_KEY].
306
+
307
+[BASIC_SETUP_SSH_KEY]: basic-setup-ssh-key.md "Tutorial: Using a master SSH key with derivepassphrase vault on existing accounts"
... ...
@@ -0,0 +1,394 @@
1
+# Using a master SSH key with `derivepassphrase vault` on existing accounts
2
+
3
+!!! abstract "See also"
4
+
5
+    → [Tutorial: Setting up `derivepassphrase vault` for three accounts, with a master passphrase][BASIC_SETUP_PASSPHRASE]
6
+
7
+    → Tradeoffs between a master passphrase and a master SSH key (TODO)
8
+
9
+## The scenario
10
+
11
+This tutorial builds upon the previous [tutorial for setting up `derivepassphrase vault` for three accounts, with a master passphrase][BASIC_SETUP_PASSPHRASE].
12
+We have a working `derivepassphrase` installation, and a `derivepassphrase vault` configuration for three services `email`, `bank` and `work`, using a master passphrase.
13
+
14
+## Installing `derivepassphrase` with SSH key support
15
+
16
+??? note "Note: Shell Notation"
17
+
18
+    `derivepassphrase` is a command-line application: it runs in the system shell, such as `/bin/sh` on UNIX and Powershell on Windows.
19
+
20
+    In the following shell session transcripts, `$` and '>' denote the <b>prompt</b> from the system shell for user input.
21
+    Type in *the remainder* of line, but not the prompt itself.
22
+    Other lines are *output lines*, which should appear on your shell.
23
+
24
+    For Windows-specific commands, we use the `PS>` prompt; otherwise, we use UNIX-style `$` and `>` prompts.
25
+
26
+[You have already installed `derivepassphrase`.](basic-setup-passphrase.md#installing-derivepassphrase)
27
+Once again, check that the installation was successful.
28
+
29
+~~~~ shell-session
30
+$ devirepassphrase vault --version
31
+derivepassphrase 0.5
32
+Using cryptography 44.0.0
33
+Using click 8.1.8
34
+
35
+Supported features: master SSH key.
36
+~~~~
37
+
38
+(…or similar output.)
39
+Furthermore, verify that "master SSH key" is among the listed supported features.[^no-support]
40
+Without support for SSH keys, this tutorial cannot be completed.
41
+
42
+[^no-support]: If "master SSH key" is not listed as a supported feature, then this `derivepassphrase` installation cannot use master SSH keys.
43
+    Sorry.
44
+    This likely means that we cannot talk to the SSH agent because it uses a communication channel that we cannot or don't know how to access.
45
+
46
+## Setting up an SSH agent and SSH key generator
47
+
48
+`derivepassphrase` cannot generate SSH keys or do SSH key operations itself; instead, it relies on widespread and well-tested third-party software such as OpenSSH or PuTTY for this purpose.
49
+We need to install such software as well.
50
+
51
+=== "UNIX (and Cygwin/MSYS, WSL, and Git for Windows)"
52
+
53
+    You likely already have OpenSSH installed, or can easily install them via your package manager or via [the official OpenSSH distribution][OPENSSH].
54
+    We only need access to the `ssh-agent`, `ssh-add` and `ssh-keygen` client tools; in particular, we do not need the OpenSSH server.
55
+
56
+    ~~~~ shell-session title="Getting the version number of the installed OpenSSH client tools"
57
+    $ ssh -V
58
+    OpenSSH_10.2p1, OpenSSL 3.5.4 30 Sep 2025
59
+    ~~~~
60
+
61
+    Start an SSH agent if none is running yet.
62
+    (Some desktop environments automatically launch an agent on startup.)
63
+    We can check this via `ssh-add -l`.
64
+
65
+    === "Could not open a connection…"
66
+
67
+        ~~~~ shell-session
68
+        $ ssh-add -l
69
+        Could not open a connection to your authentication agent.
70
+        ~~~~
71
+
72
+        The agent is not running.
73
+        So start an agent manually:
74
+
75
+        ~~~~ shell-session
76
+        $ eval "$(ssh-agent -s)"
77
+        ~~~~
78
+
79
+        and arrange for it to be shut down upon exiting this shell session:
80
+
81
+        ~~~~ shell-session
82
+        $ trap "kill $SSH_AGENT_PID" 0
83
+        ~~~~
84
+
85
+    === "The agent has no identities."
86
+
87
+        ~~~~ shell-session
88
+        $ ssh-add -l
89
+        The agent has no identities.
90
+        ~~~~
91
+
92
+        The agent is already running.
93
+
94
+    === "(numbers, random characters, …)"
95
+
96
+        ~~~~ shell-session
97
+        $ ssh-add -l
98
+        256 SHA256:0h+WAokssfhzfzVyuMLJlIcWyCtk5WiXI8BHyhXYxC0  (ED25519)
99
+        3072 SHA256:1OHE0HrVlaSzJn2aQXQIKRu0tfO1CEMefy95K2Bt0xA  (RSA)
100
+        ~~~~
101
+
102
+        (… or similar output, perhaps with text.)
103
+
104
+        The agent is running, and already contains some keys.
105
+
106
+=== "Windows"
107
+
108
+    <div style="float: right;">
109
+
110
+    <figure markdown>
111
+    ![A CRT monitor wearing a spy hat.][PAGEANT_ICON]{ width="96" }
112
+    <figcaption>
113
+    [The `pageant` icon][PUTTY_ICON_HISTORY]
114
+    </figcaption>
115
+    </figure>
116
+
117
+    </div>
118
+
119
+    Install PuTTY e.g. from [the official PuTTY distribution][PUTTY] website, or from the Microsoft Store (if supported).
120
+    We only need access to the `pageant` and `puttygen` tools.
121
+
122
+    Start `pageant` if it isn't running yet.
123
+    An icon of a CRT computer monitor wearing a black hat should appear in the task bar.
124
+
125
+[PAGEANT_ICON]: pageant.svg
126
+[PUTTY_ICON_HISTORY]: https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/putty-icons/
127
+
128
+## Generating a master SSH key, and loading it into the agent
129
+
130
+!!! abstract "Further reading"
131
+
132
+    → [Prerequisites for the SSH key, for use with `derivepassphrase
133
+    vault`][PREREQ_SSH_KEY]
134
+
135
+??? warning "Operational risk: reusing &quot;login&quot; SSH keys for passphrase derivation"
136
+
137
+    SSH keys are typically used as access tokens for logging in on a remote system.
138
+    You are **discouraged** from reusing such an existing "login" key for passphrase derivation.
139
+    A master SSH key is a **long-lived secret**, and should **never need to be rotated, unless compromised or lost**.
140
+    Rotating a master SSH key means **forcibly changing all passphrases that are derived from this key**.
141
+    By contrast, a login SSH key is an access token, and may be rotated for other policy-related reasons (e.g. "upgrades" to a different algorithm, consolidation of multiple keys into a smaller set of new keys, or artificially introduced key expiry).
142
+
143
+We generate a new Ed25519-type key for use with `derivepassphrase`.
144
+
145
+=== "UNIX (and Cygwin/MSYS, WSL, and Git for Windows)"
146
+
147
+    We store the key as `my-vault-ed25519-key` in `~/.ssh`, using the comment "vault key".
148
+
149
+    ~~~~ shell-session
150
+    $ ssh-keygen -t ed25519 -f ~/.ssh/my-vault-ed25519-key -C "vault key"
151
+    Generating public/private ed25519 key pair.
152
+    Enter passphrase for ".../.ssh/my-vault-ed25519-key" (empty for no passphrase): 
153
+    Enter same passphrase again: 
154
+    Your identification has been saved in .../.ssh/my-vault-ed25519-key
155
+    Your public key has been saved in .../.ssh/my-vault-ed25519-key.pub
156
+    The key fingerprint is:
157
+    SHA256:0h+WAokssfhzfzVyuMLJlIcWyCtk5WiXI8BHyhXYxC0 vault key
158
+    The key's randomart image is:
159
+    +--[ED25519 256]--+
160
+    |o B=+            |
161
+    |.=oE = .         |
162
+    |.oX @ +          |
163
+    | = + o * . .     |
164
+    |  + o * S B      |
165
+    |   + * + O o     |
166
+    |      * o .      |
167
+    |       o         |
168
+    |                 |
169
+    +----[SHA256]-----+
170
+    ~~~~
171
+
172
+=== "Windows"
173
+
174
+    Start `puttygen`.
175
+    Under "Parameters", as "Type of key to generate", select **EdDSA**, and as "Curve to use for generating this key", select **Ed25519 (256 bits)**.
176
+    Then, under "Actions", select "Generate", and follow the on-screen instructions.
177
+    Set the comment to "vault key", and set a strong key passphrase (*recommended*).
178
+    Finally, select "Save private key", and store the key as `my-vault-ed25519-key.ppk` in `My Documents`.
179
+    We can now close `puttygen`.
180
+
181
+<section id="sample-key" markdown>
182
+
183
+!!! note "Note: reproducibility"
184
+
185
+    **SSH key generation is non-reproducible**: your key will naturally differ from ours, as will the key fingerprint, the randomart image (if any), and -- in general, for sufficiently loose constraints -- the passphrases derived from this master SSH key.
186
+    This is *intentional* -- you wouldn't want others to be able to access all of your passphrases just because they installed the same software as you and have access to, or can guess, your configuration.
187
+
188
+    This also means that you will get **different derived passphrases from us** unless you use [exactly the same master SSH key as we are using (OpenSSH format, no key passphrase)][TEST_KEY_OPENSSH] [(PuTTY format, no key passphrase)][TEST_KEY_PUTTY].[^test-key-comment]
189
+
190
+[TEST_KEY_OPENSSH]: ../test_key_ed25519
191
+[TEST_KEY_PUTTY]: ../test_key_ed25519.ppk
192
+
193
+[^test-key-comment]:
194
+    Technically, we are lying about the key comment you will be seeing when using our test key.
195
+
196
+</section>
197
+
198
+We then need to load the key into the agent, so that `derivepassphrase` can interact with it.
199
+The key will persist as long as the agent is running, and will need to be re-added the next time the agent is started.
200
+
201
+=== "UNIX (and Cygwin/MSYS, WSL, and Git for Windows)"
202
+
203
+    We instruct the agent to pop up a confirmation prompt each time the key is used, as a safety precaution.
204
+
205
+    ~~~~ shell-session
206
+    $ ssh-add -c ~/.ssh/my-vault-ed25519-key
207
+    Enter passphrase for .../.ssh/my-vault-ed25519-key (will confirm each use):
208
+    Identity added: .../.ssh/my-vault-ed25519-key (vault key)
209
+    The user must confirm each use of the key
210
+    ~~~~
211
+
212
+=== "Windows"
213
+
214
+    Right-click on the `pageant` icon (the CRT computer monitor with the black hat) in the Windows task bar, then select "Add key (encrypted)".
215
+    Select `my-vault-ed25519-key.ppk` in `My Documents`.
216
+    The key should now be loaded.
217
+    Double-click on the `pageant` icon, or right-click and then select "View keys", to bring up the list of keys `pageant` is currently holding in memory.
218
+    The Ed25519 key we just created should be listed there, along with the "vault key" comment.
219
+
220
+    Upon first use of the key, `pageant` will issue a passphrase prompt for the key passphrase.
221
+    The key will be unlocked, and thus usable without prompts, until it is re-encrypted in the "View keys" menu.
222
+
223
+## Reconfiguring the accounts
224
+
225
+??? note "Reminder: interactive input"
226
+
227
+    In code listings, sections enclosed in `[[...]]` signify input to the program, for you to type or paste in.
228
+
229
+    Also, it is normal for passphrase prompts to not "echo" the text you type in.
230
+
231
+In the previous tutorial that set up the three accounts, [we stored the settings for each account to `derivepassphrase vault`'s configuration](basic-setup-passphrase.md#summary), meaning that we only have to enter the master passphrase to access the account passphrases.
232
+
233
+~~~~ shell-session
234
+$ derivepassphrase vault --export -  # to confirm the configuration
235
+{"services": {"email": {"length": 12, "repeat": 3, "lower": 1, "upper": 1, "number": 1, "space": 0}, "bank": {"length": 5, "lower": 0, "upper": 0, "number": 5, "space": 0, "dash": 0, "symbol": 0}, "work-2024Q4": {"length": 8, "upper": 1, "number": 1, "space": 0, "dash": 1, "symbol": 0}}}
236
+~~~~
237
+
238
+~~~~ shell-session
239
+$ derivepassphrase vault -p email
240
+Passphrase: [[I am an insecure master passphrase, but easy to type.]]
241
+kEFwoD=C?@+7
242
+$ derivepassphrase vault -p bank
243
+Passphrase: [[I am an insecure master passphrase, but easy to type.]]
244
+98517
245
+$ derivepassphrase vault -p work-2024Q4
246
+Passphrase: [[I am an insecure master passphrase, but easy to type.]]
247
+-P268G0A
248
+~~~~
249
+
250
+We first reconfigure the `email` account to use the master SSH key.
251
+`derivepassphrase` presents us with a key selector for all SSH keys suitable for passphrase derivation, including the one we generated earlier.
252
+
253
+=== "Only one suitable key"
254
+
255
+    We confirm the selection.
256
+
257
+    ~~~~ shell-session
258
+    $ derivepassphrase vault --config -k email
259
+    Suitable SSH keys:
260
+    [1] ssh-ed25519 ...gm1gJIXw//Mkhv5MEwidwcakUGCekJD/vCEml2  vault key
261
+    Use this key? [[yes]]
262
+    ~~~~
263
+
264
+=== "Multiple suitable keys"
265
+
266
+    We choose the correct key.
267
+
268
+    ~~~~ shell-session
269
+    $ derivepassphrase vault --config -k email
270
+    Suitable SSH keys:
271
+    [1] ssh-ed25519 ...gm1gJIXw//Mkhv5MEwidwcakUGCekJD/vCEml2  vault key
272
+    [2] ssh-rsa ...YAWfeXycsvJZ2uaYRjMdZeJGNAnHLUGLkBscw5aI8=  some other key
273
+    Your selection? (1-2, leave empty to abort): [[1]]
274
+    ~~~~
275
+
276
+We confirm that the reconfiguring has worked because the configuration for the `email` service now references the key we selected earlier.
277
+
278
+~~~~ shell-session
279
+$ derivepassphrase vault --export -
280
+{"services": {"email": {"key": "AAAAC3NzaC1lZDI1NTE5AAAAIIF4gWgm1gJIXw//Mkhv5MEwidwcakUGCekJD/vCEml2", "length": 12, "repeat": 3, "lower": 1, "upper": 1, "number": 1, "space": 0}, "bank": {"length": 5, "lower": 0, "upper": 0, "number": 5, "space": 0, "dash": 0, "symbol": 0}, "work-2024Q4": {"length": 8, "upper": 1, "number": 1, "space": 0, "dash": 1, "symbol": 0}}}
281
+~~~~
282
+
283
+We further confirm that the derived passphrase adheres to the rules laid out for the `email` service.
284
+Since we configured the service correctly, `derivepassphrase` knows to automatically use our previously selected SSH key.
285
+
286
+~~~~ shell-session
287
+$ derivepassphrase vault email
288
+BNbSA\E]#s8H
289
+~~~~
290
+
291
+!!! note "Reminder: reproducibility"
292
+
293
+    Unless you are using our SSH test key, **your SSH key will differ from ours, as will the derived passphrases**.
294
+
295
+We can now log in to our email account with the old passphrase
296
+(`derivepassphrase vault -p email`) and change it to the new one
297
+(`derivepassphrase vault email`).
298
+
299
+## Using the master SSH key by default
300
+
301
+To get the other two services to use the master SSH key as well, we *could* reconfigure them manually, as we did with the `email` service.
302
+However, that is unnecessarily repetitive.
303
+Instead, we will set up `derivepassphrase` to use this master SSH key by default.
304
+
305
+=== "Only one suitable key"
306
+
307
+    ~~~~ shell-session
308
+    $ derivepassphrase vault --config -k
309
+    Suitable SSH keys:
310
+    [1] ssh-ed25519 ...gm1gJIXw//Mkhv5MEwidwcakUGCekJD/vCEml2  vault key
311
+    Use this key? [[yes]]
312
+    ~~~~
313
+
314
+=== "Multiple suitable keys"
315
+
316
+    ~~~~ shell-session
317
+    $ derivepassphrase vault --config -k
318
+    Suitable SSH keys:
319
+    [1] ssh-ed25519 ...gm1gJIXw//Mkhv5MEwidwcakUGCekJD/vCEml2  vault key
320
+    [2] ssh-rsa ...YAWfeXycsvJZ2uaYRjMdZeJGNAnHLUGLkBscw5aI8=  some other key
321
+    Your selection? (1-2, leave empty to abort): [[1]]
322
+    ~~~~
323
+
324
+The selected key will then appear in the `global` section of the configuration.
325
+
326
+~~~~ shell-session
327
+$ derivepassphrase vault --export -
328
+{"global": {"key": "AAAAC3NzaC1lZDI1NTE5AAAAIIF4gWgm1gJIXw//Mkhv5MEwidwcakUGCekJD/vCEml2"}, "services": {"email": {"key": "AAAAC3NzaC1lZDI1NTE5AAAAIIF4gWgm1gJIXw//Mkhv5MEwidwcakUGCekJD/vCEml2", "length": 12, "repeat": 3, "lower": 1, "upper": 1, "number": 1, "space": 0}, "bank": {"length": 5, "lower": 0, "upper": 0, "number": 5, "space": 0, "dash": 0, "symbol": 0}, "work-2024Q4": {"length": 8, "upper": 1, "number": 1, "space": 0, "dash": 1, "symbol": 0}}}
329
+~~~~
330
+
331
+The `email` account still has an explicit configured SSH key, which overrides the global default setting; it just so happens that in this case both keys are the same.
332
+We can therefore remove the useless key override from the `email` account.
333
+
334
+~~~~ shell-session
335
+$ derivepassphrase vault --config --unset=key email
336
+$ derivepassphrase vault --export -
337
+{"global": {"key": "AAAAC3NzaC1lZDI1NTE5AAAAIIF4gWgm1gJIXw//Mkhv5MEwidwcakUGCekJD/vCEml2"}, "services": {"email": {"length": 12, "repeat": 3, "lower": 1, "upper": 1, "number": 1, "space": 0}, "bank": {"length": 5, "lower": 0, "upper": 0, "number": 5, "space": 0, "dash": 0, "symbol": 0}, "work-2024Q4": {"length": 8, "upper": 1, "number": 1, "space": 0, "dash": 1, "symbol": 0}}}
338
+~~~~
339
+
340
+The generated passphrase is still the same.
341
+
342
+~~~~ shell-session
343
+$ derivepassphrase vault email
344
+BNbSA\E]#s8H
345
+~~~~
346
+
347
+Finally, the generated passphrases for the `bank` and `work-2024Q4` accounts are affected by the global SSH key as well, as intended.
348
+
349
+~~~~ shell-session
350
+$ derivepassphrase vault bank
351
+06041
352
+$ derivepassphrase vault work-2024Q4
353
+PE1qg_M7
354
+~~~~
355
+
356
+However, [the new passphrase for the `work` account does not yet adhere to the company's passphrase policy](basic-setup-passphrase.md#the-scenario) because [we are using the "wrong" special characters](basic-setup-passphrase.md#special-character).
357
+We therefore change the passphrase generation parameters such that only dashes and no underscores are emitted.
358
+
359
+~~~~ shell-session
360
+$ derivepassphrase vault --config --lower=1 work-2024Q4
361
+$ derivepassphrase vault work-2024Q4
362
+pEY-qg7n
363
+~~~~
364
+
365
+We can then log into our bank and work accounts using the old passphrases (with `-p`) and change them to the new ones (without `-p`).
366
+
367
+## Summary
368
+
369
+We have reconfigured `derivepassphrase` (with the `vault` passphrase derivation scheme) for use with a master SSH key, and modified three existing accounts to use that key.
370
+Our configuration should look like this:
371
+
372
+~~~~ shell-session
373
+$ derivepassphrase vault --export -
374
+{"global": {"key": "AAAAC3NzaC1lZDI1NTE5AAAAIIF4gWgm1gJIXw//Mkhv5MEwidwcakUGCekJD/vCEml2"}, "services": {"bank": {"dash": 0, "length": 5, "lower": 0, "number": 5, "space": 0, "symbol": 0, "upper": 0}, "email": {"length": 12, "lower": 1, "number": 1, "repeat": 3, "space": 0, "upper": 1}, "work-2024Q4": {"dash": 1, "length": 8, "lower": 1, "number": 1, "space": 0, "symbol": 0, "upper": 1}}}
375
+~~~~
376
+
377
+We should also get the following output when asking for those passphrases again:
378
+
379
+~~~~ shell-session
380
+$ derivepassphrase vault email
381
+BNbSA\E]#s8H
382
+$ derivepassphrase vault bank
383
+06041
384
+$ derivepassphrase vault work-2024Q4
385
+pEY-qg7n
386
+~~~~
387
+
388
+This completes the tutorial.
389
+
390
+[BASIC_SETUP_PASSPHRASE]: basic-setup-passphrase.md
391
+[PREREQ_SSH_KEY]: ../reference/prerequisites-ssh-key.md#ssh-key
392
+
393
+[OPENSSH]: https://www.openssh.org/
394
+[PUTTY]: https://putty.software/
... ...
@@ -2,7 +2,8 @@
2 2
 title: Tutorial overview
3 3
 ---
4 4
 
5
-* [Setting up `derivepassphrase vault` for three accounts, with a master
6
-  passphrase][BASIC_SETUP_PASSPHRASE]
5
+1. [Setting up `derivepassphrase vault` for three accounts, with a master passphrase][BASIC_SETUP_PASSPHRASE]
6
+2. [Using a master SSH key with `derivepassphrase vault` on existing accounts][BASIC_SETUP_SSH_KEY]
7 7
 
8 8
 [BASIC_SETUP_PASSPHRASE]: basic-setup-passphrase.md
9
+[BASIC_SETUP_SSH_KEY]: basic-setup-ssh-key.md
... ...
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><defs><clipPath id="iconid0"><path d="M 45.63 24.830000000000005 l -42.0 -9.0 l 11.25 -0.0 l 6.75 -9.0 c 3.0 -4.5 , 1.5 3.3306690738754696e-16 , 5.699999999999999 0.9000000000000004 c 4.199999999999999 0.8999999999999995 , 4.675609756097561 -3.819512195121952 , 5.5682926829268276 1.5146341463414625 l 2.469512195121954 10.975609756097562 l 10.262195121951219 4.609756097560975 z" /></clipPath></defs><g><g><g><polygon points="6.921,45.92 36.639,45.92 40.239,42.32 40.239,35.84 10.521,35.84 6.921,39.44" style="fill:#bfbfbf;stroke:none" /><polygon points="8.721,45.92 8.721,44.12 34.839,44.12 34.839,41.24 40.239,35.84 40.239,42.32 36.639,45.92" style="fill:#808080;stroke:none" /><polygon points="6.921,44.12 8.721,44.12 8.721,41.24 34.839,41.24 40.239,35.84 38.439,35.84 34.839,39.44 6.921,39.44" style="fill:#ffffff;stroke:none" /><rect x="24.021" y="42.86" width="9" height="1.26" style="fill:#000000;stroke:none" /><polygon points="6.921,45.92 36.639,45.92 40.239,42.32 40.239,35.84 10.521,35.84 6.921,39.44" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5" /></g><g><polygon points="11.6781,38.72 32.3781,38.72 35.2581,35.84 35.2581,18.74 14.5581,18.74 11.6781,21.62" style="fill:#bfbfbf;stroke:none" /><polygon points="11.6781,38.72 12.5781,37.82 31.4781,37.82 31.4781,22.52 35.2581,18.74 35.2581,35.84 32.3781,38.72" style="fill:#808080;stroke:none" /><polygon points="11.6781,38.72 12.5781,37.82 12.5781,22.52 31.4781,22.52 32.3781,21.62 11.6781,21.62" style="fill:#ffffff;stroke:none" /><polygon points="12.5781,36.02 13.4781,35.12 13.4781,23.42 30.5781,23.42 31.4781,22.52 12.5781,22.52" style="fill:#808080;stroke:none" /><polygon points="12.5781,36.02 13.4781,35.12 30.5781,35.12 30.5781,23.42 31.4781,22.52 31.4781,36.02" style="fill:#ffffff;stroke:none" /><rect x="13.4781" y="23.42" width="17.1" height="11.7" style="fill:#0000ff;stroke:none" /><polygon points="13.4781,35.12 14.3781,35.12 14.3781,24.32 30.5781,24.32 30.5781,23.42 13.4781,23.42" style="fill:#000080;stroke:none" /><polygon points="11.6781,38.72 32.3781,38.72 35.2581,35.84 35.2581,18.74 14.5581,18.74 11.6781,21.62" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5" /></g></g><g><path d="M 45.63 24.830000000000005 l -42.0 -9.0 l 11.25 -0.0 l 6.75 -9.0 c 3.0 -4.5 , 1.5 3.3306690738754696e-16 , 5.699999999999999 0.9000000000000004 c 4.199999999999999 0.8999999999999995 , 4.675609756097561 -3.819512195121952 , 5.5682926829268276 1.5146341463414625 l 2.469512195121954 10.975609756097562 l 10.262195121951219 4.609756097560975 z" style="fill:#000000;stroke:none" /><polygon points="4.53,11.63 46.53,20.63 46.98,18.53 4.98,9.53" clip-path="url(#iconid0)" style="fill:#ffffff;stroke:none" /><path d="M 45.63 24.830000000000005 l -42.0 -9.0 l 11.25 -0.0 l 6.75 -9.0 c 3.0 -4.5 , 1.5 3.3306690738754696e-16 , 5.699999999999999 0.9000000000000004 c 4.199999999999999 0.8999999999999995 , 4.675609756097561 -3.819512195121952 , 5.5682926829268276 1.5146341463414625 l 2.469512195121954 10.975609756097562 l 10.262195121951219 4.609756097560975 z" style="fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1" /></g></g></svg>
0 2
\ No newline at end of file
... ...
@@ -96,6 +96,7 @@ nav:
96 96
   - Tutorials & Examples:
97 97
     - tutorials/index.md
98 98
     - tutorials/basic-setup-passphrase.md
99
+    - tutorials/basic-setup-ssh-key.md
99 100
   - How-Tos:
100 101
     - how-tos/index.md
101 102
     - how-tos/ssh-key.md
... ...
@@ -11,6 +11,7 @@ nav:
11 11
   - Tutorials & Examples:
12 12
     - tutorials/index.md
13 13
     - tutorials/basic-setup-passphrase.md
14
+    - tutorials/basic-setup-ssh-key.md
14 15
   - How-Tos:
15 16
     - how-tos/index.md
16 17
     - how-tos/ssh-key.md
17 18