├── .gitignore ├── LICENSE ├── README.org ├── default.nix ├── jobsets ├── default.nix ├── release.nix └── spec.json ├── lib └── default.nix ├── nix ├── sources.json └── sources.nix └── pkgs ├── cfssl ├── 1.3.4.nix └── 1.4.1.nix ├── drduh-gpg-conf └── default.nix ├── gpg-scripts └── default.nix └── yk-scripts └── default.nix /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | .tramp_history 4 | result 5 | result-* 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, Drew Hess 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Drew Hess nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * nixos-yubikey 2 | 3 | Create NixOS images suitable for initializing YubiKeys. 4 | 5 | This repository builds a bootable NixOS image that includes all of 6 | the software you'll typically need to initialize a YubiKey, and to 7 | configure it for use with GnuPG and SSH. It follows recommended 8 | security practices by disabling network interfaces and running the 9 | configuration environment from a ramdisk (not only the ~$GNUPGHOME~, 10 | but also the entire NixOS filesystem). The only way to write state 11 | to persistent storage is by explicit user action. 12 | 13 | ** Requirements 14 | 15 | You'll need the following: 16 | 17 | - An environment capable of building [[https://github.com/NixOS/nixpkgs][Nixpkgs]] for ~x86_64-linux~ 18 | hosts. 19 | 20 | - At least one YubiKey, preferably a YubiKey 4 or later. 21 | 22 | - At least one USB flash drive (preferably 2 or more), for keeping 23 | your master GnuPG key offline and secure. 24 | 25 | - An ~x86_64-linux~ host that you trust, and that can be 26 | "airgapped" during the key generation and YubiKey provisioning 27 | process. The host should have at least 2GB of RAM, because the 28 | NixOS image will copy its filesystem to RAM and run from there to 29 | prevent key leakage to persistent storage. 30 | 31 | ** Usage 32 | 33 | 1. Build the NixOS bootable image. 34 | #+BEGIN_SRC sh 35 | nix build -f default.nix nixos-yubikey 36 | #+END_SRC 37 | 2. Copy the ISO file in ~result/iso~ to a USB stick or CD/DVD. 38 | 3. Boot the image on trusted hardware. 39 | 4. Follow one of the guides below. 40 | 41 | ** Guides 42 | 43 | There are numerous guides on how to initialize YubiKeys and to 44 | prepare them for use with GnuPG and SSH. Below are the guides I 45 | found most useful and/or prudent, but whether you also find them 46 | useful or prudent will depend on your own security preferences and 47 | needs. If you have the time, I think it's a good idea to review 48 | each one of them before proceeding with your own YubiKey 49 | provisioning, because each guide has at least one or two insights 50 | or rationales that the others lack, meaning you're less likely to 51 | miss something important. 52 | 53 | As of May 2019, few of the guides below include instructions 54 | specific to NixOS, but for the most part you can skip the 55 | OS-specific instructions (e.g., which packages you'll need to 56 | install), as this image should include everything you need, and is 57 | easy to modify if there's something missing. Furthermore, because 58 | you shouldn't need to install any additional software, the image 59 | disables your machine's network interfaces from the very beginning 60 | of the process, so you can also ignore the bits of the guides that 61 | warn you to disable networking after installing packages. (Of 62 | course, it's always a good idea to ensure that all network 63 | interfaces are disabled before proceeding with key generation, 64 | anyway, in case of a bug or misconfiguration.) 65 | 66 | - [[https://github.com/drduh/YubiKey-Guide][DrDuh's YubiKey guide]] 67 | 68 | This one is my personal favorite. Note that the ~gpg.conf~ 69 | referred to in this guide is already set up for you when you open 70 | a shell in the NixOS YubiKey image. 71 | 72 | - [[https://rzetterberg.github.io/yubikey-gpg-nixos.html][Setting up GnuPG + YubiKey on NixoS for SSH authentication]] 73 | 74 | Contains some NixOS-specific information, all of which has been 75 | incorporated into this NixOS YubiKey image. 76 | 77 | - [[https://www.forgesi.net/gpg-ssh-with-the-yubikey-5/][GPG/SSH with the YubiKey 5]] 78 | 79 | Probably the next best guide I found after DrDuh's guide. 80 | 81 | - [[https://www.andreagrandi.it/2017/09/30/configuring-offline-gnupg-masterkey-subkeys-on-yubikey/][Configuring an offline GnuPG master key and subkeys on YubiKey]] 82 | 83 | - [[https://shankarkulumani.com/2019/03/gpg.html][Starting with GPG and YubiKey]] 84 | 85 | Probably the most "gentle" of the guides. 86 | 87 | *** Renewing subkeys 88 | 89 | DrDuh's guide [[https://github.com/drduh/YubiKey-Guide#renewing-sub-keys][now covers subkey renewal]], which is much simpler 90 | than rotating keys. Note that once you've renewed your subkeys, 91 | you'll need to re-export your keys (including the public key, 92 | which will need to be updated in all the usual places), but you do 93 | *not* need to update the subkeys on the YubiKey. 94 | 95 | ** Other useful information 96 | 97 | Debian's (and Debian developers') guides to using subkeys and why 98 | they're useful are probably the best resources on these topics, 99 | though they're not specific to YubiKeys (or even hardware keys at 100 | all): 101 | 102 | - [[https://wiki.debian.org/Subkeys][Using OpenPGP subkeys in Debian development]] 103 | 104 | - [[https://wiki.debian.org/OfflineMasterKey][Offline master key]] 105 | 106 | - [[https://wiki.debian.org/GnuPG/AirgappedMasterKey][Airgapped master key]] 107 | 108 | - [[https://github.com/tomlowenthal/documentation/blob/master/gpg/smartcard-keygen.md][Smartcard keygen]] 109 | 110 | This guide doesn't cover Yubikeys in any depth, but it does a good 111 | job of covering out to create additional GPG ID's (i.e., additional 112 | email addresses associated with your key), and also more 113 | information on how to use ~hopenpgp-tools~ and ~pgpdump~: 114 | 115 | - [[https://blog.tinned-software.net/create-gnupg-key-with-sub-keys-to-sign-encrypt-authenticate/][Create GnuPG with sub-keys to sign, encrypt, authenticate]] 116 | 117 | Everyone recommends using a 2nd YubiKey to make a backup of your 118 | primary YubiKey, but in practice, using 2 or more YubiKeys with the 119 | same subkeys is tricky. Here are some resources for more 120 | information on this subject, plus the currently best-known 121 | workarounds: 122 | 123 | - [[https://github.com/drduh/YubiKey-Guide/issues/19][Using two yubikeys not covered under guide]] 124 | 125 | - [[https://forum.yubico.com/viewtopic38a1.html?f=35&t=2400#p10091][Use PGP keys on multiple yubikeys]] 126 | 127 | If you want to use your Yubikey with VMware Workstation or VMware 128 | Fusion, you'll need to edit your virtual machine's VMX file: 129 | 130 | - [[https://support.yubico.com/support/solutions/articles/15000008891-troubleshooting-vmware-workstation-device-passthrough][Troubleshooting VMWare Workstation Device Passthrough]] 131 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | let 2 | 3 | localLib = import ./lib; 4 | outPath = localLib.fixedNixpkgs; 5 | 6 | in 7 | { system ? "x86_64-linux" 8 | , crossSystem ? null 9 | , config ? { allowBroken = true; } 10 | , supportedSystems ? [ "x86_64-linux" ] 11 | , nixpkgs ? { inherit outPath; revCount = 56789; shortRev = "gfedcba"; } 12 | , pkgs ? import nixpkgs { inherit system crossSystem config; } 13 | }: 14 | 15 | let 16 | 17 | gpg-agent-conf = pkgs.writeText "gpg-agent.conf" '' 18 | pinentry-program ${pkgs.pinentry-curses}/bin/pinentry-curses 19 | ''; 20 | 21 | yk-scripts = pkgs.callPackage pkgs/yk-scripts {}; 22 | 23 | gpg-scripts = pkgs.callPackage pkgs/gpg-scripts {}; 24 | 25 | drduh-gpg-conf = pkgs.callPackage pkgs/drduh-gpg-conf {}; 26 | 27 | cfssl_1_4_1 = pkgs.callPackage pkgs/cfssl/1.4.1.nix {}; 28 | 29 | nixos-yubikey-configuration = { 30 | 31 | ## Image overrides. 32 | 33 | isoImage.isoBaseName = pkgs.lib.mkForce "nixos-yubikey"; 34 | 35 | # Always copytoram so that, if the image is booted from, e.g., a 36 | # USB stick, nothing is mistakenly written to persistent storage. 37 | 38 | boot.kernelParams = [ "copytoram" ]; 39 | 40 | ## Required packages and services. 41 | # 42 | # ref: https://rzetterberg.github.io/yubikey-gpg-nixos.html 43 | environment.systemPackages = with pkgs; [ 44 | cfssl_1_4_1 45 | cryptsetup 46 | diceware 47 | ent 48 | git 49 | gitAndTools.git-extras 50 | gnupg 51 | gpg-scripts 52 | (haskell.lib.justStaticExecutables haskellPackages.hopenpgp-tools) 53 | paperkey 54 | parted 55 | pcsclite 56 | pcsctools 57 | pgpdump 58 | pinentry-curses 59 | pwgen 60 | yk-scripts 61 | yubikey-manager 62 | yubikey-personalization 63 | ]; 64 | services.udev.packages = [ 65 | pkgs.yubikey-personalization 66 | ]; 67 | services.pcscd.enable = true; 68 | 69 | 70 | ## Make sure networking is disabled in every way possible. 71 | 72 | boot.initrd.network.enable = false; 73 | networking.dhcpcd.enable = false; 74 | networking.dhcpcd.allowInterfaces = []; 75 | networking.firewall.enable = true; 76 | networking.useDHCP = false; 77 | networking.useNetworkd = false; 78 | networking.wireless.enable = false; 79 | 80 | 81 | ## Make it easy to tell which nixpkgs the image was built from. 82 | # 83 | # Most of the following config is thanks to Graham Christensen, 84 | # from: 85 | # https://github.com/grahamc/network/blob/1d73f673b05a7f976d82ae0e0e61a65d045b3704/modules/standard/default.nix#L56 86 | 87 | nix = { 88 | useSandbox = true; 89 | nixPath = [ 90 | # Copy the channel version from the deploy host to the target 91 | "nixpkgs=/run/current-system/nixpkgs" 92 | ]; 93 | }; 94 | system.extraSystemBuilderCmds = '' 95 | ln -sv ${pkgs.path} $out/nixpkgs 96 | ''; 97 | environment.etc.host-nix-channel.source = pkgs.path; 98 | 99 | 100 | ## Secure defaults. 101 | 102 | boot.cleanTmpDir = true; 103 | boot.kernel.sysctl = { 104 | "kernel.unprivileged_bpf_disabled" = 1; 105 | }; 106 | 107 | 108 | ## Set up the shell for making keys. 109 | 110 | environment.interactiveShellInit = '' 111 | unset HISTFILE 112 | export GNUPGHOME=/run/user/$(id -u)/gnupg 113 | [ -d $GNUPGHOME ] || install -m 0700 -d $GNUPGHOME 114 | cp ${drduh-gpg-conf}/gpg.conf $GNUPGHOME/gpg.conf 115 | cp ${gpg-agent-conf} $GNUPGHOME/gpg-agent.conf 116 | echo "\$GNUPGHOME is $GNUPGHOME" 117 | ''; 118 | }; 119 | 120 | nixos-yubikey-configuration-uk = nixos-yubikey-configuration // { 121 | console.keyMap = "uk"; 122 | i18n.defaultLocale = "en_GB.UTF-8"; 123 | }; 124 | 125 | ## Build the images. 126 | 127 | nixos = import (localLib.fixedNixpkgs + "/nixos/release.nix") { 128 | inherit supportedSystems nixpkgs; 129 | configuration = nixos-yubikey-configuration; 130 | }; 131 | nixos-yubikey = nixos.iso_minimal; 132 | 133 | nixos-uk = import (localLib.fixedNixpkgs + "/nixos/release.nix") { 134 | inherit supportedSystems nixpkgs; 135 | configuration = nixos-yubikey-configuration-uk; 136 | }; 137 | nixos-yubikey-uk = nixos-uk.iso_minimal; 138 | 139 | in 140 | { 141 | inherit gpg-scripts; 142 | inherit yk-scripts; 143 | inherit nixos-yubikey; 144 | inherit nixos-yubikey-uk; 145 | } 146 | -------------------------------------------------------------------------------- /jobsets/default.nix: -------------------------------------------------------------------------------- 1 | # Based on 2 | # https://github.com/input-output-hk/iohk-ops/blob/df01a228e559e9a504e2d8c0d18766794d34edea/jobsets/default.nix 3 | 4 | { nixpkgs ? 5 | , declInput ? {} 6 | }: 7 | 8 | let 9 | 10 | nixosYubiKeyUri = "https://github.com/dhess/nixos-yubikey.git"; 11 | 12 | mkFetchGithub = value: { 13 | inherit value; 14 | type = "git"; 15 | emailresponsible = false; 16 | }; 17 | 18 | pkgs = import nixpkgs {}; 19 | 20 | defaultSettings = { 21 | enabled = 1; 22 | hidden = false; 23 | keepnr = 20; 24 | schedulingshares = 100; 25 | checkinterval = 60; 26 | enableemail = false; 27 | emailoverride = ""; 28 | nixexprpath = "jobsets/release.nix"; 29 | nixexprinput = "nixosYubiKey"; 30 | description = "nixos-yubikey"; 31 | inputs = { 32 | nixosYubiKey = mkFetchGithub "${nixosYubiKeyUri} master"; 33 | }; 34 | }; 35 | 36 | # Build against a nixpkgs-channels repo. This can run fairly often 37 | # as the channels don't update so much. 38 | mkNixpkgsChannels = nixosYubiKeyBranch: nixpkgsRev: { 39 | checkinterval = 60 * 60; 40 | inputs = { 41 | nixosYubiKey = mkFetchGithub "${nixosYubiKeyUri} ${nixosYubiKeyBranch}"; 42 | nixpkgs_override = mkFetchGithub "https://github.com/NixOS/nixpkgs-channels.git ${nixpkgsRev}"; 43 | }; 44 | }; 45 | 46 | # Build against the nixpkgs repo. Runs less often due to nixpkgs' 47 | # velocity. 48 | mkNixpkgs = nixosYubiKeyBranch: nixpkgsRev: { 49 | checkinterval = 60 * 60 * 12; 50 | inputs = { 51 | nixosYubiKey = mkFetchGithub "${nixosYubiKeyUri} ${nixosYubiKeyBranch}"; 52 | nixpkgs_override = mkFetchGithub "https://github.com/NixOS/nixpkgs.git ${nixpkgsRev}"; 53 | }; 54 | }; 55 | 56 | mainJobsets = with pkgs.lib; mapAttrs (name: settings: defaultSettings // settings) (rec { 57 | master = {}; 58 | nixos-unstable = mkNixpkgsChannels "master" "nixos-unstable"; 59 | nixpkgs-unstable = mkNixpkgsChannels "master" "nixpkgs-unstable"; 60 | nixpkgs = mkNixpkgs "master" "master"; 61 | }); 62 | 63 | jobsetsAttrs = mainJobsets; 64 | 65 | jobsetJson = pkgs.writeText "spec.json" (builtins.toJSON jobsetsAttrs); 66 | 67 | in { 68 | jobsets = with pkgs.lib; pkgs.runCommand "spec.json" {} '' 69 | cat <" try.value 11 | else src; 12 | 13 | sources = import ../nix/sources.nix; 14 | 15 | fixedNixpkgs = fixedNixSrc "nixpkgs_override" sources.nixpkgs; 16 | nixpkgs = import fixedNixpkgs; 17 | pkgs = nixpkgs {}; 18 | lib = pkgs.lib; 19 | 20 | in lib // 21 | { 22 | inherit fixedNixSrc fixedNixpkgs; 23 | inherit nixpkgs pkgs; 24 | } 25 | -------------------------------------------------------------------------------- /nix/sources.json: -------------------------------------------------------------------------------- 1 | { 2 | "niv": { 3 | "branch": "master", 4 | "description": "Easy dependency management for Nix projects", 5 | "homepage": "https://github.com/nmattia/niv", 6 | "owner": "nmattia", 7 | "repo": "niv", 8 | "rev": "ba57d5a29b4e0f2085917010380ef3ddc3cf380f", 9 | "sha256": "1kpsvc53x821cmjg1khvp1nz7906gczq8mp83664cr15h94sh8i4", 10 | "type": "tarball", 11 | "url": "https://github.com/nmattia/niv/archive/ba57d5a29b4e0f2085917010380ef3ddc3cf380f.tar.gz", 12 | "url_template": "https://github.com///archive/.tar.gz" 13 | }, 14 | "nixpkgs": { 15 | "branch": "nixos-20.03", 16 | "description": "Nix Packages collection", 17 | "homepage": "", 18 | "owner": "NixOS", 19 | "repo": "nixpkgs", 20 | "rev": "9518fac712ca001009bd12a3c94621f1ee805657", 21 | "sha256": "13clqhmmxdinn7figq8plxkxnwja96x7d79ghl1831rw8ys9x9dn", 22 | "type": "tarball", 23 | "url": "https://github.com/NixOS/nixpkgs/archive/9518fac712ca001009bd12a3c94621f1ee805657.tar.gz", 24 | "url_template": "https://github.com///archive/.tar.gz" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /nix/sources.nix: -------------------------------------------------------------------------------- 1 | # This file has been generated by Niv. 2 | 3 | let 4 | 5 | # 6 | # The fetchers. fetch_ fetches specs of type . 7 | # 8 | 9 | fetch_file = pkgs: name: spec: 10 | let 11 | name' = sanitizeName name + "-src"; 12 | in 13 | if spec.builtin or true then 14 | builtins_fetchurl { inherit (spec) url sha256; name = name'; } 15 | else 16 | pkgs.fetchurl { inherit (spec) url sha256; name = name'; }; 17 | 18 | fetch_tarball = pkgs: name: spec: 19 | let 20 | name' = sanitizeName name + "-src"; 21 | in 22 | if spec.builtin or true then 23 | builtins_fetchTarball { name = name'; inherit (spec) url sha256; } 24 | else 25 | pkgs.fetchzip { name = name'; inherit (spec) url sha256; }; 26 | 27 | fetch_git = name: spec: 28 | let 29 | ref = 30 | if spec ? ref then spec.ref else 31 | if spec ? branch then "refs/heads/${spec.branch}" else 32 | if spec ? tag then "refs/tags/${spec.tag}" else 33 | abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!"; 34 | in 35 | builtins.fetchGit { url = spec.repo; inherit (spec) rev; inherit ref; }; 36 | 37 | fetch_local = spec: spec.path; 38 | 39 | fetch_builtin-tarball = name: throw 40 | ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`. 41 | $ niv modify ${name} -a type=tarball -a builtin=true''; 42 | 43 | fetch_builtin-url = name: throw 44 | ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`. 45 | $ niv modify ${name} -a type=file -a builtin=true''; 46 | 47 | # 48 | # Various helpers 49 | # 50 | 51 | # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695 52 | sanitizeName = name: 53 | ( 54 | concatMapStrings (s: if builtins.isList s then "-" else s) 55 | ( 56 | builtins.split "[^[:alnum:]+._?=-]+" 57 | ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name) 58 | ) 59 | ); 60 | 61 | # The set of packages used when specs are fetched using non-builtins. 62 | mkPkgs = sources: system: 63 | let 64 | sourcesNixpkgs = 65 | import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; }; 66 | hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath; 67 | hasThisAsNixpkgsPath = == ./.; 68 | in 69 | if builtins.hasAttr "nixpkgs" sources 70 | then sourcesNixpkgs 71 | else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then 72 | import {} 73 | else 74 | abort 75 | '' 76 | Please specify either (through -I or NIX_PATH=nixpkgs=...) or 77 | add a package called "nixpkgs" to your sources.json. 78 | ''; 79 | 80 | # The actual fetching function. 81 | fetch = pkgs: name: spec: 82 | 83 | if ! builtins.hasAttr "type" spec then 84 | abort "ERROR: niv spec ${name} does not have a 'type' attribute" 85 | else if spec.type == "file" then fetch_file pkgs name spec 86 | else if spec.type == "tarball" then fetch_tarball pkgs name spec 87 | else if spec.type == "git" then fetch_git name spec 88 | else if spec.type == "local" then fetch_local spec 89 | else if spec.type == "builtin-tarball" then fetch_builtin-tarball name 90 | else if spec.type == "builtin-url" then fetch_builtin-url name 91 | else 92 | abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}"; 93 | 94 | # If the environment variable NIV_OVERRIDE_${name} is set, then use 95 | # the path directly as opposed to the fetched source. 96 | replace = name: drv: 97 | let 98 | saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name; 99 | ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}"; 100 | in 101 | if ersatz == "" then drv else ersatz; 102 | 103 | # Ports of functions for older nix versions 104 | 105 | # a Nix version of mapAttrs if the built-in doesn't exist 106 | mapAttrs = builtins.mapAttrs or ( 107 | f: set: with builtins; 108 | listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)) 109 | ); 110 | 111 | # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295 112 | range = first: last: if first > last then [] else builtins.genList (n: first + n) (last - first + 1); 113 | 114 | # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257 115 | stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1)); 116 | 117 | # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269 118 | stringAsChars = f: s: concatStrings (map f (stringToCharacters s)); 119 | concatMapStrings = f: list: concatStrings (map f list); 120 | concatStrings = builtins.concatStringsSep ""; 121 | 122 | # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331 123 | optionalAttrs = cond: as: if cond then as else {}; 124 | 125 | # fetchTarball version that is compatible between all the versions of Nix 126 | builtins_fetchTarball = { url, name ? null, sha256 }@attrs: 127 | let 128 | inherit (builtins) lessThan nixVersion fetchTarball; 129 | in 130 | if lessThan nixVersion "1.12" then 131 | fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; })) 132 | else 133 | fetchTarball attrs; 134 | 135 | # fetchurl version that is compatible between all the versions of Nix 136 | builtins_fetchurl = { url, name ? null, sha256 }@attrs: 137 | let 138 | inherit (builtins) lessThan nixVersion fetchurl; 139 | in 140 | if lessThan nixVersion "1.12" then 141 | fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; })) 142 | else 143 | fetchurl attrs; 144 | 145 | # Create the final "sources" from the config 146 | mkSources = config: 147 | mapAttrs ( 148 | name: spec: 149 | if builtins.hasAttr "outPath" spec 150 | then abort 151 | "The values in sources.json should not have an 'outPath' attribute" 152 | else 153 | spec // { outPath = replace name (fetch config.pkgs name spec); } 154 | ) config.sources; 155 | 156 | # The "config" used by the fetchers 157 | mkConfig = 158 | { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null 159 | , sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile) 160 | , system ? builtins.currentSystem 161 | , pkgs ? mkPkgs sources system 162 | }: rec { 163 | # The sources, i.e. the attribute set of spec name to spec 164 | inherit sources; 165 | 166 | # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers 167 | inherit pkgs; 168 | }; 169 | 170 | in 171 | mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); } 172 | -------------------------------------------------------------------------------- /pkgs/cfssl/1.3.4.nix: -------------------------------------------------------------------------------- 1 | { stdenv 2 | , buildGoPackage 3 | , fetchFromGitHub 4 | }: 5 | 6 | buildGoPackage rec { 7 | pname = "cfssl"; 8 | version = "1.3.4"; 9 | 10 | goPackagePath = "github.com/cloudflare/cfssl"; 11 | 12 | src = fetchFromGitHub { 13 | owner = "cloudflare"; 14 | repo = "cfssl"; 15 | rev = version; 16 | sha256 = "0fpj7234xfqpbnjfrz45sx9grmr9wwsnhaz0mpfbswjll9v2d9rk"; 17 | }; 18 | 19 | meta = with stdenv.lib; { 20 | homepage = https://cfssl.org/; 21 | description = "Cloudflare's PKI and TLS toolkit"; 22 | license = licenses.bsd2; 23 | platforms = platforms.all; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /pkgs/cfssl/1.4.1.nix: -------------------------------------------------------------------------------- 1 | { stdenv 2 | , lib 3 | , buildGoModule 4 | , fetchFromGitHub 5 | }: 6 | 7 | buildGoModule rec { 8 | pname = "cfssl"; 9 | version = "1.4.1"; 10 | 11 | goPackagePath = "github.com/cloudflare/cfssl"; 12 | modSha256 = "0mqrll5dgz6i8hpk4v2i0picyc8pns39sp2l7wvm0kf3syarch01"; 13 | 14 | src = fetchFromGitHub { 15 | owner = "cloudflare"; 16 | repo = "cfssl"; 17 | rev = "v${version}"; 18 | sha256 = "07qacg95mbh94fv64y577zyr4vk986syf8h5l8lbcmpr0zcfk0pd"; 19 | }; 20 | 21 | meta = with lib; { 22 | homepage = https://cfssl.org/; 23 | description = "Cloudflare's PKI and TLS toolkit"; 24 | license = licenses.bsd2; 25 | maintainers = lib.singleton lib.maintainers.dhess; 26 | platforms = platforms.all; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /pkgs/drduh-gpg-conf/default.nix: -------------------------------------------------------------------------------- 1 | { stdenv 2 | , fetchFromGitHub 3 | }: 4 | 5 | let 6 | 7 | version = "20200213"; 8 | 9 | src = fetchFromGitHub { 10 | owner = "drduh"; 11 | repo = "config"; 12 | rev = "681a5e2252f8097e3f0ab70fc49b0977bb3cfe0c"; 13 | sha256 = "0d7d1ma9hxq1ysc2jq737602qnyjg1gkdyxc791a7g3axcxph98z"; 14 | }; 15 | 16 | in 17 | stdenv.mkDerivation { 18 | name = "drduh-gpg-conf-${version}"; 19 | 20 | inherit src; 21 | 22 | dontBuild = true; 23 | 24 | installPhase = '' 25 | mkdir $out 26 | cp $src/gpg.conf $out/gpg.conf 27 | ''; 28 | 29 | meta = with stdenv.lib; { 30 | description = "drduh's gpg.conf"; 31 | homepage = https://github.com/drduh/config; 32 | license = licenses.mit; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /pkgs/gpg-scripts/default.nix: -------------------------------------------------------------------------------- 1 | # A set of scripts to help automate some of the steps in Dr Duh's 2 | # YubiKey guide: https://github.com/drduh/YubiKey-Guide. 3 | # 4 | # Most of the heavy lifting can't be automated, because GPG isn't 5 | # easily scripted, but at least these scripts help avoid typos or 6 | # needing to remember cryptic command-line flags. 7 | # 8 | # Note that these scripts use a heuristic to determine the key ID of 9 | # the key you're generating/renewing. It probably won't work if you 10 | # have more than one secret (master) key in your keyring. Caveat 11 | # emptor. 12 | 13 | { cryptsetup 14 | , git 15 | , gnugrep 16 | , gnupg 17 | , haskell 18 | , haskellPackages 19 | , newt 20 | , parted 21 | , yubikey-manager 22 | , symlinkJoin 23 | , writeShellScriptBin 24 | , lib 25 | }: 26 | 27 | let 28 | 29 | hopenpgp-tools = (haskell.lib.justStaticExecutables haskellPackages.hopenpgp-tools); 30 | 31 | cryptsetupcmd = "${cryptsetup}/bin/cryptsetup"; 32 | gitcmd = "${git}/bin/git"; 33 | gpg = "${gnupg}/bin/gpg"; 34 | grep = "${gnugrep}/bin/grep"; 35 | hokey = "${hopenpgp-tools}/bin/hokey"; 36 | partedcmd = "${parted}/bin/parted"; 37 | whiptail = "${newt}/bin/whiptail"; 38 | 39 | get_keyid = '' 40 | if [ -f "$GNUPGHOME"/keyid ] ; then 41 | KEYID=`head "$GNUPGHOME"/keyid` 42 | else 43 | echo "$GNUPGHOME/keyid file doesn't exist." 44 | echo "Write the secret key ID to a file with that name and re-run the script." 45 | exit 70 46 | fi 47 | ''; 48 | 49 | opts = '' 50 | basename=`basename "$0"` 51 | 52 | show_help() { 53 | echo "Usage: $basename [--help]" 54 | echo 55 | echo "--help Show usage." 56 | } 57 | 58 | while :; do 59 | case $1 in 60 | -h|-\?|--help) 61 | show_help # Display a usage synopsis. 62 | exit 63 | ;; 64 | -?*) 65 | printf 'Unknown option: %s\n' "$1" >&2 66 | exit 99 67 | ;; 68 | *) # Default case: No more options, so break out of the loop. 69 | break 70 | esac 71 | shift 72 | done 73 | ''; 74 | 75 | drive-opts = '' 76 | basename=`basename "$0"` 77 | 78 | show_help() { 79 | echo "Usage: $basename [--help] DEVICE" 80 | echo 81 | echo "--help Show usage." 82 | echo "DEVICE The USB drive device name to operate on." 83 | } 84 | 85 | while :; do 86 | case $1 in 87 | -h|-\?|--help) 88 | show_help # Display a usage synopsis. 89 | exit 90 | ;; 91 | -?*) 92 | printf 'Unknown option: %s\n' "$1" >&2 93 | exit 99 94 | ;; 95 | *) # Default case: No more options, so break out of the loop. 96 | break 97 | esac 98 | shift 99 | done 100 | 101 | if [[ $# -ne 1 ]]; then 102 | echo "Please provide the device name of the USB drive you want to use, e.g., /dev/sda." 103 | echo 104 | show_help 105 | exit 2 106 | else 107 | DRIVE="$1" 108 | fi 109 | 110 | p1="1" 111 | p2="2" 112 | encrypted_partition=$DRIVE$p1 113 | public_partition=$DRIVE$p2 114 | 115 | encrypted_mount="/tmp/encrypted" 116 | public_mount="/tmp/public" 117 | ''; 118 | 119 | gpg-scripts = rec { 120 | gpg-write-keyid = writeShellScriptBin "gpg-write-keyid" '' 121 | set -e 122 | 123 | ${opts} 124 | 125 | keys=`${gpg} --list-secret-keys | ${grep} "^sec" | ${grep} -o "0x[[:xdigit:]]*"` 126 | if [ "$keys" ] ; then 127 | key=`echo "$keys" | tail -1` 128 | echo "$key" > "$GNUPGHOME"/keyid 129 | echo "Wrote $key to $GNUPGHOME/keyid" 130 | else 131 | echo "Couldn't guess GPG key ID, sorry." 132 | exit 1 133 | fi 134 | ''; 135 | 136 | gpg-generate-master-key = writeShellScriptBin "gpg-generate-master-key" '' 137 | set -e 138 | 139 | ${opts} 140 | 141 | ${gpg} --expert --full-generate-key 142 | ''; 143 | 144 | gpg-add-subkeys = writeShellScriptBin "gpg-add-subkeys" '' 145 | set -e 146 | 147 | ${opts} 148 | 149 | ${get_keyid} 150 | ${gpg} --expert --edit-key $KEYID 151 | ''; 152 | 153 | gpg-renew-subkeys = writeShellScriptBin "gpg-renew-subkeys" '' 154 | set -e 155 | 156 | ${opts} 157 | 158 | ${get_keyid} 159 | ${gpg} --expert --edit-key $KEYID 160 | ''; 161 | 162 | gpg-add-emails = writeShellScriptBin "gpg-add-emails" '' 163 | set -e 164 | 165 | ${opts} 166 | 167 | ${get_keyid} 168 | ${gpg} --expert --edit-key $KEYID 169 | ''; 170 | 171 | gpg-verify-keys = writeShellScriptBin "gpg-verify-keys" '' 172 | set -e 173 | 174 | ${opts} 175 | 176 | ${get_keyid} 177 | ${gpg} --export $KEYID | ${hokey} lint 178 | ''; 179 | 180 | gpg-export-keys = writeShellScriptBin "gpg-export-keys" '' 181 | set -e 182 | 183 | ${opts} 184 | 185 | ${get_keyid} 186 | ${gpg} --armor --export-secret-keys $KEYID > $GNUPGHOME/mastersub.key 187 | ${gpg} --armor --export-secret-subkeys $KEYID > $GNUPGHOME/sub.key 188 | ''; 189 | 190 | gpg-git-init = writeShellScriptBin "gpg-git-init" '' 191 | set -e 192 | 193 | basename=`basename "$0"` 194 | 195 | show_help() { 196 | echo "Usage: $basename [--help] \"NAME\" EMAIL" 197 | echo 198 | echo "--help Show usage." 199 | echo "\"NAME\" Your full name (in quotes)." 200 | echo "EMAIL Your Hackworth Ltd email address." 201 | } 202 | 203 | while :; do 204 | case $1 in 205 | -h|-\?|--help) 206 | show_help # Display a usage synopsis. 207 | exit 208 | ;; 209 | -?*) 210 | printf 'Unknown option: %s\n' "$1" >&2 211 | exit 99 212 | ;; 213 | *) # Default case: No more options, so break out of the loop. 214 | break 215 | esac 216 | shift 217 | done 218 | 219 | if [[ $# -ne 2 ]]; then 220 | echo "Please provide your full name (in quotes) and your Hackworth Ltd email address." 221 | echo 222 | show_help 223 | exit 2 224 | else 225 | NAME="$1" 226 | EMAIL="$2" 227 | fi 228 | 229 | ${get_keyid} 230 | cd $GNUPGHOME && ${gitcmd} init 231 | cd $GNUPGHOME && ${gitcmd} config user.name "$NAME" 232 | cd $GNUPGHOME && ${gitcmd} config user.email "$EMAIL" 233 | cd $GNUPGHOME && ${gitcmd} add . 234 | cd $GNUPGHOME && ${gitcmd} commit -m "Initial commit of key ID $KEYID." 235 | ''; 236 | 237 | gpg-backup-wipe-drive = writeShellScriptBin "gpg-backup-wipe-drive" '' 238 | set -e 239 | 240 | ${drive-opts} 241 | 242 | if (${whiptail} --title "Wipe $DRIVE?" --defaultno --yesno "Do you really want to securely wipe the USB drive $DRIVE? All data on the drive will be lost." 8 78); then 243 | echo "Proceeding with secure wipe." 244 | else 245 | echo "Aborting secure wipe of $DRIVE." 246 | exit 1 247 | fi 248 | 249 | sudo dd if=/dev/urandom of=$DRIVE bs=4M status=progress 250 | ''; 251 | 252 | gpg-backup-partition-drive = writeShellScriptBin "gpg-backup-partition-drive" '' 253 | set -e 254 | 255 | ${drive-opts} 256 | 257 | if (${whiptail} --title "Partition $DRIVE?" --defaultno --yesno "Do you really want to partition the USB drive $DRIVE? All data on the drive will be lost." 8 78); then 258 | echo "Proceeding with partitioning." 259 | else 260 | echo "Aborting partitioning of $DRIVE." 261 | exit 1 262 | fi 263 | 264 | sudo ${partedcmd} --align=opt --script -- $DRIVE \ 265 | mklabel gpt \ 266 | mkpart primary ext2 1MiB 2GiB \ 267 | mkpart primary fat32 2GiB -0 \ 268 | name 1 encrypted \ 269 | name 2 public 270 | ''; 271 | 272 | 273 | # Note that we can't use bash's nice ${foo} string substition here 274 | # because it conflicts with Nix's string interpolation. 275 | 276 | gpg-backup-format-drive = writeShellScriptBin "gpg-backup-format-drive" '' 277 | set -e 278 | 279 | ${drive-opts} 280 | 281 | if (${whiptail} --title "Format $DRIVE?" --defaultno --yesno "Do you really want to format the USB drive $DRIVE? All data on the drive will be lost." 8 78); then 282 | echo "Proceeding with formatting." 283 | else 284 | echo "Aborting formatting of $DRIVE." 285 | exit 1 286 | fi 287 | 288 | sudo ${cryptsetupcmd} luksFormat $encrypted_partition 289 | sudo ${cryptsetupcmd} open $encrypted_partition encrypted 290 | sudo mkfs.ext4 /dev/mapper/encrypted -L encrypted 291 | sudo ${cryptsetupcmd} close /dev/mapper/encrypted 292 | sudo mkfs.vfat -n public $public_partition 293 | ''; 294 | 295 | gpg-backup-sync-keys = writeShellScriptBin "gpg-backup-sync-keys" '' 296 | set -e 297 | ${drive-opts} 298 | 299 | echo "Checking whether $GNUPGHOME is clean... " 300 | pushd $GNUPGHOME 2>&1 1>/dev/null 301 | if output=$(${gitcmd} status --porcelain) && [ -z "$output" ]; then 302 | # Clean. 303 | echo "done." 304 | else 305 | echo "Your $GNUPGHOME GPG directory has uncommited changes." 306 | echo "Please resolve them and then run this command again." 307 | exit 2 308 | fi 309 | popd 2>&1 1>/dev/null 310 | 311 | ${get_keyid} 312 | 313 | gpg-backup-mount $DRIVE 314 | 315 | echo "Checking for an existing backup..." 316 | if [ -d $encrypted_mount/gnupg ] ; then 317 | 318 | # Existing backup. 319 | echo "There appears to be one already, we'll use that." 320 | pushd $encrypted_mount/gnupg 2>&1 1>/dev/null 321 | 322 | echo "Checking whether the existing backup git repo is clean..." 323 | if output=$(${gitcmd} status --porcelain) && [ -z "$output" ]; then 324 | 325 | # Clean. 326 | echo "done." 327 | 328 | echo "Merging changes in $GNUPGHOME to the backup git repo..." 329 | if (${gitcmd} pull $GNUPGHOME); then 330 | echo "done." 331 | else 332 | echo "The backup merge failed!" 333 | echo 334 | echo "Please resolve the merge issue. For your convenience, the backup" 335 | echo "is still mounted at $encrypted_mount/gnupg. If you get stuck, you" 336 | echo "can run 'git merge --abort' from the $encrypted_mount/gnupg" 337 | echo "backup directory and start over." 338 | echo 339 | echo "When you've resolved the issue and are ready to try a new backup," 340 | echo "run 'gpg-backup-unmount $DRIVE' and run this backup script again." 341 | exit 8 342 | fi 343 | 344 | else 345 | 346 | # Dirty. 347 | echo "Your backup git repo has uncommited changes. Please resolve them" 348 | echo "and then run this command again." 349 | echo 350 | echo "For your convenience, the offline backup directory is still mounted" 351 | echo "at $encrypted_mount/gnupg. When you're finished cleaning it up," 352 | echo "run 'gpg-backup-unmount $DRIVE' and then run this script again." 353 | exit 9 354 | fi 355 | popd 2>&1 1>/dev/null 356 | 357 | else 358 | 359 | # First backup to this drive. 360 | echo "There doesn't appear to be one; creating the initial backup in $encrypted_mount/gnupg..." 361 | sudo chmod 0777 $encrypted_mount 362 | ${gitcmd} clone $GNUPGHOME $encrypted_mount/gnupg 363 | echo "done." 364 | 365 | echo "Fixing permissions on backup directory..." 366 | chmod 0700 $encrypted_mount/gnupg 367 | find $encrypted_mount/gnupg -type d -exec chmod 0700 {} \; 368 | find $encrypted_mount/gnupg -type f -exec chmod 0600 {} \; 369 | echo "done." 370 | fi 371 | 372 | echo "Exporting your public key to the public filesystem..." 373 | ${gpg} --armor --export $KEYID > $public_mount/$KEYID-$(date +%F).txt 374 | echo "done." 375 | 376 | gpg-backup-unmount $DRIVE 377 | ''; 378 | 379 | gpg-backup-restore-keys = writeShellScriptBin "gpg-backup-restore-keys" '' 380 | set -e 381 | ${drive-opts} 382 | 383 | gpg-backup-mount $DRIVE 384 | 385 | echo "Checking for an existing backup..." 386 | if [ -d $encrypted_mount/gnupg ] ; then 387 | 388 | # Existing backup. 389 | echo "There appears to be a backup, we'll use that." 390 | 391 | if (${whiptail} --title "Restore GnuPG keys from backup?" --defaultno --yesno "This operation will remove the contents of $GNUPGHOME and restore it from your USB backup drive. Do you want to proceed?" 8 78); then 392 | echo "Proceeding with restore." 393 | pkill gpg-agent || true 394 | rm -rf $GNUPGHOME 395 | 396 | echo "Cloning the backup to $GNUPGHOME..." 397 | ${gitcmd} clone $encrypted_mount/gnupg $GNUPGHOME 398 | echo "done." 399 | 400 | echo "Fixing permissions on restored directory..." 401 | chmod 0700 $GNUPGHOME 402 | find $GNUPGHOME -type d -exec chmod 0700 {} \; 403 | find $GNUPGHOME -type f -exec chmod 0600 {} \; 404 | echo "done." 405 | 406 | else 407 | echo "Aborting the restore." 408 | fi 409 | else 410 | echo "There doesn't appear to be a gpg backup on $DRIVE. Aborting the restore." 411 | fi 412 | 413 | gpg-backup-unmount $DRIVE 414 | ''; 415 | 416 | gpg-backup-mount = writeShellScriptBin "gpg-backup-mount" '' 417 | set -e 418 | 419 | ${drive-opts} 420 | 421 | echo "Opening encrypted filesystem..." 422 | sudo ${cryptsetupcmd} open $encrypted_partition encrypted 423 | echo "done." 424 | 425 | echo "Mounting encrypted filesystem on $encrypted_mount..." 426 | sudo mkdir $encrypted_mount 427 | sudo mount /dev/mapper/encrypted $encrypted_mount 428 | echo "done." 429 | 430 | echo "Mounting public filesystem on $public_mount..." 431 | sudo mkdir $public_mount 432 | sudo mount -o rw,umask=0000 $public_partition $public_mount 433 | echo "done." 434 | ''; 435 | 436 | gpg-backup-unmount = writeShellScriptBin "gpg-backup-unmount" '' 437 | set -e 438 | 439 | unmount() { 440 | echo "Unmounting $1..." 441 | sudo sync 442 | sudo sync 443 | sudo umount $1 444 | echo "done." 445 | 446 | echo "Removing mount point $1..." 447 | sudo rmdir $1 448 | echo "done." 449 | } 450 | 451 | ${drive-opts} 452 | 453 | if [ -d $encrypted_mount ] ; then 454 | unmount $encrypted_mount 455 | echo "Closing encrypted filesystem..." 456 | sudo ${cryptsetupcmd} close /dev/mapper/encrypted 457 | echo "done." 458 | else 459 | echo "The encrypted backup filesystem doesn't appear to be mounted, skipping." 460 | fi 461 | 462 | if [ -d $public_mount ] ; then 463 | unmount $public_mount 464 | else 465 | echo "The public backup filesystem doesn't appear to be mounted, skipping." 466 | fi 467 | ''; 468 | }; 469 | 470 | in 471 | symlinkJoin { 472 | name = "gpg-scripts"; 473 | paths = lib.attrValues gpg-scripts; 474 | } 475 | -------------------------------------------------------------------------------- /pkgs/yk-scripts/default.nix: -------------------------------------------------------------------------------- 1 | # A set of scripts to perform various common YubiKey configuration 2 | # functions. In a few cases, these are just one-liners, but I think 3 | # those are still useful because they're easier to remember, once you 4 | # get in the habit of looking for `yk-*` scripts. 5 | 6 | { gnupg 7 | , newt 8 | , yubikey-manager 9 | , symlinkJoin 10 | , writeShellScriptBin 11 | , lib 12 | }: 13 | 14 | let 15 | 16 | gpg = "${gnupg}/bin/gpg"; 17 | whiptail = "${newt}/bin/whiptail"; 18 | ykman = "${yubikey-manager}/bin/ykman"; 19 | 20 | opts = '' 21 | basename=`basename "$0"` 22 | 23 | show_help() { 24 | echo "Usage: $basename [--help]" 25 | echo 26 | echo "--help Show usage." 27 | } 28 | 29 | while :; do 30 | case $1 in 31 | -h|-\?|--help) 32 | show_help # Display a usage synopsis. 33 | exit 34 | ;; 35 | -?*) 36 | printf 'Unknown option: %s\n' "$1" >&2 37 | exit 99 38 | ;; 39 | *) # Default case: No more options, so break out of the loop. 40 | break 41 | esac 42 | shift 43 | done 44 | ''; 45 | 46 | nfc-opts = '' 47 | basename=`basename "$0"` 48 | 49 | show_help() { 50 | echo "Usage: $basename [--nfc]" 51 | echo 52 | echo "-n, --nfc YubiKey has NFC capabilities." 53 | } 54 | 55 | nfc="" 56 | 57 | while :; do 58 | case $1 in 59 | -h|-\?|--help) 60 | show_help # Display a usage synopsis. 61 | exit 62 | ;; 63 | -n|--nfc) 64 | nfc="--nfc" 65 | ;; 66 | -?*) 67 | printf 'Unknown option: %s\n' "$1" >&2 68 | exit 99 69 | ;; 70 | *) # Default case: No more options, so break out of the loop. 71 | break 72 | esac 73 | shift 74 | done 75 | ''; 76 | 77 | yk-scripts = rec { 78 | # Run all these script in one go. Useful for YubiKey initialization 79 | # out of the box. 80 | # 81 | # This script uses the `yk-gpg-set-default-touch` version of the 82 | # OpenPGP touch settings; i.e., it requires touch for encryption and 83 | # signing, but not authentication. 84 | # 85 | # We sleep for 2s between operations to let everything settle. 86 | 87 | yk-init = writeShellScriptBin "yk-init" '' 88 | set -e 89 | 90 | pause() { 91 | sleep 2 92 | } 93 | 94 | ${nfc-opts} 95 | 96 | ${yk-reset}/bin/yk-reset 97 | pause 98 | ${yk-gpg-change-pin}/bin/yk-gpg-change-pin 99 | pause 100 | ${yk-gpg-set-default-touch}/bin/yk-gpg-set-default-touch 101 | pause 102 | ${yk-piv-change-puk}/bin/yk-piv-change-puk 103 | pause 104 | ${yk-otp-disable}/bin/yk-otp-disable $nfc 105 | pause 106 | ${yk-oath-disable}/bin/yk-oath-disable $nfc 107 | pause 108 | echo 109 | ${ykman} openpgp info 110 | echo 111 | ${ykman} info 112 | echo 113 | echo "Your YubiKey is now ready to be used." 114 | ''; 115 | 116 | 117 | # Reset a YubiKey to its factory configuration, except for any 118 | # disabled modes, which will remain disabled. 119 | # 120 | # Note that OATH reset will fail if OATH has been disabled, so we 121 | # ignore errors from that step. 122 | 123 | yk-reset = writeShellScriptBin "yk-reset" '' 124 | set -e 125 | 126 | ${opts} 127 | 128 | if (${whiptail} --title "Reset YubiKey?" --defaultno --yesno "Do you really want to reset this YubiKey? All keys will be erased and all PINs will be reset to their default values." 8 78); then 129 | echo "Proceeding with YubiKey reset." 130 | else 131 | echo "Aborting YubiKey reset." 132 | exit 1 133 | fi 134 | 135 | echo "Resetting FIDO2 configuration (note: deletes U2F and FIDO2 keys)..." 136 | echo "(Note: you may need to touch the YubiKey to proceed with the FIDO2 reset.)" 137 | ${ykman} fido reset --force 138 | echo "done." 139 | 140 | echo "Resetting OATH configuration..." 141 | ${ykman} oath reset --force 142 | echo "done." 143 | 144 | echo "Resetting OpenPGP configuration (note: wipes GPG keys)..." 145 | ${ykman} openpgp reset --force 146 | echo "done." 147 | 148 | echo "Resetting PIV configuration (note: deletes PIV certificates)..." 149 | ${ykman} piv reset --force 150 | echo "done." 151 | ''; 152 | 153 | 154 | # Disable OTP, and disable the OTP USB mode, for good measure. 155 | 156 | yk-otp-disable = writeShellScriptBin "yk-otp-disable" '' 157 | set -e 158 | 159 | ${nfc-opts} 160 | 161 | echo "Disabling OTP functions... " 162 | 163 | if [ "$nfc" == "--nfc" ] ; then 164 | ${ykman} config nfc --force --disable OTP 165 | fi 166 | ${ykman} mode --force FIDO+CCID 167 | 168 | # This will sometimes fail if we first set the mode as above. 169 | # In any case, it's not needed once we disable USB OTP mode. 170 | #${ykman} config usb --force --disable OTP 171 | ''; 172 | 173 | 174 | # Disable OATH. 175 | # 176 | # Not all keys support NFC, so we ignore errors from the NFC bit. 177 | 178 | yk-oath-disable = writeShellScriptBin "yk-oath-disable" '' 179 | set -e 180 | 181 | ${nfc-opts} 182 | 183 | if [ "$nfc" == "--nfc" ] ; then 184 | ${ykman} config nfc --force --disable OATH 185 | fi 186 | ${ykman} config usb --force --disable OATH 187 | ''; 188 | 189 | 190 | # Change the YubiKey OpenPGP Card PIN. 191 | # 192 | # This script can be used to change either the user PIN (answer "1" 193 | # at the prompt), or the Admin PIN (answer "3" at the prompt). 194 | 195 | yk-gpg-change-pin = writeShellScriptBin "yk-gpg-change-pin" '' 196 | set -e 197 | 198 | ${opts} 199 | 200 | echo "Changing the YubiKey OpenPGP (admin|user) PIN... " 201 | ${gpg} --change-pin 202 | echo "done." 203 | ''; 204 | 205 | 206 | # Enforce touch on OpenPGP encryption and signing. 207 | # 208 | # We don't enforce touch on OpenPGP authentication, because if 209 | # you're using your OpenPGP key for SSH authentication, it's often 210 | # not apparent that your SSH client is waiting for you to touch the 211 | # YubiKey. (If you have a YubiKey with a blinking LED, you may want 212 | # to use the `yk-gpg-set-all-touch` script, instead.) 213 | # 214 | # Note that changing these touch settings requires knowledge of the 215 | # YubiKey's OpenPGP Admin PIN. 216 | 217 | yk-gpg-set-default-touch = writeShellScriptBin "yk-gpg-set-default-touch" '' 218 | set -e 219 | 220 | ${opts} 221 | 222 | echo "Configuring OpenPGP to require touch for signing and encryption." 223 | echo 224 | echo "You'll be prompted for the YubiKey's OpenPGP admin PIN once" 225 | echo "for each operation." 226 | echo 227 | 228 | echo "Disabling touch for OpenPGP authentication... " 229 | ${ykman} openpgp set-touch --force aut off 230 | echo "done." 231 | 232 | echo "Requiring touch for OpenPGP encryption... " 233 | ${ykman} openpgp set-touch --force enc on 234 | echo "done." 235 | 236 | echo "Requiring touch for OpenPGP signing... " 237 | ${ykman} openpgp set-touch --force sig on 238 | echo "done." 239 | ''; 240 | 241 | 242 | # Enforce touch on all OpenPGP operations. 243 | # 244 | # Note that changing these touch settings requires knowledge of the 245 | # YubiKey's OpenPGP Admin PIN. 246 | 247 | yk-gpg-set-all-touch = writeShellScriptBin "yk-gpg-set-all-touch" '' 248 | set -e 249 | 250 | ${opts} 251 | 252 | echo "Configuring OpenPGP to require touch for all operations." 253 | echo 254 | echo "You'll be prompted for the YubiKey's OpenPGP admin PIN once" 255 | echo "for each operation." 256 | echo 257 | 258 | echo "Requiring touch for OpenPGP authentication... " 259 | ${ykman} openpgp set-touch --force aut on 260 | echo "done." 261 | 262 | echo "Requiring touch for OpenPGP encryption... " 263 | ${ykman} openpgp set-touch --force enc on 264 | echo "done." 265 | 266 | echo "Requiring touch for OpenPGP signing... " 267 | ${ykman} openpgp set-touch --force sig on 268 | echo "done." 269 | ''; 270 | 271 | 272 | # Change the PIV PUK. 273 | 274 | yk-piv-change-puk = writeShellScriptBin "yk-piv-change-puk" '' 275 | set -e 276 | 277 | ${opts} 278 | 279 | echo "Changing the PIV PUK... " 280 | ${ykman} piv change-puk 281 | echo "done." 282 | ''; 283 | }; 284 | 285 | in 286 | symlinkJoin { 287 | name = "yk-scripts"; 288 | paths = lib.attrValues yk-scripts; 289 | } 290 | --------------------------------------------------------------------------------