├── .github
└── CODEOWNERS
├── .gitignore
├── LICENSE
├── README.md
├── SECURITY.md
├── bin
└── gpg-sign-notify
├── docs
├── optional.md
└── troubleshooting.md
├── env.sh
├── expects
├── expect-arch.sh
├── expect-macos.sh
└── expect-ubuntu.sh
├── git.sh
├── gpg.sh
├── import.sh
├── lib
├── git_conf.sh
├── gpg_agent_conf.sh
├── gpg_conf.sh
├── install.sh
├── notifications.sh
├── scdaemon.sh
├── ssh_conf.sh
└── tree.sh
├── realname-and-email.sh
├── reset.sh
└── ssh.sh
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @DataDog/corp-it-security
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gpg.pub.asc
2 | *.gpg.pub.bin
3 | *.pub
4 | *.code-workspace
5 | *.sw[a-z]
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Trishank K Kuppusamy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ⚠️ Repository Deprecated ⚠️
2 | Notice: This code repository is no longer maintained or updated. The content and code are provided as-is, and may no longer be relevant or functional.
3 |
4 | For Datadog employee, see the "commit signing setup guide" in Confluence instead_
5 |
6 | # YubiKey at Datadog
7 |
8 | - [Summary](#summary)
9 | - [Estimated burden and prerequisites](#estimated-burden-and-prerequisites)
10 | - [U2F](#u2f)
11 | - [GPG](#gpg)
12 | - [git](#git)
13 | - [SSH](#ssh)
14 | - [Reset](#reset)
15 | - [Troubleshooting](#troubleshooting)
16 | - [Optional](#optional)
17 | - [References](#references)
18 |
19 | ## Summary
20 |
21 | GPG is useful for authenticating yourself over SSH and / or GPG-signing your
22 | git commits / tags. However, without hardware like the
23 | [YubiKey](https://www.yubico.com/products/yubikey-hardware/), you would
24 | typically keep your GPG private subkeys in "plain view" on your machine, even
25 | if encrypted. That is, attackers who personally target
26 | [[1](https://www.kennethreitz.org/essays/on-cybersecurity-and-being-targeted),
27 | [2](https://bitcoingold.org/critical-warning-nov-26/),
28 | [3](https://panic.com/blog/stolen-source-code/),
29 | [4](https://www.fox-it.com/en/insights/blogs/blog/fox-hit-cyber-attack/)] you
30 | can compromise your machine can exfiltrate your (encrypted) private key, and
31 | your passphrase, in order to pretend to be you.
32 |
33 | Instead, this setup lets you store your private subkeys on your YubiKey.
34 | Actually, it gives you much stronger guarantees: you *cannot* authenticate over
35 | SSH and / or sign GPG commits / tags *without*: (1) your YubiKey plugged in and
36 | operational, (2) your YubiKey PIN, and (3) touching your YubiKey. So, even if
37 | there is malware trying to get you to sign, encrypt, or authenticate something,
38 | you would almost certainly notice, because your YubiKey will flash, asking for
39 | your attention. (There is the "[time of check to time of
40 | use](https://en.wikipedia.org/wiki/Time_of_check_to_time_of_use)" issue,
41 | but that is out of our scope.)
42 |
43 | ## Estimated burden and prerequisites
44 |
45 | About 2-3 hours. 15 minutes could save you 15% or more on cybersecurity
46 | insurance.
47 |
48 | You will need macOS with [Homebrew](https://brew.sh/) / Ubuntu / Archlinux, a password manager, and a
49 | [YubiKey 5](https://www.yubico.com/products/yubikey-hardware/).
50 |
51 | ## U2F
52 |
53 | **STRONGLY recommended:** configure U2F for
54 | [GitHub](https://help.github.com/articles/configuring-two-factor-authentication/#configuring-two-factor-authentication-using-fido-u2f)
55 | and
56 | [Google](https://support.yubico.com/hc/en-us/articles/360013717460-Using-Your-YubiKey-with-Google).
57 |
58 | ## GPG
59 |
60 | **Please read and follow all of the instructions carefully.**
61 |
62 | ```bash
63 | $ ./gpg.sh
64 | ```
65 |
66 | (Protip: set `TEMPDIR=1` when preparing YubiKey for someone else to avoid
67 | polluting your default GPG homedir.)
68 |
69 | ## git
70 |
71 | **STRONGLY RECOMMENDED:** signing your git commits and tags.
72 |
73 | You **must** first set up [GPG](#gpg).
74 |
75 | Then, to sign git commits and tags for a _particular_ repository:
76 |
77 | ```bash
78 | $ ./git.sh /path/to/git/repository
79 | ```
80 |
81 | Or, to sign git commits and tags for _all_ repositories:
82 |
83 | ```bash
84 | $ ./git.sh
85 | ```
86 |
87 | ## SSH
88 |
89 | **NOT recommended** for most users. This script sets up your YubiKey as the holder of your SSH key,
90 | helping to prevent it from being leaked or stolen. The script will take control of `ssh-agent`, so
91 | it's not particularly compatible with other SSH keys - you should only run this if you intend to use
92 | this as your only SSH key on the machine you're using.
93 |
94 | With this setup, you'll need to enter a PIN to unlock the key every 24 hours and then physically touch the
95 | key when it blinks (i.e. every time you SSH or push/pull Git). If you don't touch the key, the request will
96 | timeout and you'll get an unhelpful message.
97 |
98 | This is compatible with usage on remote machines over SSH
99 | (it will set up agent forwarding to use the key remotely; touch is required on each action).
100 |
101 | You **must** have first set up [GPG](#gpg). Then:
102 |
103 | ```bash
104 | $ ./ssh.sh
105 | ```
106 |
107 | ## Reset
108 |
109 | If you need to reset YubiKeys, you may use the following script. The script looks for every plugged YubiKey,
110 | and shows a menu to reset one specific key, or all of them.
111 | **Please read and follow all of the instructions carefully. YOU WILL NOT BE ABLE TO RETRIEVE KEYS/DATA FROM THE YUBIKEY AFTER COMPLETION.**
112 |
113 | ```bash
114 | $ ./reset.sh
115 | ```
116 |
117 | ## Troubleshooting
118 |
119 | Go [here](docs/troubleshooting.md) for troubleshooting common issues such as unblocking a blocked card, error when pulling or pushing with git over SSH, and rebasing with git.
120 |
121 | ## Optional
122 |
123 | Go [here](docs/optional.md) for support on optional bits such as configuring a computer to use an already configured YubiKey, signing for different git repositories with different keys, Keybase, VMware Fusion, and Docker Content Trust.
124 |
125 | ## References
126 |
127 | 1. [YubiKey Handbook](https://ruimarinho.gitbooks.io/yubikey-handbook/content/openpgp/)
128 |
129 | 2. [A Git Horror Story: Repository Integrity With Signed Commits](https://mikegerwitz.com/papers/git-horror-story)
130 |
131 | 3. [Welp, there go my Git signatures](http://karl.kornel.us/2017/10/welp-there-go-my-git-signatures/)
132 |
133 | 4. [[Bitcoin-development] PSA: Please sign your git commits](https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2014-May/005877.html)
134 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Reporting a Vulnerability
4 |
5 | If you believe you’ve discovered a bug in Datadog’s security,
6 | please get in touch at security@datadoghq.com and we will get back to you within 24 hours,
7 | and usually earlier.
8 | Our [PGP key](https://www.datadoghq.com/8869756E.asc.txt) is available for download in case you need to encrypt communications with us.
9 | We request that you not publicly disclose the issue until we have had a chance to address it.
10 |
--------------------------------------------------------------------------------
/bin/gpg-sign-notify:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Notifies the user that they need to do something if gpg-sign takes longer than
4 | # 5 seconds
5 |
6 | # The explicit redirection of stdin is required, otherwise (in the absence of
7 | # job control) bash will bind stdin of the background process to /dev/null,
8 | # resulting in a bad signature/bad verification for good signatures.
9 | gpg "$@" = 4 thing, and the default bash on MacOS is 3, so we prioritise
17 | # compatibility.
18 |
19 | notify() {
20 | local title="$1"; shift;
21 | local body="$1"; shift;
22 |
23 | %%NOTIFICATION_NOTIFY%%
24 | }
25 |
26 | reap() {
27 | # Cancel the trap, otherwise it will run again when another child exits
28 | trap "" SIGCHLD
29 |
30 | if kill -0 $gpg_pid 2>/dev/null; then
31 | # GPG is still running; we assume it's waiting for input
32 | notify "Git wants to sign a commit!" "Touch your YubiKey after submitting the User PIN"
33 | # Make sure we exit with the gpg status
34 | wait $gpg_pid
35 | else
36 | # GPG is not running; kill the sleep process if it's still there and exit
37 | kill $sleep_pid
38 | wait $gpg_pid
39 | exit_status=$?
40 | exit $exit_status
41 | fi
42 | }
43 |
44 | # Run reap as soon as _any_ child exits
45 | trap reap SIGCHLD
46 |
47 | # Wait for all children to exit
48 | wait
49 |
--------------------------------------------------------------------------------
/docs/optional.md:
--------------------------------------------------------------------------------
1 | # Optional
2 |
3 | - [Configure another computer to use a configured YubiKey](#configure-another-computer-to-use-a-configured-yubikey)
4 | - [Signing for different git repositories with different keys](#signing-for-different-git-repositories-with-different-keys)
5 | - [Keybase](#keybase)
6 | - [VMware Fusion](#vmware-fusion)
7 | - [Docker Content Trust](#docker-content-trust)
8 |
9 | ## Configure another computer to use a configured YubiKey
10 |
11 | You don't need to do anything extra if you have not set up GPG and SSH to your use YubiKey.
12 |
13 | Otherwise, you need to:
14 |
15 | On your previous computer:
16 | 1. Get the Yubikey GPG key ID by running `gpg --list-keys`, in the following example the key ID is `4E09860E71D948019BD426D5D099A306DBECDF1B`
17 | 
18 | 2. Get a copy of your Yubikey GPG public key (this might have been backed up in your password manager) by running `gpg --export --armor key_id > /path/to/pubkey.asc`, so in our example it will be `gpg --export --armor 4E09860E71D948019BD426D5D099A306DBECDF1B > pubkey.asc`.
19 | 3. (Optional) Write your copy of your GPG public key stored in your password manager to disk if not already there (e.g., to `/path/to/pubkey.asc`).
20 |
21 | On the new computer:
22 | 1. Get the pubkey.asc file on the disk by downloading it
23 | 2. Run [`./import.sh -p /path/to/pubkey.asc -i key_id`](../import.sh). In our example, `./import.sh -p ~/pubkey.asc -i 4E09860E71D948019BD426D5D099A306DBECDF1B`
24 | 3. You will be prompted several times:
25 | 1. To install dependencies (required), type yes, and press enter
26 | 2. To configure the Yubikey GPG key for commit signing (or not), type yes or no, and press enter
27 | 3. To use the Yubikey GPG key for SSH connections (or not), type yes or no, and press enter
28 |
29 | ## Signing for different git repositories with different keys
30 |
31 | The script can setup your Git installation so that all your commits and tags
32 | will be signed by default with the key contained in the YubiKey. We
33 | **strongly** recommend that you turn on this option. If you have done so,
34 | please stop reading here.
35 |
36 | Otherwise, one reason for declining this option may be that you wish to sign
37 | for different repositories with different keys. There are a few ways to handle
38 | this. Perhaps the simplest is to let the script assign the YubiKey to all git
39 | repositories, and then use `git config --local` to override `user.signingkey`
40 | for different repositories.
41 |
42 | Alternatively, let us say you use your personal key for open source projects,
43 | and the one in the YubiKey for Datadog proprietary code. One possible
44 | solution is to setup git aliases. First, make sure signing is turned on
45 | globally:
46 |
47 | ```sh
48 | git config --global commit.gpgsign true
49 | git config --global tag.forceSignAnnotated true
50 | ```
51 |
52 | Then you can tell git to use a specific key by default, depending on which one
53 | is the one you use the most:
54 |
55 | ```sh
56 | git config --global user.signingkey
57 | ```
58 |
59 | You can alias the `commit` command to override the default key and use another
60 | one to sign that specific commit:
61 |
62 | ```sh
63 | git config --global alias.dd-commit '-c user.signingkey= commit'
64 | git config --global alias.dd-tag '-c user.signingkey= tag'
65 | ```
66 |
67 | With this setup, every time you do `git commit` or `git tag`, the default key
68 | will be used while `git dd-commit` and `git dd-tag` will use the one in the
69 | YubiKey.
70 |
71 | ## Keybase
72 |
73 | Optional: verify public key on Keybase. You can now do this using the
74 | command-line option, with only `curl` and `gpg`, and without installing any
75 | Keybase app, or uploading an encrypted copy of your private key. For example,
76 | see this [profile](https://keybase.io/trishankdatadog).
77 |
78 | If you have the [Keybase application](https://keybase.io/docs/the_app/install_macos)
79 | installed, you can import your YubiKey public key like this:
80 |
81 | ```bash
82 | $ keybase pgp select
83 |
84 | # If you already have a primary Keybase public key, use the --multi flag to import another
85 | $ keybase pgp select --multi
86 | ```
87 |
88 | See `keybase pgp help select` for more detail.
89 |
90 | ## VMware Fusion
91 |
92 | Optional: using YubiKey inside GNU/Linux running on VMware Fusion.
93 |
94 | 1. Shut down your VM, find its .vmx file, edit the file to the [add the
95 | following
96 | line](https://www.symantec.com/connect/blogs/enabling-hid-devices-such-usb-keyboards-barcode-scanners-vmware),
97 | and then reboot it: `usb.generic.allowHID = "TRUE"`
98 |
99 | 2. Connect your YubiKey to the VM once you have booted and logged in.
100 |
101 | 3. Install libraries for smart card:
102 |
103 | 1. Ubuntu 17.10: `apt install scdaemon`
104 |
105 | 2. Fedora 27: `dnf install pcsc-lite pcsc-lite-ccid`
106 |
107 | 4. Import your public key (see Step 13).
108 |
109 | 5. Set ultimate trust for your key (see Step 20).
110 |
111 | 6. Configure GPG (see Step 22).
112 |
113 | 7. Test the keys (see Step 23). On Fedora, make sure to replace `gpg` with
114 | `gpg2`.
115 |
116 | 8. Use the absolutely terrible kludge in Table 1 to make SSH work.
117 |
118 | 9. Spawn a new shell, and test GitHub SSH (see Step 26).
119 |
120 | 10. Test Git signing (see Step 28). On Fedora, make sure to replace `gpg` with
121 | `gpg2`: `git config --global gpg.program gpg2`
122 |
123 | ```sh
124 | # gpg-ssh hack
125 | gpg-connect-agent killagent /bye
126 | eval $(gpg-agent --daemon --enable-ssh-support --sh)
127 | ssh-add -l
128 | ```
129 |
130 | **Table 1**: Add these lines to `~/.bashrc`.
131 |
132 | ## Docker Content Trust
133 |
134 | Optional: using YubiKey to store the root role key for Docker Notary.
135 |
136 | 1. Assumption: you are running all of the following under [Fedora
137 | 27](#vmware-fusion).
138 |
139 | 2. Install prerequisites: `dnf install golang yubico-piv-tool`
140 |
141 | 3. Set [GOPATH](https://golang.org/doc/code.html#GOPATH) (make sure to update
142 | PATH too), and spawn a new `bash` shell.
143 |
144 | 4. Check out the Notary source code: `go get
145 | github.com/theupdateframework/notary`
146 |
147 | 5. Patch source code to [point to correct location of shared library on
148 | Fedora](https://github.com/theupdateframework/notary/pull/1286).
149 |
150 | 1. `cd ~/go/src/go get github.com/theupdateframework/notary`
151 |
152 | 2. `git pull https://github.com/trishankatdatadog/notary.git trishank_kuppusamy/fedora-pkcs11`
153 |
154 | 6. [Build and install](https://github.com/theupdateframework/notary/pull/1285)
155 | the Notary client: `go install -tags pkcs11
156 | github.com/theupdateframework/notary/cmd/notary`
157 |
158 | 7. Add the lines in Table 2 to your `bash` profile, and spawn a new shell.
159 |
160 | 8. Try listing keys (there should be no signing keys as yet):
161 |
162 | 1. `dockernotary key list -D`
163 |
164 | 2. If you see the line `"DEBU[0000] Initialized PKCS11 library
165 | /usr/lib64/libykcs11.so.1 and started HSM session"`, then we are in
166 | business.
167 |
168 | 3. Otherwise, if you see the line `"DEBU[0000] No yubikey found, using
169 | alternative key storage: found library /usr/lib64/libykcs11.so.1, but
170 | initialize error pkcs11: 0x6: CKR_FUNCTION_FAILED"`, then you probably
171 | need to `gpgconf --kill scdaemon` ([see this
172 | issue](https://github.com/theupdateframework/notary/issues/1006)),
173 | and try again.
174 |
175 | 9. Generate the root role key ([can be reused across multiple Docker
176 | repositories](https://github.com/theupdateframework/notary/blame/a41821feaf59a28c1d8f78799300d26f8bdf8b0d/docs/best_practices.md#L91-L95)),
177 | and export it to both YubiKey, and keep a copy on disk:
178 |
179 | 1. Choose a strong passphrase.
180 |
181 | 2. `dockernotary key generate -D`
182 |
183 | 3. Commit passphrase to memory and / or offline storage.
184 |
185 | 4. Try listing keys again, you should now see a copy of the same private
186 | key in two places (disk, and YubiKey).
187 |
188 | 5. Backup private key in `~/.docker/trust/private/KEYID.key` unto offline,
189 | encrypted, long-term storage.
190 |
191 | 6. [Securely
192 | delete](https://www.gnu.org/software/coreutils/manual/html_node/shred-invocation.html)
193 | this private key on disk.
194 |
195 | 7. Now if you list the keys again, you should see the private key only on
196 | YubiKey.
197 |
198 | 10. Link the yubikey library so that the prebuilt docker client can find it:
199 | `sudo ln -s /usr/lib64/libykcs11.so.1 /usr/local/lib/libykcs11.so`
200 |
201 | 11. Later, when you want Docker to use the root role key on your YubiKey:
202 |
203 | 1. When you push an image, you may have to kill `scdaemon` (in a separate
204 | shell) right after Docker pushes, but right before Docker uses the root
205 | role key on your YubiKey, and generates a new targets key for the
206 | repository.
207 |
208 | 2. Use `docker -D` to find out exactly when to do this.
209 |
210 | 3. This is annoying, but it works.
211 |
212 | ```sh
213 | # docker notary stuff
214 | alias dockernotary="notary -s https://notary.docker.io -d ~/.docker/trust"
215 | # always be using content trust
216 | export DOCKER_CONTENT_TRUST=1
217 | ```
218 |
219 | **Table 2**: Add these lines to `~/.bashrc`.
220 |
--------------------------------------------------------------------------------
/docs/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
2 |
3 | - [Blocked card](#blocked-card)
4 | - [Error with git pull/fetch or when using SSH](#error-with-git-pullfetch-or-when-using-ssh)
5 | - [git rebase](#git-rebase)
6 | - [No PyUSB backend detected](#no-pyusb-backend-detected)
7 | - [Bad substitution](#bad-substitution)
8 | - [Operation-not-supported-by-device-error](#operation-not-supported-by-device-error)
9 |
10 | ## Blocked card
11 |
12 | If you are blocked out of using GPG because you entered your PIN wrong too
13 | many times (3x by default), **don’t panic**: just [follow the
14 | instructions](https://github.com/ruimarinho/yubikey-handbook/blob/master/openpgp/troubleshooting/gpg-failed-to-sign-the-data.md)
15 | here. Make sure you enter your **Admin PIN** correctly within 3x, otherwise
16 | your current keys are blocked, and you must reset your YubiKey to use new keys.
17 |
18 | ## Error with git pull/fetch or when using SSH
19 |
20 | If you try to ssh or git pull/fetch and you have the following error:
21 | ```
22 | sign_and_send_pubkey: signing failed: agent refused operation
23 | ```
24 | You are probably mistyping your PIN. To verify it, you can:
25 | ```
26 | gpg --card-edit
27 | gpg/card> verify
28 | ...
29 | PIN retry counter : 3 0 3 # if it is the right PIN
30 | PIN retry counter : 2 0 3 # if it is a wrong PIN
31 | ...
32 | ```
33 | If your PIN is wrong, try 123456, which is the default PIN.
34 | If it still fails, reset your PIN:
35 | ```
36 | gpg --card-edit
37 | gpg/card> admin
38 | gpg/card> passwd
39 | gpg: OpenPGP card no. D2760001240102010006055532110000 detected
40 |
41 | Your selection? 1
42 | PIN changed.
43 |
44 | 1 - change PIN
45 | 2 - unblock PIN
46 | 3 - change Admin PIN
47 | 4 - set the Reset Code
48 | Q - quit
49 |
50 | Your selection? q
51 | ```
52 |
53 | ## git rebase
54 |
55 | If you are using the FIPS model, you can perform signing operations for 15
56 | seconds after touching your YubiKey before having to touch it again. When
57 | running a large git rebase, you may have to touch your YubiKey multiple times.
58 | If the rebase seems to hang and the YubiKey flashes, it means you need to touch
59 | it again.
60 |
61 | If you are still having issues when rebasing, you might consider using
62 | the `--no-gpg-sign` flag as a [workaround](https://github.com/DataDog/yubikey/issues/19).
63 |
64 | ## No PyUSB backend detected
65 |
66 | If you see the following error while running `./gpg.sh`:
67 |
68 | ```
69 | Usage: ykman [OPTIONS] COMMAND [ARGS]...
70 | Try "ykman -h" for help.
71 |
72 | Error: No PyUSB backend detected!
73 | ```
74 |
75 | Hit CTRL-C to exit the script (if the script has not already exited) and [reinstall](https://github.com/Yubico/yubikey-manager/issues/185#issuecomment-446379356) `libsub`, then try again: `brew reinstall libusb`
76 |
77 | ## Bad substitution
78 |
79 | If you see the following error while running `./gpg.sh`:
80 |
81 | ```
82 | OS detected is macos
83 | Is it correct ? (y|N) Y
84 | env.sh: line 34: ${OS,,}: bad substitution
85 | ```
86 |
87 | Run `brew install bash`. The script is using a feature not that is not supported by the old macOS bash.
88 |
89 | ## Operation not supported by device error
90 |
91 | This manifests as PIN Entry dialog prompting to insert the card in a perpetual loop.
92 |
93 | You may also see:
94 |
95 | ```shell
96 | gpg --card-status
97 | gpg: selecting card failed: Operation not supported by device
98 | gpg: OpenPGP card not available: Operation not supported by device
99 | ```
100 |
101 | Run [./lib/scdaemon.sh](../lib/scdaemon.sh).
102 |
103 | ## GPG error on maOS M1
104 |
105 | If you see the following running gpg related scripts:
106 | ```
107 | gpg
108 | dyld[23790]: Library not loaded: /opt/homebrew/opt/libgpg-error/lib/libgpg-error.0.dylib
109 | Referenced from: /opt/homebrew/Cellar/gnupg/2.3.4/bin/gpg
110 | Reason: tried: '/opt/homebrew/opt/libgpg-error/lib/libgpg-error.0.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64e')), '/usr/local/lib/libgpg-error.0.dylib' (no such file), '/usr/lib/libgpg-error.0.dylib' (no such file), '/opt/homebrew/Cellar/libgpg-error/1.44/lib/libgpg-error.0.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64e')), '/usr/local/lib/libgpg-error.0.dylib' (no such file), '/usr/lib/libgpg-error.0.dylib' (no such file)
111 | [1] 23790 abort gpg
112 | ```
113 |
114 | Use `brew reinstall` to reinstall all of the GPG dependencies as well as GPG itself.
115 |
--------------------------------------------------------------------------------
/env.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | function which_flavour {
4 | if [[ -f /etc/os-release ]]; then
5 | detected="$(grep '^ID=' /etc/os-release | cut -d= -f2)"
6 | fi
7 | echo "$detected"
8 | }
9 |
10 | case "$OSTYPE" in
11 | darwin*)
12 | OS='macos'
13 | ;;
14 | linux*)
15 | OS=$(which_flavour)
16 | ;;
17 | *)
18 | OS="not detected"
19 | ;;
20 | esac
21 |
22 | echo "OS detected is $OS"
23 |
24 | case $(echo "$OS" | tr "[:upper:]" "[:lower:]") in
25 | macos)
26 | PKG_MANAGER="brew"
27 | PKG_MANAGER_ENV=""
28 | PKG_MANAGER_INSTALL="install"
29 | PKG_MANAGER_UPDATE="update"
30 | PKG_MANAGER_UPGRADE="upgrade"
31 | PKG_CHECK="brew"
32 | PKG_CHECK_ARGS="list"
33 | HOMEBREW_PREFIX=$(brew --prefix)
34 | HOMEBREW_BIN=$HOMEBREW_PREFIX/bin
35 | USER_BIN_DIR=${HOME}/.local/bin
36 | GIT=$HOMEBREW_BIN/git
37 | GPG=$HOMEBREW_BIN/gpg
38 | GPG_AGENT=$HOMEBREW_BIN/gpg-agent
39 | GPGCONF=$HOMEBREW_BIN/gpgconf
40 | YKMAN=$HOMEBREW_BIN/ykman
41 | CLIP="pbcopy"
42 | CLIP_ARGS=""
43 | PINENTRY_SETUP="${HOMEBREW_BIN}/pinentry-tty"
44 | PINENTRY="${HOMEBREW_BIN}/pinentry-mac"
45 | OPEN="open"
46 | DEPS=(
47 | "expect"
48 | "git"
49 | "gpg"
50 | "pinentry-mac"
51 | "ykman"
52 | )
53 | set +e
54 | read -r -d '' NOTIFICATION_NOTIFY << EOF
55 | osascript -e 'display notification "'"\$body"'" with title "'"\$title"'"'
56 | EOF
57 | set -e
58 | NOTIFICATION_SCRIPT_PATH="${USER_BIN_DIR}/yubinotif"
59 | SCDAEMON_CONF="disable-ccid\nreader-port \"$(pcsctest <<< 01 | grep 'Reader 01' | awk -F ': ' '{print $2}' | head -n1)\""
60 | export HOMEBREW_NO_AUTO_UPDATE=1
61 | ;;
62 | ubuntu|debian)
63 | PKG_MANAGER="apt"
64 | PKG_MANAGER_ENV="sudo"
65 | PKG_MANAGER_INSTALL="install"
66 | PKG_MANAGER_UPDATE="update"
67 | PKG_MANAGER_UPGRADE="install"
68 | PKG_CHECK="apt"
69 | PKG_CHECK_ARGS="show"
70 | BIN_PATH="/usr/bin"
71 | USER_BIN_DIR=${HOME}/.local/bin
72 | GIT="${BIN_PATH}/git"
73 | GPG="${BIN_PATH}/gpg"
74 | GPG_AGENT="${BIN_PATH}/gpg-agent"
75 | GPGCONF="${BIN_PATH}/gpgconf"
76 | YKMAN="${BIN_PATH}/ykman"
77 | CLIP="${BIN_PATH}/xclip"
78 | CLIP_ARGS="-selection clipboard -i"
79 | PINENTRY_SETUP="/usr/bin/pinentry-tty"
80 | PINENTRY="/usr/bin/pinentry-gnome3"
81 | OPEN="xdg-open"
82 | DEPS=(
83 | "expect"
84 | "git"
85 | "gpg"
86 | "pinentry-tty"
87 | "python3"
88 | "scdaemon"
89 | "yubikey-manager"
90 | "xclip"
91 | )
92 | set +e
93 | read -r -d '' NOTIFICATION_NOTIFY << EOF
94 | notify-send "\$title" "\$body"
95 | EOF
96 | set -e
97 | NOTIFICATION_SCRIPT_PATH="${USER_BIN_DIR}/yubinotif"
98 | SCDAEMON_CONF=""
99 | if ! grep -rqE '^deb http://ppa.launchpad.net/yubico/stable/ubuntu' /etc/apt/sources.list.d/*.list; then
100 | sudo apt-add-repository ppa:yubico/stable
101 | fi
102 | ;;
103 | arch)
104 | PKG_MANAGER="pacman"
105 | PKG_MANAGER_ENV="sudo"
106 | PKG_MANAGER_INSTALL="-S"
107 | PKG_MANAGER_UPDATE="-Sy"
108 | PKG_MANAGER_UPGRADE="-S"
109 | PKG_CHECK="pacman"
110 | PKG_CHECK_ARGS="-Qi"
111 | BIN_PATH="/usr/bin"
112 | USER_BIN_DIR=${HOME}/.local/bin
113 | GIT="${BIN_PATH}/git"
114 | GPG="${BIN_PATH}/gpg"
115 | GPG_AGENT="${BIN_PATH}/gpg-agent"
116 | GPGCONF="${BIN_PATH}/gpgconf"
117 | YKMAN="${BIN_PATH}/ykman"
118 | CLIP="${BIN_PATH}/xclip"
119 | CLIP_ARGS="-selection clipboard -i"
120 | PINENTRY_SETUP="/usr/bin/pinentry"
121 | PINENTRY="/usr/bin/pinentry"
122 | OPEN="xdg-open"
123 | # shellcheck disable=SC2034
124 | DEPS=(
125 | "expect"
126 | "gnupg"
127 | "pinentry"
128 | "git"
129 | "yubikey-manager"
130 | "xclip"
131 | "pcsclite"
132 | )
133 | set +e
134 | read -r -d '' NOTIFICATION_NOTIFY << EOF
135 | notify-send "\$title" "\$body"
136 | EOF
137 | set -e
138 | NOTIFICATION_SCRIPT_PATH="${USER_BIN_DIR}/yubinotif"
139 | # shellcheck disable=SC2034
140 | SCDAEMON_CONF=""
141 | ;;
142 | *)
143 | echo "Sorry, your OS is not supported"
144 | exit 1
145 | esac
146 |
147 | # Use Homebrew binaries.
148 | export PKG_MANAGER
149 | export PKG_MANAGER_ENV
150 | export PKG_MANAGER_INSTALL
151 | export PKG_MANAGER_UPDATE
152 | export PKG_MANAGER_UPGRADE
153 | export PKG_CHECK
154 | export PKG_CHECK_ARGS
155 | export GIT
156 | export GPG
157 | export GPG_AGENT
158 | export GPGCONF
159 | export YKMAN
160 | export CLIP
161 | export CLIP_ARGS
162 | export OPEN
163 | export PINENTRY_SETUP
164 | export PINENTRY
165 | export NOTIFICATION_SCRIPT_PATH
166 | export USER_BIN_DIR
167 |
168 | # Colors galore.
169 | BOLD=$(tput bold)
170 | export BOLD
171 | RED=$(tput setaf 1)
172 | export RED
173 | GREEN=$(tput setaf 2)
174 | export GREEN
175 | YELLOW=$(tput setaf 3)
176 | export YELLOW
177 | BLUE=$(tput setaf 4)
178 | export BLUE
179 | MAGENTA=$(tput setaf 5)
180 | export MAGENTA
181 | RESET=$(tput sgr0) # Reset text
182 | export RESET
183 |
184 | # SSH.
185 | export SSH_ENV="$HOME/.ssh/environment"
186 |
187 | # Folders and files.
188 | export DEFAULT_GPG_HOMEDIR=$HOME/.gnupg
189 | export DEFAULT_GPG_AGENT_CONF=$DEFAULT_GPG_HOMEDIR/gpg-agent.conf
190 | export DEFAULT_GPG_CONF=$DEFAULT_GPG_HOMEDIR/gpg.conf
191 | export DEFAULT_GPG_SCDAEMON_CONF=${DEFAULT_GPG_HOMEDIR}/scdaemon.conf
192 |
193 | # Functions.
194 |
195 | # Backup configuration in default GPG homedir, if it exists.
196 | function backup_conf {
197 | local conf
198 | local conf_backup
199 | conf="$1"
200 |
201 | if [[ -e "$conf" ]]
202 | then
203 | conf_backup=$conf.$(date +%s)
204 | if [[ -e $conf_backup ]]
205 | then
206 | echo "Unlikely for $conf_backup to exist!"
207 | exit 4
208 | else
209 | echo "Backing up $conf to $conf_backup"
210 | mv "$conf" "$conf_backup"
211 | fi
212 | else
213 | echo "$conf doesn't exist"
214 | fi
215 | }
216 |
217 | # Get the GPG keyid using the given homedir.
218 | function get_keyid {
219 | $GPG --homedir="$1" --card-status | grep 'Signature key' | cut -f2 -d: | tr -d ' '
220 | }
221 |
222 | function vercomp {
223 | if [[ $1 == "$2" ]]
224 | then
225 | return 0
226 | fi
227 | local IFS=.
228 | # shellcheck disable=SC2206
229 | local i ver1=($1) ver2=($2)
230 | # fill empty fields in ver1 with zeros
231 | for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
232 | do
233 | ver1[i]=0
234 | done
235 | for ((i=0; i<${#ver1[@]}; i++))
236 | do
237 | if [[ -z ${ver2[i]} ]]
238 | then
239 | # fill empty fields in ver2 with zeros
240 | ver2[i]=0
241 | fi
242 | if ((10#${ver1[i]} > 10#${ver2[i]}))
243 | then
244 | return 1
245 | fi
246 | if ((10#${ver1[i]} < 10#${ver2[i]}))
247 | then
248 | return 2
249 | fi
250 | done
251 | return 0
252 | }
253 |
254 | function join { local IFS="$1"; shift; echo "$*"; }
255 |
256 | # https://stackoverflow.com/a/44348249
257 | function install_or_upgrade {
258 | local pkg
259 | pkg="$1"
260 | if "$PKG_CHECK" "$PKG_CHECK_ARGS" "$pkg" >/dev/null; then
261 | eval "$PKG_MANAGER_ENV" "$PKG_MANAGER" "$PKG_MANAGER_UPGRADE" "$pkg"
262 | else
263 | eval "$PKG_MANAGER_ENV" "$PKG_MANAGER" "$PKG_MANAGER_INSTALL" "$pkg"
264 | fi
265 | }
266 |
267 | function check_presence {
268 | local pkg
269 | pkg="$1"
270 | if ! "$PKG_CHECK" "$PKG_CHECK_ARGS" "$pkg" >/dev/null 2>&1; then
271 | echo "$pkg is missing, please install it"
272 | return 1
273 | fi
274 | }
275 |
--------------------------------------------------------------------------------
/expects/expect-arch.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env expect
2 | #
3 | # This Expect script was generated by autoexpect on Wed Aug 1 17:10:59 2018
4 | # Expect and autoexpect were both written by Don Libes, NIST.
5 | #
6 | # Note that autoexpect does not guarantee a working script. It
7 | # necessarily has to guess about certain things. Two reasons a script
8 | # might fail are:
9 | #
10 | # 1) timing - A surprising number of programs (rn, ksh, zsh, telnet,
11 | # etc.) and devices discard or ignore keystrokes that arrive "too
12 | # quickly" after prompts. If you find your new script hanging up at
13 | # one spot, try adding a short sleep just before the previous send.
14 | # Setting "force_conservative" to 1 (see below) makes Expect do this
15 | # automatically - pausing briefly before sending each character. This
16 | # pacifies every program I know of. The -c flag makes the script do
17 | # this in the first place. The -C flag allows you to define a
18 | # character to toggle this mode off and on.
19 |
20 | set force_conservative 1 ;# set to 1 to force conservative mode even if
21 | ;# script was not run conservatively originally
22 | if {$force_conservative} {
23 | set send_slow {1 .1}
24 | proc send {ignore arg} {
25 | sleep .1
26 | exp_send -s -- $arg
27 | }
28 | }
29 |
30 | #
31 | # 2) differing output - Some programs produce different output each time
32 | # they run. The "date" command is an obvious example. Another is
33 | # ftp, if it produces throughput statistics at the end of a file
34 | # transfer. If this causes a problem, delete these patterns or replace
35 | # them with wildcards. An alternative is to use the -p flag (for
36 | # "prompt") which makes Expect only look for the last line of output
37 | # (i.e., the prompt). The -P flag allows you to define a character to
38 | # toggle this mode off and on.
39 | #
40 | # Read the man page for more info.
41 | #
42 | # -Don
43 |
44 | set timeout -1
45 | match_max 100000
46 |
47 | # https://stackoverflow.com/a/17060172
48 | set TOUCH_POLICY [lindex $argv 0];
49 | set ADMIN_PIN [lindex $argv 1];
50 | set GPG_HOMEDIR [lindex $argv 2];
51 | set USER_PIN [lindex $argv 3];
52 | set KEY_LENGTH [lindex $argv 4];
53 | set REALNAME [lindex $argv 5];
54 | set EMAIL [lindex $argv 6];
55 | set COMMENT [lindex $argv 7];
56 |
57 | # Turn off OTP.
58 | send_user "Turning off YubiKey OTP:\n"
59 | spawn ykman config mode "FIDO+CCID"
60 | expect {
61 | "Mode is already FIDO+CCID, nothing to do..." {
62 | expect eof
63 | }
64 |
65 | ": " {
66 | send -- "y\r"
67 | expect eof
68 | }
69 | }
70 |
71 | # Set up User and Admin PINs, and then generate keys on card.
72 |
73 | send_user "Now generating your GPG keys on the YubiKey itself.\n"
74 | spawn gpg --homedir=$GPG_HOMEDIR --card-edit
75 |
76 | expect -exact "gpg/card> "
77 | send -- "admin\r"
78 |
79 | # https://developers.yubico.com/PGP/Card_edit.html
80 |
81 | expect -exact "gpg/card> "
82 | send -- "passwd\r"
83 |
84 | # Change User PIN
85 | expect -exact "Your selection? "
86 | send -- "1\r"
87 |
88 | # Default User PIN
89 | expect -exact "PIN: "
90 | send -- "123456\r"
91 |
92 | # New User PIN
93 | expect -exact "PIN: "
94 | send -- "$USER_PIN\r"
95 |
96 | # Repeat new User PIN
97 | expect -exact "PIN: "
98 | send -- "$USER_PIN\r"
99 |
100 | # Change Admin PIN
101 | expect -exact "Your selection? "
102 | send -- "3\r"
103 |
104 | # Default Admin PIN
105 | expect -exact "Admin PIN: "
106 | send -- "12345678\r"
107 |
108 | # New Admin PIN
109 | expect -exact "Admin PIN: "
110 | send -- "$ADMIN_PIN\r"
111 |
112 | # Repeat new Admin PIN
113 | expect -exact "Admin PIN: "
114 | send -- "$ADMIN_PIN\r"
115 |
116 | # Get out of passwd menu
117 | expect -exact "Your selection? "
118 | send -- "q\r"
119 |
120 | # Set desired key attributes.
121 |
122 | expect -exact "gpg/card> "
123 | send -- "key-attr\r"
124 |
125 | # Signature key.
126 | expect -exact "Your selection? "
127 | # RSA
128 | send -- "1\r"
129 |
130 | expect "What keysize do you want? (*) "
131 | send -- "$KEY_LENGTH\r"
132 |
133 | # Send new Admin PIN
134 | expect -exact "Admin PIN: "
135 | send -- "$ADMIN_PIN\r"
136 |
137 | # Encryption key.
138 | expect -exact "Your selection? "
139 | # RSA
140 | send -- "1\r"
141 |
142 | expect "What keysize do you want? (*) "
143 | send -- "$KEY_LENGTH\r"
144 |
145 | # Send new Admin PIN
146 | expect -exact "Admin PIN: "
147 | send -- "$ADMIN_PIN\r"
148 |
149 | # Authentication key.
150 | expect -exact "Your selection? "
151 | # RSA
152 | send -- "1\r"
153 |
154 | expect "What keysize do you want? (*) "
155 | send -- "$KEY_LENGTH\r"
156 |
157 | # Send new Admin PIN
158 | expect -exact "Admin PIN: "
159 | send -- "$ADMIN_PIN\r"
160 |
161 | # Time to generate.
162 |
163 | expect -exact "gpg/card> "
164 | send -- "generate\r"
165 |
166 | expect -exact "Make off-card backup of encryption key? (Y/n) "
167 | send -- "n\r"
168 |
169 | # Send new User PIN
170 | expect -exact "PIN: "
171 | send -- "$USER_PIN\r"
172 |
173 | expect -exact "Key is valid for? (0) "
174 | send -- "10y\r"
175 |
176 | expect -exact "Is this correct? (y/N) "
177 | send -- "y\r"
178 |
179 | expect -exact "Real name: "
180 | send -- "$REALNAME\r"
181 |
182 | expect -exact "E-mail address: "
183 | send -- "$EMAIL\r"
184 |
185 | expect -exact "Comment: "
186 | send -- "$COMMENT\r"
187 |
188 | expect -exact "Change (N)ame, (C)omment, (E)-mail or (O)kay/(Q)uit? "
189 | send -- "O\r"
190 |
191 | # Send new Admin PIN
192 | expect -exact "Admin PIN: "
193 | send -- "$ADMIN_PIN\r"
194 |
195 | send_user "\nNow generating keys on card, lights will be flashing, this will take a few minutes, please wait...\n"
196 |
197 | # Send new User PIN
198 | expect {
199 | "PIN: " {
200 | send -- "$USER_PIN\r"
201 | expect -exact "gpg/card> "
202 | send -- "quit\r"
203 | }
204 | "gpg/card> " { send -- "quit\r" }
205 | }
206 |
207 | expect eof
208 |
209 | # Turn on touch for SIGNATURES.
210 |
211 | send_user "Now requiring you to touch your YubiKey to sign any message.\n"
212 | spawn ykman openpgp keys set-touch sig $TOUCH_POLICY
213 |
214 | expect -exact "Enter Admin PIN: "
215 | stty -echo
216 | send -- "$ADMIN_PIN\r"
217 |
218 | expect -exact "Set touch policy of SIG key to $TOUCH_POLICY? \[y/N\]: "
219 | send -- "y\r"
220 | expect eof
221 |
222 | # Turn on touch for AUTHENTICATION.
223 |
224 | send_user "Now requiring you to touch your YubiKey to authenticate SSH.\n"
225 | spawn ykman openpgp keys set-touch aut on
226 |
227 | expect -exact "Enter Admin PIN: "
228 | stty -echo
229 | send -- "$ADMIN_PIN\r"
230 |
231 | expect -exact "Set touch policy of AUT key to on? \[y/N\]: "
232 | send -- "y\r"
233 | expect eof
234 |
235 | # Turn on touch for ENCRYPTION.
236 |
237 | send_user "Now requiring you to touch your YubiKey to encrypt any message.\n"
238 | spawn ykman openpgp keys set-touch enc on
239 |
240 | expect -exact "Enter Admin PIN: "
241 | stty -echo
242 | send -- "$ADMIN_PIN\r"
243 |
244 | expect -exact "Set touch policy of ENC key to on? \[y/N\]: "
245 | send -- "y\r"
246 | expect eof
247 |
248 | # Touch for ATTESTATION works only for Yubico firmware >= 5.2.3.
249 | # https://support.yubico.com/support/solutions/articles/15000027139-yubikey-5-2-3-enhancements-to-openpgp-3-4-support
250 |
--------------------------------------------------------------------------------
/expects/expect-macos.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/expect
2 | #
3 | # This Expect script was generated by autoexpect on Wed Aug 1 17:10:59 2018
4 | # Expect and autoexpect were both written by Don Libes, NIST.
5 | #
6 | # Note that autoexpect does not guarantee a working script. It
7 | # necessarily has to guess about certain things. Two reasons a script
8 | # might fail are:
9 | #
10 | # 1) timing - A surprising number of programs (rn, ksh, zsh, telnet,
11 | # etc.) and devices discard or ignore keystrokes that arrive "too
12 | # quickly" after prompts. If you find your new script hanging up at
13 | # one spot, try adding a short sleep just before the previous send.
14 | # Setting "force_conservative" to 1 (see below) makes Expect do this
15 | # automatically - pausing briefly before sending each character. This
16 | # pacifies every program I know of. The -c flag makes the script do
17 | # this in the first place. The -C flag allows you to define a
18 | # character to toggle this mode off and on.
19 |
20 | set force_conservative 1 ;# set to 1 to force conservative mode even if
21 | ;# script was not run conservatively originally
22 | if {$force_conservative} {
23 | set send_slow {1 .1}
24 | proc send {ignore arg} {
25 | sleep .1
26 | exp_send -s -- $arg
27 | }
28 | }
29 |
30 | #
31 | # 2) differing output - Some programs produce different output each time
32 | # they run. The "date" command is an obvious example. Another is
33 | # ftp, if it produces throughput statistics at the end of a file
34 | # transfer. If this causes a problem, delete these patterns or replace
35 | # them with wildcards. An alternative is to use the -p flag (for
36 | # "prompt") which makes Expect only look for the last line of output
37 | # (i.e., the prompt). The -P flag allows you to define a character to
38 | # toggle this mode off and on.
39 | #
40 | # Read the man page for more info.
41 | #
42 | # -Don
43 |
44 | set timeout -1
45 | match_max 100000
46 |
47 | # https://stackoverflow.com/a/17060172
48 | set TOUCH_POLICY [lindex $argv 0];
49 | set ADMIN_PIN [lindex $argv 1];
50 | set GPG_HOMEDIR [lindex $argv 2];
51 | set USER_PIN [lindex $argv 3];
52 | set KEY_LENGTH [lindex $argv 4];
53 | set REALNAME [lindex $argv 5];
54 | set EMAIL [lindex $argv 6];
55 | set COMMENT [lindex $argv 7];
56 |
57 | # Turn off OTP.
58 | send_user "Turning off YubiKey OTP:\n"
59 | spawn ykman config mode "FIDO+CCID"
60 | expect {
61 | "Mode is already FIDO+CCID, nothing to do..." {
62 | expect eof
63 | }
64 |
65 | ": " {
66 | send -- "y\r"
67 | expect eof
68 | }
69 | }
70 |
71 | # Set up User and Admin PINs, and then generate keys on card.
72 |
73 | send_user "Now generating your GPG keys on the YubiKey itself.\n"
74 | spawn gpg --homedir=$GPG_HOMEDIR --card-edit
75 |
76 | expect -exact "gpg/card> "
77 | send -- "admin\r"
78 |
79 | # https://developers.yubico.com/PGP/Card_edit.html
80 |
81 | expect -exact "gpg/card> "
82 | send -- "passwd\r"
83 |
84 | # Change User PIN
85 | expect -exact "Your selection? "
86 | send -- "1\r"
87 |
88 | # Default User PIN
89 | expect -exact "PIN: "
90 | send -- "123456\r"
91 |
92 | # New User PIN
93 | expect -exact "PIN: "
94 | send -- "$USER_PIN\r"
95 |
96 | # Repeat new User PIN
97 | expect -exact "PIN: "
98 | send -- "$USER_PIN\r"
99 |
100 | # Change Admin PIN
101 | expect -exact "Your selection? "
102 | send -- "3\r"
103 |
104 | # Default Admin PIN
105 | expect -exact "Admin PIN: "
106 | send -- "12345678\r"
107 |
108 | # New Admin PIN
109 | expect -exact "Admin PIN: "
110 | send -- "$ADMIN_PIN\r"
111 |
112 | # Repeat new Admin PIN
113 | expect -exact "Admin PIN: "
114 | send -- "$ADMIN_PIN\r"
115 |
116 | # Get out of passwd menu
117 | expect -exact "Your selection? "
118 | send -- "q\r"
119 |
120 | # Set desired key attributes.
121 |
122 | expect -exact "gpg/card> "
123 | send -- "key-attr\r"
124 |
125 | # Signature key.
126 | expect -exact "Your selection? "
127 | # RSA
128 | send -- "1\r"
129 |
130 | expect "What keysize do you want? (*) "
131 | send -- "$KEY_LENGTH\r"
132 |
133 | # Send new Admin PIN
134 | expect -exact "Admin PIN: "
135 | send -- "$ADMIN_PIN\r"
136 |
137 | # Encryption key.
138 | expect -exact "Your selection? "
139 | # RSA
140 | send -- "1\r"
141 |
142 | expect "What keysize do you want? (*) "
143 | send -- "$KEY_LENGTH\r"
144 |
145 | # Send new Admin PIN
146 | expect -exact "Admin PIN: "
147 | send -- "$ADMIN_PIN\r"
148 |
149 | # Authentication key.
150 | expect -exact "Your selection? "
151 | # RSA
152 | send -- "1\r"
153 |
154 | expect "What keysize do you want? (*) "
155 | send -- "$KEY_LENGTH\r"
156 |
157 | # Send new Admin PIN
158 | expect -exact "Admin PIN: "
159 | send -- "$ADMIN_PIN\r"
160 |
161 | # Time to generate.
162 |
163 | expect -exact "gpg/card> "
164 | send -- "generate\r"
165 |
166 | expect -exact "Make off-card backup of encryption key? (Y/n) "
167 | send -- "n\r"
168 |
169 | # Send new User PIN
170 | expect -exact "PIN: "
171 | send -- "$USER_PIN\r"
172 |
173 | expect -exact "Key is valid for? (0) "
174 | send -- "10y\r"
175 |
176 | expect -exact "Is this correct? (y/N) "
177 | send -- "y\r"
178 |
179 | expect -exact "Real name: "
180 | send -- "$REALNAME\r"
181 |
182 | expect -exact "Email address: "
183 | send -- "$EMAIL\r"
184 |
185 | expect -exact "Comment: "
186 | send -- "$COMMENT\r"
187 |
188 | expect -exact "Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? "
189 | send -- "O\r"
190 |
191 | # Send new Admin PIN
192 | expect -exact "Admin PIN: "
193 | send -- "$ADMIN_PIN\r"
194 |
195 | send_user "\nNow generating keys on card, lights will be flashing, this will take a few minutes, please wait...\n"
196 |
197 | # Send new User PIN
198 | expect {
199 | "PIN: " {
200 | send -- "$USER_PIN\r"
201 | expect -exact "gpg/card> "
202 | send -- "quit\r"
203 | }
204 | "gpg/card> " { send -- "quit\r" }
205 | }
206 |
207 | expect eof
208 |
209 | # Turn on touch for SIGNATURES.
210 |
211 | send_user "Now requiring you to touch your YubiKey to sign any message.\n"
212 | spawn ykman openpgp keys set-touch sig $TOUCH_POLICY
213 |
214 | expect -exact "Enter Admin PIN: "
215 | stty -echo
216 | send -- "$ADMIN_PIN\r"
217 |
218 | expect -exact "Set touch policy of SIG key to $TOUCH_POLICY? \[y/N\]: "
219 | send -- "y\r"
220 | expect eof
221 |
222 | # Turn on touch for AUTHENTICATION.
223 |
224 | send_user "Now requiring you to touch your YubiKey to authenticate SSH.\n"
225 | spawn ykman openpgp keys set-touch aut on
226 |
227 | expect -exact "Enter Admin PIN: "
228 | stty -echo
229 | send -- "$ADMIN_PIN\r"
230 |
231 | expect -exact "Set touch policy of AUT key to on? \[y/N\]: "
232 | send -- "y\r"
233 | expect eof
234 |
235 | # Turn on touch for ENCRYPTION.
236 |
237 | send_user "Now requiring you to touch your YubiKey to encrypt any message.\n"
238 | spawn ykman openpgp keys set-touch enc on
239 |
240 | expect -exact "Enter Admin PIN: "
241 | stty -echo
242 | send -- "$ADMIN_PIN\r"
243 |
244 | expect -exact "Set touch policy of DEC key to on? \[y/N\]: "
245 | send -- "y\r"
246 | expect eof
247 |
248 | # Touch for ATTESTATION works only for Yubico firmware >= 5.2.3.
249 | # https://support.yubico.com/support/solutions/articles/15000027139-yubikey-5-2-3-enhancements-to-openpgp-3-4-support
250 |
--------------------------------------------------------------------------------
/expects/expect-ubuntu.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env expect
2 | #
3 | # This Expect script was generated by autoexpect on Wed Aug 1 17:10:59 2018
4 | # Expect and autoexpect were both written by Don Libes, NIST.
5 | #
6 | # Note that autoexpect does not guarantee a working script. It
7 | # necessarily has to guess about certain things. Two reasons a script
8 | # might fail are:
9 | #
10 | # 1) timing - A surprising number of programs (rn, ksh, zsh, telnet,
11 | # etc.) and devices discard or ignore keystrokes that arrive "too
12 | # quickly" after prompts. If you find your new script hanging up at
13 | # one spot, try adding a short sleep just before the previous send.
14 | # Setting "force_conservative" to 1 (see below) makes Expect do this
15 | # automatically - pausing briefly before sending each character. This
16 | # pacifies every program I know of. The -c flag makes the script do
17 | # this in the first place. The -C flag allows you to define a
18 | # character to toggle this mode off and on.
19 |
20 | set force_conservative 1 ;# set to 1 to force conservative mode even if
21 | ;# script was not run conservatively originally
22 | if {$force_conservative} {
23 | set send_slow {1 .1}
24 | proc send {ignore arg} {
25 | sleep .1
26 | exp_send -s -- $arg
27 | }
28 | }
29 |
30 | #
31 | # 2) differing output - Some programs produce different output each time
32 | # they run. The "date" command is an obvious example. Another is
33 | # ftp, if it produces throughput statistics at the end of a file
34 | # transfer. If this causes a problem, delete these patterns or replace
35 | # them with wildcards. An alternative is to use the -p flag (for
36 | # "prompt") which makes Expect only look for the last line of output
37 | # (i.e., the prompt). The -P flag allows you to define a character to
38 | # toggle this mode off and on.
39 | #
40 | # Read the man page for more info.
41 | #
42 | # -Don
43 |
44 | set timeout -1
45 | match_max 100000
46 |
47 | # https://stackoverflow.com/a/17060172
48 | set TOUCH_POLICY [lindex $argv 0];
49 | set ADMIN_PIN [lindex $argv 1];
50 | set GPG_HOMEDIR [lindex $argv 2];
51 | set USER_PIN [lindex $argv 3];
52 | set KEY_LENGTH [lindex $argv 4];
53 | set REALNAME [lindex $argv 5];
54 | set EMAIL [lindex $argv 6];
55 | set COMMENT [lindex $argv 7];
56 |
57 | # Turn off OTP.
58 | send_user "Turning off YubiKey OTP:\n"
59 | spawn ykman config mode "FIDO+CCID"
60 | expect {
61 | "Mode is already FIDO+CCID, nothing to do..." {
62 | expect eof
63 | }
64 |
65 | ": " {
66 | send -- "y\r"
67 | expect eof
68 | }
69 | }
70 |
71 | # Set up User and Admin PINs, and then generate keys on card.
72 |
73 | send_user "Now generating your GPG keys on the YubiKey itself.\n"
74 | spawn gpg --homedir=$GPG_HOMEDIR --card-edit
75 |
76 | expect -exact "gpg/card> "
77 | send -- "admin\r"
78 |
79 | # https://developers.yubico.com/PGP/Card_edit.html
80 |
81 | expect -exact "gpg/card> "
82 | send -- "passwd\r"
83 |
84 | # Change User PIN
85 | expect -exact "Your selection? "
86 | send -- "1\r"
87 |
88 | # Default User PIN
89 | expect -exact "PIN: "
90 | send -- "123456\r"
91 |
92 | # New User PIN
93 | expect -exact "PIN: "
94 | send -- "$USER_PIN\r"
95 |
96 | # Repeat new User PIN
97 | expect -exact "PIN: "
98 | send -- "$USER_PIN\r"
99 |
100 | # Change Admin PIN
101 | expect -exact "Your selection? "
102 | send -- "3\r"
103 |
104 | # Default Admin PIN
105 | expect -exact "Admin PIN: "
106 | send -- "12345678\r"
107 |
108 | # New Admin PIN
109 | expect -exact "Admin PIN: "
110 | send -- "$ADMIN_PIN\r"
111 |
112 | # Repeat new Admin PIN
113 | expect -exact "Admin PIN: "
114 | send -- "$ADMIN_PIN\r"
115 |
116 | # Get out of passwd menu
117 | expect -exact "Your selection? "
118 | send -- "q\r"
119 |
120 | # Set desired key attributes.
121 |
122 | expect -exact "gpg/card> "
123 | send -- "key-attr\r"
124 |
125 | # Signature key.
126 | expect -exact "Your selection? "
127 | # RSA
128 | send -- "1\r"
129 |
130 | expect "What keysize do you want? (*) "
131 | send -- "$KEY_LENGTH\r"
132 |
133 | # Send new Admin PIN
134 | expect -exact "Admin PIN: "
135 | send -- "$ADMIN_PIN\r"
136 |
137 | # Encryption key.
138 | expect -exact "Your selection? "
139 | # RSA
140 | send -- "1\r"
141 |
142 | expect "What keysize do you want? (*) "
143 | send -- "$KEY_LENGTH\r"
144 |
145 | # Send new Admin PIN
146 | expect -exact "Admin PIN: "
147 | send -- "$ADMIN_PIN\r"
148 |
149 | # Authentication key.
150 | expect -exact "Your selection? "
151 | # RSA
152 | send -- "1\r"
153 |
154 | expect "What keysize do you want? (*) "
155 | send -- "$KEY_LENGTH\r"
156 |
157 | # Send new Admin PIN
158 | expect -exact "Admin PIN: "
159 | send -- "$ADMIN_PIN\r"
160 |
161 | # Time to generate.
162 |
163 | expect -exact "gpg/card> "
164 | send -- "generate\r"
165 |
166 | expect -exact "Make off-card backup of encryption key? (Y/n) "
167 | send -- "n\r"
168 |
169 | # Send new User PIN
170 | expect -exact "PIN: "
171 | send -- "$USER_PIN\r"
172 |
173 | expect -exact "Key is valid for? (0) "
174 | send -- "10y\r"
175 |
176 | expect -exact "Is this correct? (y/N) "
177 | send -- "y\r"
178 |
179 | expect -exact "Real name: "
180 | send -- "$REALNAME\r"
181 |
182 | expect -exact "Email address: "
183 | send -- "$EMAIL\r"
184 |
185 | expect -exact "Comment: "
186 | send -- "$COMMENT\r"
187 |
188 | expect -exact "Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? "
189 | send -- "O\r"
190 |
191 | # Send new Admin PIN
192 | expect -exact "Admin PIN: "
193 | send -- "$ADMIN_PIN\r"
194 |
195 | send_user "\nNow generating keys on card, lights will be flashing, this will take a few minutes, please wait...\n"
196 |
197 | # Send new User PIN
198 | expect {
199 | "PIN: " {
200 | send -- "$USER_PIN\r"
201 | expect -exact "gpg/card> "
202 | send -- "quit\r"
203 | }
204 | "gpg/card> " { send -- "quit\r" }
205 | }
206 |
207 | expect eof
208 |
209 | # Turn on touch for SIGNATURES.
210 |
211 | send_user "Now requiring you to touch your YubiKey to sign any message.\n"
212 | spawn ykman openpgp keys set-touch sig $TOUCH_POLICY
213 |
214 | expect -exact "Enter Admin PIN: "
215 | stty -echo
216 | send -- "$ADMIN_PIN\r"
217 |
218 | expect -exact "Set touch policy of SIG key to $TOUCH_POLICY? \[y/N\]: "
219 | send -- "y\r"
220 | expect eof
221 |
222 | # Turn on touch for AUTHENTICATION.
223 |
224 | send_user "Now requiring you to touch your YubiKey to authenticate SSH.\n"
225 | spawn ykman openpgp keys set-touch aut on
226 |
227 | expect -exact "Enter Admin PIN: "
228 | stty -echo
229 | send -- "$ADMIN_PIN\r"
230 |
231 | expect -exact "Set touch policy of AUT key to on? \[y/N\]: "
232 | send -- "y\r"
233 | expect eof
234 |
235 | # Turn on touch for ENCRYPTION.
236 |
237 | send_user "Now requiring you to touch your YubiKey to encrypt any message.\n"
238 | spawn ykman openpgp keys set-touch enc on
239 |
240 | expect -exact "Enter Admin PIN: "
241 | stty -echo
242 | send -- "$ADMIN_PIN\r"
243 |
244 | expect -exact "Set touch policy of DEC key to on? \[y/N\]: "
245 | send -- "y\r"
246 | expect eof
247 |
248 | # Touch for ATTESTATION works only for Yubico firmware >= 5.2.3.
249 | # https://support.yubico.com/support/solutions/articles/15000027139-yubikey-5-2-3-enhancements-to-openpgp-3-4-support
250 |
--------------------------------------------------------------------------------
/git.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Stop on error.
4 | set -e
5 |
6 | source env.sh
7 | source realname-and-email.sh
8 |
9 | # Determine whether to set globally or locally.
10 | OLD_PWD="$(pwd)"
11 | if [[ -z "$1" ]]
12 | then
13 | echo "Signing git commits & tags ${GREEN}${BOLD}GLOBALLY${RESET}"
14 | SCOPE="--global"
15 | else
16 | echo "Signing git commits & tags ${GREEN}${BOLD}LOCALLY${RESET}: $1"
17 | SCOPE="--local"
18 | cd "$1"
19 | fi
20 |
21 |
22 | source "$OLD_PWD"/lib/git_conf.sh
23 | cd "$OLD_PWD"
24 |
25 | # If scope local, only configure git, and don't try to push the key
26 | # to github and set up the notifications
27 | if [[ "${SCOPE}" == "--local" ]]; then
28 | exit 0
29 | fi
30 |
31 | # Export GPG public key to GitHub.
32 | echo "Exporting your GPG public key to GitHub."
33 | $GPG --armor --export "$KEYID" | $CLIP $CLIP_ARGS
34 | echo "It has been copied to your clipboard."
35 | echo "${YELLOW}You may now add it to GitHub: https://github.com/settings/gpg/new${RESET}"
36 | echo "${GREEN}Opening GitHub...${RESET}"
37 | $OPEN "https://github.com/settings/gpg/new"
38 | echo
39 |
40 | # Turn on notifications.
41 | source lib/notifications.sh $SCOPE
42 | echo "${GREEN}Enjoy signing your git commits with your YubiKey!${RESET}"
43 |
--------------------------------------------------------------------------------
/gpg.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Stop on error.
4 | set -e
5 |
6 | # shellcheck disable=SC1091
7 | source env.sh
8 | source lib/install.sh
9 |
10 | # Get full name and email address.
11 | # shellcheck disable=SC1091
12 | source realname-and-email.sh
13 |
14 | # Get comment to distinguish between keys.
15 | COMMENT="GPG on YubiKey for Datadog"
16 | echo "${YELLOW}What is a comment you would like to use to distinguish this key?"
17 | read -rp "Comment (press Enter to accept '$COMMENT'): ${RESET}" input
18 | COMMENT=${input:-$COMMENT}
19 | echo
20 |
21 | # Generate some information for the user.
22 | USER_PIN=$(python3 -S -c "import random; print(random.SystemRandom().randrange(10**7,10**8))")
23 | ADMIN_PIN=$(python3 -S -c "import random; print(random.SystemRandom().randrange(10**7,10**8))")
24 | SERIAL=$($YKMAN info | grep 'Serial number:' | cut -f2 -d: | tr -d ' ')
25 |
26 | # Set some parameters based on whether FIPS key or not.
27 | DEVICE_TYPE=$($YKMAN info | grep 'Device type:' | cut -f2 -d: | awk '{$1=$1;print}')
28 | echo "YubiKey device type: $DEVICE_TYPE"
29 | if [[ "$DEVICE_TYPE" == *"YubiKey"*"FIPS"* ]]; then
30 | echo "Which appears to be a FIPS key"
31 | YUBIKEY_FIPS=true
32 | # YubiKey FIPS supports at most RSA-3072 on-card key generation, which should
33 | # be good until at least 2030 according to NIST:
34 | # https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp3204.pdf
35 | # https://www.keylength.com/en/compare/
36 | KEY_LENGTH=3072
37 | else
38 | echo "Which does not appear to be a FIPS key"
39 | YUBIKEY_FIPS=false
40 | KEY_LENGTH=4096
41 | fi
42 | # Activate cache policy if possible (when firmware version equal or superior to 5.2.3
43 | # https://github.com/Yubico/yubikey-manager/issues/277#issuecomment-529805540
44 | FIRMWARE_VERSION=$($YKMAN info | grep 'Firmware version:' | cut -f2 -d: | awk '{$1=$1;print}')
45 | set +e
46 | vercomp "$FIRMWARE_VERSION" 5.2.3
47 | if [[ "$?" -eq 2 ]] || [[ "$YUBIKEY_FIPS" == "true" ]]; then
48 | echo "Setting touch policy to on"
49 | TOUCH_POLICY=on
50 | else
51 | echo "Setting touch policy to cached"
52 | TOUCH_POLICY=cached
53 | fi
54 | set -e
55 | echo
56 |
57 | source lib/tree.sh
58 |
59 | # Update scdaemon.conf
60 | source lib/scdaemon.sh
61 |
62 | # Show card information to user so they can be sure they are wiping right key
63 | # NOTE: explicitly check against default GPG homedir to make sure we are not wiping something critical...
64 | echo "YubiKey status:"
65 | # shellcheck disable=SC2153
66 | $GPG --card-status
67 | echo
68 |
69 | # Reset YubiKey openPGP applet
70 | echo "${YELLOW}RESETTING THE OPENGPG APPLET ON YOUR YUBIKEY!!!"
71 | $YKMAN openpgp reset
72 | echo "${RESET}"
73 |
74 | # Whatever our GPG homedir, we replace pinentry-curses with pinentry-tty, so that we can automate entering User and Admin PINs.
75 | GPG_AGENT_CONF=$GPG_HOMEDIR/gpg-agent.conf
76 | cat << EOF > "$GPG_AGENT_CONF"
77 | pinentry-program $PINENTRY_SETUP
78 | EOF
79 |
80 | source lib/gpg_conf.sh
81 | # force locale to prevent expect script from breaking on non-english systems.
82 | old_locale="${LC_ALL}"
83 | export LC_ALL=en_US.UTF-8
84 |
85 | # drive yubikey setup
86 | # but right before, kill all GPG daemons to make sure things work reliably
87 | $GPGCONF --homedir="$GPG_HOMEDIR" --kill all
88 | # The script failed to run when GPG_TTY != '' so we ensure it's empty before the expect script.
89 | # https://gnupg.org/documentation/manuals/gnupg/Common-Problems.html
90 | GPG_TTY="" ./expects/"expect-${OS}.sh" "$TOUCH_POLICY" "$ADMIN_PIN" "$GPG_HOMEDIR" "$USER_PIN" "$KEY_LENGTH" "$REALNAME" "$EMAIL" "$COMMENT"
91 | echo
92 |
93 | # restore initial locale value
94 | export LC_ALL="${old_locale}"
95 |
96 | source lib/gpg_agent_conf.sh
97 |
98 | # restart GPG daemons to pick up pinentry-mac
99 | $GPGCONF --kill all
100 |
101 | echo "There are two important random numbers for the YubiKey you MUST keep safely."
102 | echo "See https://developers.yubico.com/yubikey-piv-manager/PIN_and_Management_Key.html"
103 | echo
104 |
105 | echo "The first number is the User PIN."
106 | echo "The User PIN is used during normal operation to authorize an action such as issuing a new GPG signature."
107 | echo "${GREEN}"
108 | echo "***********************************************************"
109 | echo "New User PIN: $USER_PIN"
110 | echo "***********************************************************"
111 | echo "${RESET}"
112 | echo "${YELLOW}Please save this new User PIN (copied to clipboard) immediately in your password manager.${RESET}"
113 | echo "$USER_PIN" | $CLIP $CLIP_ARGS
114 | read -rp "${YELLOW}Have you done this?${RESET}"
115 | echo "${YELLOW}Please also associate it with this YubiKey serial number (copied to clipboard): ${SERIAL}${RESET}"
116 | echo "$SERIAL" | $CLIP $CLIP_ARGS
117 | read -rp "${YELLOW}Have you done this? ${RESET}"
118 | echo
119 |
120 | echo "The second number is the Admin PIN."
121 | echo "The Admin PIN can be used to reset the PIN if it is ever lost or becomes blocked after the maximum number of incorrect attempts."
122 | echo "${GREEN}"
123 | echo "***********************************************************"
124 | echo "New Admin PIN: $ADMIN_PIN"
125 | echo "***********************************************************"
126 | echo "${RESET}"
127 | echo "${YELLOW}Please save this new Admin PIN (copied to clipboard) immediately in your password manager.${RESET}"
128 | echo "$ADMIN_PIN" | $CLIP $CLIP_ARGS
129 | read -rp "${YELLOW}Have you done this? ${RESET}"
130 | echo "${YELLOW}Please also associate it with this YubiKey serial number (copied to clipboard): ${SERIAL}${RESET}"
131 | echo "$SERIAL" | $CLIP $CLIP_ARGS
132 | read -rp "${YELLOW}Have you done this?${RESET}"
133 | echo
134 |
135 | # Export GPG public key.
136 | KEYID=$(get_keyid "$GPG_HOMEDIR")
137 | BIN_GPG_PUBKEY=$KEYID.gpg.pub.bin
138 | ASC_GPG_PUBKEY=$KEYID.gpg.pub.asc
139 | echo "${GREEN}Exporting your binary GPG public key to $(pwd)/${BIN_GPG_PUBKEY}${RESET}"
140 | $GPG --homedir="$GPG_HOMEDIR" --export "$KEYID" > "$BIN_GPG_PUBKEY"
141 | echo "${GREEN}Exporting your ASCII-armored GPG public key to $(pwd)/${ASC_GPG_PUBKEY}${RESET}"
142 | $GPG --homedir="$GPG_HOMEDIR" --armor --export "$KEYID" > "$ASC_GPG_PUBKEY"
143 | echo "$ASC_GPG_PUBKEY" | $CLIP $CLIP_ARGS
144 | echo "${YELLOW}Please save a copy in your password manager.${RESET}"
145 | read -rp "${YELLOW}Have you done this? ${RESET}"
146 | echo "There is NO off-card backup of your private / secret keys."
147 | echo "So, if your YubiKey is damaged, lost, or stolen, then you must rotate your GPG keys out-of-band."
148 | echo "You would also no longer be able to decrypt messages encrypted for this GPG key."
149 | echo
150 |
151 | # Ask user to save revocation certificate before deleting it.
152 | REVOCATION_CERT=$GPG_HOMEDIR/openpgp-revocs.d/$KEYID.rev
153 | echo "$REVOCATION_CERT" | $CLIP $CLIP_ARGS
154 | echo "${GREEN}Your revocation certificate is at ${REVOCATION_CERT}${RESET}"
155 | echo "It has been copied to your clipboard."
156 | echo "${YELLOW}Please save a copy in your password manager before we delete it off disk.${RESET}"
157 | read -rp "${YELLOW}Have you done this? ${RESET}"
158 | rm "$REVOCATION_CERT"
159 | echo "Great. Deleted this revocation certificate from disk."
160 | # NOTE: EMPTY clipboard after this.
161 | $CLIP $CLIP_ARGS < /dev/null
162 | echo
163 |
164 | # Final reminders.
165 | echo "Finally, remember that your keys will not ${GREEN}expire until 10 years from now.${RESET}"
166 | echo "You will need to ${GREEN}${BOLD}enter your User PIN (once a day)${RESET}, and ${GREEN}${BOLD}touch your YubiKey${RESET} in order to sign any message with this GPG key."
167 | if [[ "$TOUCH_POLICY" == "on" ]]; then
168 | echo "${YELLOW}You may wish to pass the --no-gpg-sign flag to git rebase.${RESET}"
169 | else
170 | echo "Touch is cached for 15s on sign operations."
171 | fi
172 | echo "Enjoy using your YubiKey at Datadog!"
173 |
--------------------------------------------------------------------------------
/import.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Stop on error.
4 | set -e
5 |
6 |
7 | function usage ()
8 | {
9 | cat << EOF
10 | Usage : $0 [options] [--]
11 |
12 | Options:
13 | -h|--help OPTIONAL Display this message
14 | -p|--public REQUIRED Path to your public key on disk
15 | -i|--id REQUIRED Key ID you are importing
16 | EOF
17 |
18 | }
19 |
20 | if [[ $# -lt 1 ]]; then
21 | echo -e "Missing arguments\n"
22 | usage
23 | exit 1
24 | fi
25 |
26 | while [[ $# -gt 0 ]]; do
27 | case $1 in
28 | -h|--help)
29 | usage
30 | exit 0
31 | ;;
32 | -p|--public)
33 | publickey_path="$2"
34 | shift 2
35 | ;;
36 | -i|--id)
37 | keyid="$2"
38 | shift 2
39 | ;;
40 | *)
41 | echo -e "ERROR: An unknown flag was passed: ${1}\n"
42 | usage
43 | ;;
44 | esac
45 | done
46 | if [[ -z "$publickey_path" ]] || [[ -z "$keyid" ]]; then
47 | usage
48 | exit 1
49 | fi
50 | if [[ ! -f "$publickey_path" ]] \
51 | || [[ "$(head -n 1 "$publickey_path")" != "-----BEGIN PGP PUBLIC KEY BLOCK-----" ]]; then
52 | echo "Public key $publickey_path is not GPG public key, exiting"
53 | exit 1
54 | fi
55 |
56 | # shellcheck disable=SC1091
57 | source env.sh
58 | source lib/install.sh
59 | source lib/tree.sh
60 | source lib/gpg_conf.sh
61 | source lib/gpg_agent_conf.sh
62 |
63 | # Configure scdaemon.
64 | source lib/scdaemon.sh
65 | echo "YubiKey status:"
66 | # https://security.stackexchange.com/questions/108190/export-secret-key-after-yubikey-is-plugged-in
67 | $GPG --card-status
68 | echo
69 |
70 | # Import the GPG public key.
71 | echo "Importing your GPG public key..."
72 | $GPG --import "$publickey_path"
73 | echo
74 | echo -e "5\ny\n" | $GPG --no-tty --command-fd 0 --edit-key "$keyid" trust
75 |
76 | read -rp "${YELLOW}Do you also want to use GPG on your YubiKey to sign git commits? (y/n)${RESET}" answer
77 | case "$answer" in
78 | yes|YES|y|Y|Yes)
79 | echo "Configuring git to use GPG signing subkey..."
80 | export SCOPE="--global"
81 | source lib/git_conf.sh
82 | source lib/notifications.sh $SCOPE
83 | ;;
84 | *)
85 | echo "Skipping signing git commits."
86 | esac
87 | echo
88 |
89 | # Authenticating over SSH with their GPG authentication subkey OTOH is a different story.
90 | read -rp "${YELLOW}Do you also want to use GPG on your YubiKey to authenticate over SSH? (y/n)${RESET}" answer
91 | case "$answer" in
92 | yes|YES|y|Y|Yes)
93 | echo "Configuring SSH to use GPG authentication subkey..."
94 | source lib/ssh_conf.sh
95 | ;;
96 | *)
97 | echo "Skipping using GPG authentication subkey for SSH."
98 | esac
99 | echo
100 |
101 | echo "All done! Enjoy reusing your YubiKey on your new computer."
102 |
--------------------------------------------------------------------------------
/lib/git_conf.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Set git name and email.
4 | echo "Setting your git-config user.name..."
5 | $GIT config $SCOPE user.name "$REALNAME"
6 | echo "Setting your git-config user.email..."
7 | $GIT config $SCOPE user.email "$EMAIL"
8 |
9 | # Ask user whether all git commits and tags should be signed.
10 | KEYID=$(get_keyid "$DEFAULT_GPG_HOMEDIR")
11 | echo "Setting git to use this GPG key."
12 | echo "Also, turning on signing of all commits and tags by default."
13 | # Tell git to use this GPG key.
14 | $GIT config $SCOPE user.signingkey "$KEYID"
15 | # Also, turn on signing commits and tags by default.
16 | $GIT config $SCOPE commit.gpgsign true
17 | $GIT config $SCOPE tag.forceSignAnnotated true
18 | echo
19 |
--------------------------------------------------------------------------------
/lib/gpg_agent_conf.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Overwrite default GPG agent configuration with our own.
4 | # We want to replace the pinentry-tty with the pinentry-mac.
5 | cat << EOF > "$DEFAULT_GPG_AGENT_CONF"
6 | # https://www.gnupg.org/documentation/manuals/gnupg/Agent-Options.html
7 | pinentry-program $PINENTRY
8 | # For usability while balancing security, cache User PIN for at most a day.
9 | default-cache-ttl 86400
10 | max-cache-ttl 86400
11 | EOF
12 |
13 |
--------------------------------------------------------------------------------
/lib/gpg_conf.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Backup GPG configuration in default GPG homedir, if it exists.
4 | backup_conf "$DEFAULT_GPG_CONF"
5 |
6 | # https://csrc.nist.rip/groups/STM/cmvp/documents/140-1/140crt/140crt1130.pdf
7 | # https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf
8 | # Specify crypto algorithms that will be used
9 | # Note: the goal is to favor algorithms:
10 | # - without known vulnerabilties
11 | # - with a long key and block sizes
12 | GPG_CONF=$GPG_HOMEDIR/gpg.conf
13 | cat << EOF > "$GPG_CONF"
14 | disable-pubkey-algo ELG
15 | disable-pubkey-algo DSA
16 | disable-cipher-algo 3DES
17 | disable-cipher-algo BLOWFISH
18 | disable-cipher-algo CAMELLIA256
19 | disable-cipher-algo CAMELLIA128
20 | disable-cipher-algo CAMELLIA192
21 | disable-cipher-algo CAST5
22 | disable-cipher-algo IDEA
23 | disable-cipher-algo TWOFISH
24 | personal-cipher-preferences AES256 AES192 AES
25 | personal-digest-preferences SHA512 SHA384 SHA256 SHA224
26 | personal-compress-preferences BZIP2 ZLIB ZIP Uncompressed
27 | default-preference-list AES256 AES192 AES SHA512 SHA384 SHA256 SHA224 BZIP2 ZLIB ZIP Uncompressed
28 | EOF
29 |
--------------------------------------------------------------------------------
/lib/install.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Install required dependencies.
4 | echo "${YELLOW}You need to have $(join ',' "${DEPS[@]}") installed on your device."
5 | read -rp "Do you want us to install them for you ? (y/n)${RESET}" answer
6 | case "$answer" in
7 | yes|YES|y|Y|Yes)
8 | # install required tools
9 | echo "Installing or upgrading required tools..."
10 | eval "$PKG_MANAGER_ENV" "$PKG_MANAGER" "$PKG_MANAGER_UPDATE"
11 | for pkg in "${DEPS[@]}"; do
12 | install_or_upgrade "$pkg"
13 | done
14 | ;;
15 | *)
16 | echo "Skipping install or upgrade of required tools"
17 | for pkg in "${DEPS[@]}"; do
18 | check_presence "$pkg"
19 | done
20 | ;;
21 | esac
22 | echo
23 |
24 |
--------------------------------------------------------------------------------
/lib/notifications.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Look if the file is sourced or directly called. if not source env.sh
4 | # https://stackoverflow.com/a/2684300
5 | [[ ${BASH_SOURCE[0]} != "$0" ]] || source env.sh
6 |
7 | # Honour git.sh setting or set to --global by default
8 | # Besides you can specify --local when directly called
9 | SCOPE=${1:---global}
10 | echo "Turning on notifications for signing git commits & tags ${GREEN}${BOLD}${SCOPE//--/}ly${RESET}"
11 |
12 | set -e
13 |
14 | # Create a bin directory where user has write access
15 | mkdir -p "$USER_BIN_DIR"
16 |
17 | echo "Deploying the notifications script"
18 | sed -E -e 's/%%NOTIFICATION_NOTIFY%%/'"$NOTIFICATION_NOTIFY"'/' bin/gpg-sign-notify > "$NOTIFICATION_SCRIPT_PATH"
19 | chmod u+x "$NOTIFICATION_SCRIPT_PATH"
20 | echo "${GREEN}The notifications script has been deployed${RESET}"
21 |
22 | $GIT config "$SCOPE" --add gpg.program "$NOTIFICATION_SCRIPT_PATH"
23 | echo "${GREEN}Notifications have been set up in git${RESET}"
24 |
--------------------------------------------------------------------------------
/lib/scdaemon.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Stop on error.
4 | set -e
5 |
6 | # shellcheck disable=SC1091
7 | [[ ${BASH_SOURCE[0]} != "$0" ]] || source env.sh
8 |
9 | echo "${GREEN}Generating scdaemon.conf.${RESET}"
10 |
11 | # Sometimes on macOS, a gpg update make the yubikey detection flaky or completely impossible
12 | # So we enforce the scdaemon.conf configuration to detect the YubiKey as it is on macOS only
13 | # cf env.sh
14 | # https://gpgtools.tenderapp.com/discussions/problems/58454-after-updating-to-gpgtools-20171-yubikey-no-longer-functions-properly-both-in-mail-gpg2-card-edit/page/1
15 | if [[ -n "$SCDAEMON_CONF" ]]; then
16 | backup_conf "$DEFAULT_GPG_SCDAEMON_CONF"
17 | echo -e "$SCDAEMON_CONF" > "$DEFAULT_GPG_SCDAEMON_CONF"
18 | fi
19 |
20 | $GPGCONF --kill all
21 |
--------------------------------------------------------------------------------
/lib/ssh_conf.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | function configure_shell {
4 | case $(basename "$SHELL") in
5 | bash)
6 | config_file="${HOME}/.bashrc"
7 | ;;
8 | zsh)
9 | config_file="${HOME}/.zshrc"
10 | ;;
11 | fish)
12 | config_file="${HOME}/.config/fish/config.fish"
13 | ;;
14 | *)
15 | config_file="${HOME}/.profile"
16 | ;;
17 | esac
18 |
19 | config_file_basename=$(basename "$config_file")
20 | echo "$config_file_basename detected"
21 | if [[ "$config_file_basename" == "config.fish" ]]; then
22 | RC_SSH_CONF="set -gx SSH_AUTH_SOCK \$($GPGCONF --list-dirs agent-ssh-socket)"
23 | else
24 | RC_SSH_CONF="export SSH_AUTH_SOCK=\$($GPGCONF --list-dirs agent-ssh-socket)"
25 | fi
26 | if ! grep -q "gpg-agent.ssh" "$config_file"; then
27 | echo "$RC_SSH_CONF" >> "$config_file"
28 | fi
29 | eval "$RC_SSH_CONF"
30 | if [[ "$SSH_AUTH_SOCK" != "$($GPGCONF --list-dirs agent-ssh-socket)" ]]; then
31 | echo "Failed to configure SSH_AUTH_SOCK in $config_file"
32 | exit 1
33 | fi
34 | }
35 |
36 | if ! grep -q "enable-ssh-support" "$DEFAULT_GPG_AGENT_CONF"; then
37 | # enable ssh support
38 | echo "enable-ssh-support" >> "$DEFAULT_GPG_AGENT_CONF"
39 | fi
40 | # NOTE: Kill existing SSH and GPG agents, and start GPG agent manually (with SSH
41 | # support added above) to maximize odds of picking up SSH key.
42 | killall ssh-agent || echo "ssh-agent was not running."
43 | $GPGCONF --kill all
44 | # put set +e before trying running the gpg agent as it can be already running according to the OS and return != 0
45 | set +e
46 | $GPG_AGENT --daemon
47 | set -e
48 | if [[ -f "$SSH_ENV" ]]; then
49 | rm -f "$SSH_ENV"
50 | fi
51 |
52 | configure_shell
53 |
--------------------------------------------------------------------------------
/lib/tree.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Figure out whether we need to write GPG keys to a tempdir.
4 | # This is useful when you need to generate keys for someone else w/o adding to your own keystore.
5 | if [[ -z "$TEMPDIR" ]]
6 | then
7 | GPG_HOMEDIR=$DEFAULT_GPG_HOMEDIR
8 | echo "Using *default* GPG homedir: $GPG_HOMEDIR"
9 | else
10 | GPG_HOMEDIR=$(mktemp -d)
11 | echo "Using *temp* GPG homedir: $GPG_HOMEDIR"
12 | fi
13 | echo
14 |
15 | # Create default directories.
16 | mkdir -p "$GPG_HOMEDIR"
17 | chmod 700 "$GPG_HOMEDIR"
18 |
19 | # Backup GPG agent configuration in default GPG homedir, if it exists.
20 | backup_conf "$DEFAULT_GPG_AGENT_CONF"
21 |
--------------------------------------------------------------------------------
/realname-and-email.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Stop on error.
4 | set -e
5 |
6 | # Get some information from the user.
7 |
8 | # 1. Real name.
9 | REALNAME=$($GIT config --global --default '' --get user.name)
10 | echo "${YELLOW}What is the real name you use on GitHub?"
11 | read -rp "Real name (press Enter to accept '$REALNAME')${RESET}: " input
12 |
13 | if [[ -z $REALNAME ]]
14 | then
15 | if [[ -z $input ]]
16 | then
17 | echo "${RED}No name given!${RESET}"
18 | exit 1
19 | else
20 | REALNAME=$input
21 | echo "Using given input: $REALNAME"
22 | fi
23 | else
24 | if [[ -z $input ]]
25 | then
26 | echo "Using given user.name: $REALNAME"
27 | else
28 | REALNAME=$input
29 | echo "Using given input: $REALNAME"
30 | fi
31 | fi
32 |
33 | REALNAME_LEN=${#REALNAME}
34 | if [[ $REALNAME_LEN -lt 5 ]]
35 | then
36 | echo "${RED}Real name has $REALNAME_LEN < 5 characters!${RESET}"
37 | exit 2
38 | fi
39 |
40 | echo
41 |
42 | # 2. Email address.
43 | EMAIL=$($GIT config --global --default '' --get user.email)
44 | echo "${YELLOW}What is an email address you have registered with GitHub?"
45 | read -rp "Email (press Enter to accept '$EMAIL'): ${RESET}" input
46 |
47 | if [[ -z $EMAIL ]]
48 | then
49 | if [[ -z $input ]]
50 | then
51 | echo "${RED}No email given!${RESET}"
52 | exit 3
53 | else
54 | EMAIL=$input
55 | echo "Using given input: $EMAIL"
56 | fi
57 | else
58 | if [[ -z $input ]]
59 | then
60 | echo "Using given user.email: $EMAIL"
61 | else
62 | EMAIL=$input
63 | echo "Using given input: $EMAIL"
64 | fi
65 | fi
66 |
67 | echo
68 |
--------------------------------------------------------------------------------
/reset.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | source env.sh
3 |
4 | shopt -s extglob
5 |
6 | confirm() {
7 | local msg
8 |
9 | msg="$1"
10 |
11 | echo "$msg"
12 | read -rn 3 answer
13 | case $answer in
14 | Yes|yes|Y|y|YES)
15 | echo
16 | return 0
17 | ;;
18 | *)
19 | echo "Cancelling"
20 | return 1
21 | esac
22 | }
23 |
24 | reset_device() {
25 | local serial
26 | serial="$1"
27 |
28 | for i in $(seq 1 2); do
29 | if ! $YKMAN --device "${serial}" otp delete "$i" -f >/dev/null 2>&1; then
30 | echo "Warning the slot $i didn't contain OTP configuration or an error happened"
31 | fi
32 | done
33 | $YKMAN --device "${serial}" oath reset -f
34 | $YKMAN --device "${serial}" openpgp reset -f
35 | $YKMAN --device "${serial}" piv reset -f
36 | $YKMAN --device "${serial}" fido reset
37 | }
38 |
39 | yubikeys=$($YKMAN list --serials)
40 | select serial in all $yubikeys cancel; do
41 | echo "You chose $serial"
42 | case $serial in
43 | all)
44 | confirm "Are you sure you want to reset $yubikeys ? yes/no" || exit 0
45 | for yubikey in $yubikeys; do
46 | echo "Reset $yubikey"
47 | reset_device "$yubikey"
48 | done
49 | break
50 | ;;
51 | cancel)
52 | echo "Cancelled"
53 | break
54 | ;;
55 | # https://www.linuxjournal.com/content/bash-extended-globbing
56 | +([0-9]))
57 | confirm "Are you sure you want to reset $serial ? yes/no" || exit 0
58 | echo "Reset $serial"
59 | reset_device "$serial"
60 | break
61 | ;;
62 | *)
63 | echo "Unexpected error, exiting"
64 | exit 1
65 | ;;
66 | esac
67 | done
68 |
69 |
70 |
--------------------------------------------------------------------------------
/ssh.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Stop on error.
4 | set -e
5 |
6 | source env.sh
7 |
8 | # Sets gpg-agent for SSH_AUTH_SOCK in the user's profile & environment
9 | echo "${GREEN}Setting the GPG agent for SSH_AUTH_SOCK to handle SSH actions${RESET}"
10 | echo "${YELLOW}This includes git actions like pushing and pulling from Github${RESET}"
11 | echo "${YELLOW}If this breaks your workflow, edit your profile to remove it${RESET}"
12 | echo "${YELLOW}And please open a ticket to let us know: https://github.com/DataDog/yubikey${RESET}"
13 | source lib/ssh_conf.sh
14 |
15 | # Export SSH key derived from GPG authentication subkey.
16 | KEYID=$(get_keyid "$DEFAULT_GPG_HOMEDIR")
17 | SSH_PUBKEY=$KEYID.ssh.pub
18 | echo "${YELLOW}Exporting your SSH public key to ${SSH_PUBKEY}${RESET}"
19 | ssh-add -L | grep -iF 'cardno' > "$SSH_PUBKEY"
20 | cat "$SSH_PUBKEY" | $CLIP $CLIP_ARGS
21 | echo "It has also been copied to your clipboard."
22 | echo "${YELLOW}You may now add it to GitHub: https://github.com/settings/ssh/new${RESET}"
23 | echo "${GREEN}Opening GitHub...${RESET}"
24 | $OPEN "https://github.com/settings/ssh/new"
25 | echo "${YELLOW}Please save a copy in your password manager.${RESET}"
26 | read -rp "${YELLOW}Have you done this? ${RESET}"
27 | echo "Great."
28 | echo
29 | echo "You will need to ${GREEN}${BOLD}enter your PIN (once a day)${RESET}, and ${GREEN}${BOLD}touch your YubiKey everytime${RESET} in order to use SSH."
30 | echo
31 | echo "Enjoy authenticating over SSH with your YubiKey at Datadog!"
32 |
--------------------------------------------------------------------------------