nix: fix PASSWORD_STORE_DIR
[sourcephile-nix.git] / shell / modules / tools / security / gnupg.nix
index c5821bed2f0a3b16030a6b5b05d21c4ae80fb59a..7f8816728e5377f7ece38f1183b0cddd3bd299db 100644 (file)
@@ -14,13 +14,14 @@ let
    , expire ? "-"
    , passPath
    , subKeys ? {}
+   , postRun ? ""
    , ...
    }@primary:
     ''
     info "generateKey uid=\"${uid}\""
     if ! ${gpg-with-home}/bin/gpg-with-home --list-secret-keys -- "=${uid}" >/dev/null 2>/dev/null
      then
-      ${pkgs.pass}/bin/pass "${passPath}" |
+      ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
       ${gpg-with-home}/bin/gpg-with-home \
         --batch --pinentry-mode loopback --passphrase-fd 0 \
         --quick-generate-key "${uid}" "${algo}" "${unwords usage}" "${expire}"
@@ -28,13 +29,14 @@ let
     ${head1}
     fpr=$(${gpg-fingerprint}/bin/gpg-fingerprint -- "=${uid}" | head1)
     caps=$(${gpg-with-home}/bin/gpg-with-home \
-            --with-colons --fixed-list-mode --with-fingerprint \
+            --with-colons --with-fingerprint \
             --list-secret-keys -- "=${uid}" |
            ${pkgs.gnugrep}/bin/grep '^ssb:' |
            ${pkgs.coreutils}/bin/cut -d : -f 12 || true)
     ''
     + unlines (map (generateSubKey primary) subKeys)
     + generateBackupKey "$fpr" primary
+    + postRun
     ;
   generateSubKey =
    primary:
@@ -47,7 +49,7 @@ let
     info "  generateSubKey usage=[${unwords usage}]"
     if ! printf '%s\n' "$caps" | ${pkgs.gnugrep}/bin/grep -Fqx "${lettersKeyUsage usage}"
      then
-      ${pkgs.pass}/bin/pass "${primary.passPath}" |
+      ${if primary.passPath != "" then "${pkgs.pass}/bin/pass '${primary.passPath}'" else "cat /dev/null"} |
       ${gpg-with-home}/bin/gpg-with-home \
         --batch --pinentry-mode loopback --passphrase-fd 0 \
         --quick-add-key "$fpr" "${algo}" "${unwords usage}" "${expire}"
@@ -74,9 +76,10 @@ let
      fi
     '' + (if backupRecipients == [""] then
     ''
-    if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.revoke.asc"
+    if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.revoke.asc" &&
+     ${gpg-with-home}/bin/gpg-with-home --list-secret-keys "${fpr}" | grep -q "sec "
      then
-      ${pkgs.pass}/bin/pass "${passPath}" |
+      ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
       ${gpg-with-home}/bin/gpg-with-home \
         --pinentry-mode loopback --passphrase-fd 0 \
         --armor --yes --output "${gnupg.gnupgHome}/backup/${uid}/${fpr}.revoke.asc" \
@@ -84,7 +87,7 @@ let
      fi
     if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.privkey.sec"
      then
-      ${pkgs.pass}/bin/pass "${passPath}" |
+      ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
       ${gpg-with-home}/bin/gpg-with-home \
         --batch --pinentry-mode loopback --passphrase-fd 0 \
         --armor --yes --output "${gnupg.gnupgHome}/backup/${uid}/${fpr}.privkey.sec" \
@@ -93,7 +96,7 @@ let
      fi
     if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.subkeys.sec"
      then
-      ${pkgs.pass}/bin/pass "${passPath}" |
+      ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
       ${gpg-with-home}/bin/gpg-with-home \
         --batch --pinentry-mode loopback --passphrase-fd 0 \
         --armor --yes --output "${gnupg.gnupgHome}/backup/${uid}/${fpr}.subkeys.sec" \
@@ -103,7 +106,7 @@ let
     '' else ''
     if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.revoke.asc.gpg"
      then
-      ${pkgs.pass}/bin/pass "${passPath}" |
+      ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
       ${gpg-with-home}/bin/gpg-with-home \
         --pinentry-mode loopback --passphrase-fd 0 \
         --armor --gen-revoke "${fpr}" |
@@ -112,7 +115,7 @@ let
      fi
     if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.privkey.sec.gpg"
      then
-      ${pkgs.pass}/bin/pass "${passPath}" |
+      ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
       ${gpg-with-home}/bin/gpg-with-home \
         --batch --pinentry-mode loopback --passphrase-fd 0 \
         --armor --export-options export-backup \
@@ -122,7 +125,7 @@ let
      fi
     if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.subkeys.sec.gpg"
      then
-      ${pkgs.pass}/bin/pass "${passPath}" |
+      ${if passPath != "" then "${pkgs.pass}/bin/pass '${passPath}'" else "cat /dev/null"} |
       ${gpg-with-home}/bin/gpg-with-home \
         --batch --pinentry-mode loopback --passphrase-fd 0 \
         --armor --export-options export-backup \
@@ -216,7 +219,7 @@ let
   gpg-fingerprint = pkgs.writeScriptBin "gpg-fingerprint" ''
     set -eu
     ${gpg-with-home}/bin/gpg-with-home \
-     --with-colons --fixed-list-mode --with-fingerprint --with-subkey-fingerprint \
+     --with-colons --with-fingerprint --with-subkey-fingerprint \
      --list-public-keys "$@" |
     while IFS=: read -r t x x x key x x x x uid x
      do case $t in
@@ -232,23 +235,16 @@ let
   gpg-keygrip = pkgs.writeScriptBin "gpg-keygrip" ''
     set -eu
     ${gpg-with-home}/bin/gpg-with-home \
-     --with-colons --fixed-list-mode --with-keygrip \
+     --with-colons --with-keygrip \
      --list-public-keys "$@" |
-    while IFS=: read -r t x x x key x x x x uid x
-     do case $t in
-       (pub|sub|sec|ssb)
-        while IFS=: read -r t x x x x x x x x grp x
-         do case $t in (grp) printf '%s\n' "$grp"; break;;
-         esac done
-        ;;
-     esac done
+    while IFS=: read -r t x x x key x x x x uid x do case $t in (pub|sub|sec|ssb) while IFS=: read -r t x x x x x x x x grp x do case $t in (grp) printf '%s\n' "$grp"; break;; esac done ;; esac done
     '';
 
   # A wrapper around gpg to get uids.
   gpg-uid = pkgs.writeScriptBin "gpg-uid" ''
     set -eu
     ${gpg-with-home}/bin/gpg-with-home \
-     --with-colons --fixed-list-mode \
+     --with-colons \
      --list-public-keys "$@" |
     while IFS=: read -r t st x x x x x id x uid x
      do case $t in
@@ -277,7 +273,7 @@ in
 options.gnupg = {
   enable = lib.mkEnableOption "GnuPG shell utilities";
   gnupgHome = lib.mkOption {
-    type = types.path;
+    type = types.str;
     default = "sec/gnupg";
     description = ''
     '';
@@ -298,13 +294,12 @@ options.gnupg = {
           backupRecipients = ["@john@doe.pro"];
         };
       };
-    type = types.attrsOf (types.submodule ({uid, ...}: {
-      #config.uid = lib.mkDefault uid;
+    type = types.attrsOf (types.submodule ({name, ...}: {
       options = {
         uid = lib.mkOption {
           type        = types.str;
           example     = "John Doe <john.doe@example.coop>";
-          default     = uid;
+          default     = name;
           description = ''
             User ID.
           '';
@@ -319,7 +314,7 @@ options.gnupg = {
         };
         expire = lib.mkOption {
           type        = types.str;
-          default     = "1y";
+          default     = "0";
           example     = "1y";
           description = ''
             Expiration timeout.
@@ -353,7 +348,7 @@ options.gnupg = {
               };
               expire = lib.mkOption {
                 type        = types.str;
-                default     = "1y";
+                default     = "0";
                 example     = "1y";
                 description = ''
                   Expiration timeout.
@@ -378,6 +373,13 @@ options.gnupg = {
             Backup keys used to encrypt the a backup copy of the secret keys.
           '';
         };
+        postRun = lib.mkOption {
+          type = types.lines;
+          default = "";
+          description = ''
+            Shell code to run after the key has been generated or tested to exist.
+          '';
+        };
       };
     }));
   };
@@ -388,7 +390,7 @@ options.gnupg = {
       allow-ocsp
       hkp-cacert ${gnupg.keyserverPEM}
       keyserver hkps://keys.mayfirst.org
-      use-tor
+      #use-tor
       #log-file ${gnupg.gnupgHome}/dirmngr.log
       #standard-resolver
     '';
@@ -406,15 +408,33 @@ options.gnupg = {
   };
   gpgAgentConf = lib.mkOption {
     type = types.lines;
-    apply = s: pkgs.writeText "gpg-agent.conf" s;
-    default = ''
+    apply = s: pkgs.writeText "gpg-agent.conf" (s+"\n"+gnupg.gpgAgentExtraConf);
+    default =
+      let pinentry = pkgs.writeShellScript "pinentry" ''
+        #!${pkgs.runtimeShell}
+        # choose pinentry depending on PINENTRY_USER_DATA
+        # this *only works* with gpg2
+        # see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=802020
+        case "''${PINENTRY_USER_DATA:-curses}" in
+        curses) exec ${pkgs.pinentry.curses}/bin/pinentry-curses "$@";;
+        #emacs)  exec ''${pkgs.pinentry.emacs}/bin/pinentry-emacs "$@";;
+        #gnome3) exec ''${pkgs.pinentry.gnome3}/bin/pinentry-gnome3 "$@";;
+        gtk-2)  exec ${pkgs.pinentry.gtk2}/bin/pinentry-gtk-2 "$@";;
+        none)   exit 1;; # do not ask for passphrase
+        #qt)     exec ''${pkgs.pinentry.qt}/bin/pinentry-qt "$@";;
+        tty)    exec ${pkgs.pinentry.tty}/bin/pinentry-tty "$@";;
+        esac
+      '';
+    in ''
+      allow-loopback-pinentry
       allow-preset-passphrase
       default-cache-ttl 17200
       default-cache-ttl-ssh 17200
       enable-ssh-support
       max-cache-ttl 17200
       max-cache-ttl-ssh 17200
-      pinentry-program ${pkgs.pinentry}/bin/pinentry
+      no-allow-external-cache
+      pinentry-program ${pinentry}
     '';
     description = ''
       GnuPG's gpg-agent.conf content.
@@ -428,7 +448,6 @@ options.gnupg = {
       cert-digest-algo SHA512
       charset utf-8
       default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 TWOFISH BZIP2 ZLIB ZIP Uncompressed
-      fixed-list-mode
       keyid-format 0xlong
       keyserver-options no-honor-keyserver-url
       no-auto-key-locate
@@ -457,6 +476,13 @@ options.gnupg = {
       GnuPG's gpg.conf extra content.
     '';
   };
+  gpgAgentExtraConf = lib.mkOption {
+    type = types.lines;
+    default = "";
+    description = ''
+      GnuPG's gpg-agent.conf extra content.
+    '';
+  };
 };
 config = lib.mkIf gnupg.enable {
   nix-shell.buildInputs = [