{ pkgs, lib, config, ... }:
-let cfg = config.gnupg;
- inherit (lib) types;
- unlines = builtins.concatStringsSep "\n";
- unwords = builtins.concatStringsSep " ";
+let
+ inherit (lib) types;
+ inherit (config) gnupg;
+ unlines = builtins.concatStringsSep "\n";
+ unwords = builtins.concatStringsSep " ";
- generateKeys = keys: unlines (lib.mapAttrsToList generateKey keys);
- generateKey =
- uid:
- { uid ? uid
- , algo ? "future-default"
- , usage ? ["default"]
- , expire ? "-"
- , passPath
- , subKeys ? {}
- , ...
- }@primary:
- ''
- info " generateKey uid=\"${uid}\""
- if ! ${cfg.gpg-with-home}/bin/gpg-with-home --list-secret-keys -- "=${uid}" >/dev/null 2>/dev/null
+ generateKeys = keys: unlines (lib.mapAttrsToList generateKey keys);
+ generateKey =
+ _uid:
+ { uid ? uid
+ , algo ? "future-default"
+ , usage ? [ "default" ]
+ , 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}" |
- ${cfg.gpg-with-home}/bin/gpg-with-home \
+ ${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}"
fi
${head1}
- fpr=$(${cfg.gpg-fingerprint}/bin/gpg-fingerprint -- "=${uid}" | head1)
- caps=$(${cfg.gpg-with-home}/bin/gpg-with-home \
- --with-colons --fixed-list-mode --with-fingerprint \
+ fpr=$(${gpg-fingerprint}/bin/gpg-fingerprint -- "=${uid}" | head1)
+ caps=$(${gpg-with-home}/bin/gpg-with-home \
+ --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
- ;
- generateSubKey =
- primary:
- { expire ? primary.expire
- , algo ? primary.algo
- , usage
- , ...
- }:
- ''
- info " generateSubKey usage=[${unwords usage}]"
+ ''
+ + unlines (map (generateSubKey primary) subKeys)
+ + generateBackupKey "$fpr" primary
+ + postRun
+ ;
+ generateSubKey =
+ primary:
+ { expire ? primary.expire
+ , algo ? primary.algo
+ , usage
+ , ...
+ }:
+ ''
+ info " generateSubKey usage=[${unwords usage}]"
if ! printf '%s\n' "$caps" | ${pkgs.gnugrep}/bin/grep -Fqx "${lettersKeyUsage usage}"
then
- ${pkgs.pass}/bin/pass "${primary.passPath}" |
- ${cfg.gpg-with-home}/bin/gpg-with-home \
+ ${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}"
fi
- '';
- generateBackupKey =
- fpr:
- { passPath
- , backupRecipients ? []
- , uid
- , ...
- }:
- lib.optionalString (backupRecipients != [])
+ '';
+ generateBackupKey =
+ fpr:
+ { passPath
+ , backupRecipients ? [ ]
+ , uid
+ , ...
+ }:
+ lib.optionalString (backupRecipients != [ ])
''
- info " generateBackupKey backupRecipients=[${unwords (map (s: "\\\"${s}\\\"") backupRecipients)}]"
- mkdir -p "${cfg.dir.var}/backup/${uid}/"
- if ! test -s "${cfg.dir.var}/backup/${uid}/${fpr}.pubkey.asc"
- then
- ${cfg.gpg-with-home}/bin/gpg-with-home \
- --batch \
- --armor --yes --output "${cfg.dir.var}/backup/${uid}/${fpr}.pubkey.asc" \
- --export-options export-backup \
- --export "${fpr}"
- fi
- '' + (if backupRecipients == [""] then
+ info " generateBackupKey backupRecipients=[${unwords (map (s: "\\\"${s}\\\"") backupRecipients)}]"
+ mkdir -p "${gnupg.gnupgHome}/backup/${uid}/"
+ if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.pubkey.asc"
+ then
+ ${gpg-with-home}/bin/gpg-with-home \
+ --batch \
+ --armor --yes --output "${gnupg.gnupgHome}/backup/${uid}/${fpr}.pubkey.asc" \
+ --export-options export-backup \
+ --export "${fpr}"
+ fi
+ '' + (if backupRecipients == [ "" ] then
''
- if ! test -s "${cfg.dir.var}/backup/${uid}/${fpr}.revoke.asc"
- then
- ${pkgs.pass}/bin/pass "${passPath}" |
- ${cfg.gpg-with-home}/bin/gpg-with-home \
- --pinentry-mode loopback --passphrase-fd 0 \
- --armor --yes --output "${cfg.dir.var}/backup/${uid}/${fpr}.revoke.asc" \
- --gen-revoke "${fpr}"
- fi
- if ! test -s "${cfg.dir.var}/backup/${uid}/${fpr}.privkey.sec"
- then
- ${pkgs.pass}/bin/pass "${passPath}" |
- ${cfg.gpg-with-home}/bin/gpg-with-home \
- --batch --pinentry-mode loopback --passphrase-fd 0 \
- --armor --yes --output "${cfg.dir.var}/backup/${uid}/${fpr}.privkey.sec" \
- --export-options export-backup \
- --export-secret-key "${fpr}"
- fi
- if ! test -s "${cfg.dir.var}/backup/${uid}/${fpr}.subkeys.sec"
- then
- ${pkgs.pass}/bin/pass "${passPath}" |
- ${cfg.gpg-with-home}/bin/gpg-with-home \
- --batch --pinentry-mode loopback --passphrase-fd 0 \
- --armor --yes --output "${cfg.dir.var}/backup/${uid}/${fpr}.subkeys.sec" \
- --export-options export-backup \
- --export-secret-subkeys "${fpr}"
- fi
+ 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
+ ${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" \
+ --gen-revoke "${fpr}"
+ fi
+ if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.privkey.sec"
+ then
+ ${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" \
+ --export-options export-backup \
+ --export-secret-key "${fpr}"
+ fi
+ if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.subkeys.sec"
+ then
+ ${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" \
+ --export-options export-backup \
+ --export-secret-subkeys "${fpr}"
+ fi
'' else ''
- if ! test -s "${cfg.dir.var}/backup/${uid}/${fpr}.revoke.asc.gpg"
+ if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.revoke.asc.gpg"
then
- ${pkgs.pass}/bin/pass "${passPath}" |
- ${cfg.gpg-with-home}/bin/gpg-with-home \
+ ${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}" |
gpg --encrypt ${recipients backupRecipients} \
- --armor --yes --output "${cfg.dir.var}/backup/${uid}/${fpr}.revoke.asc.gpg"
+ --armor --yes --output "${gnupg.gnupgHome}/backup/${uid}/${fpr}.revoke.asc.gpg"
fi
- if ! test -s "${cfg.dir.var}/backup/${uid}/${fpr}.privkey.sec.gpg"
+ if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.privkey.sec.gpg"
then
- ${pkgs.pass}/bin/pass "${passPath}" |
- ${cfg.gpg-with-home}/bin/gpg-with-home \
+ ${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 \
--export-secret-key "${fpr}" |
gpg --encrypt ${recipients backupRecipients} \
- --armor --yes --output "${cfg.dir.var}/backup/${uid}/${fpr}.privkey.sec.gpg"
+ --armor --yes --output "${gnupg.gnupgHome}/backup/${uid}/${fpr}.privkey.sec.gpg"
fi
- if ! test -s "${cfg.dir.var}/backup/${uid}/${fpr}.subkeys.sec.gpg"
+ if ! test -s "${gnupg.gnupgHome}/backup/${uid}/${fpr}.subkeys.sec.gpg"
then
- ${pkgs.pass}/bin/pass "${passPath}" |
- ${cfg.gpg-with-home}/bin/gpg-with-home \
+ ${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 \
--export-secret-subkeys "${fpr}" |
gpg --encrypt ${recipients backupRecipients} \
- --armor --yes --output "${cfg.dir.var}/backup/${uid}/${fpr}.subkeys.sec.gpg"
+ --armor --yes --output "${gnupg.gnupgHome}/backup/${uid}/${fpr}.subkeys.sec.gpg"
fi
- '');
- recipients = rs: unwords (map (r: ''--recipient "${refKey r}"'') rs);
- refKey = key: if builtins.typeOf key == "string" then key else "=${key.uid}";
- signer = s: if s == null
- then ""
- else ''--sign --default-key "${refKey s}"'';
- lettersKeyUsage = usage:
- (if builtins.elem "encrypt" usage then "e" else "") +
- (if builtins.elem "sign" usage then "s" else "") +
- (if builtins.elem "cert" usage then "c" else "") +
- (if builtins.elem "auth" usage then "a" else "");
+ '');
+ recipients = rs: unwords (map (r: ''--recipient "${refKey r}"'') rs);
+ refKey = key: if builtins.typeOf key == "string" then key else "=${key.uid}";
+ lettersKeyUsage = usage:
+ (if builtins.elem "encrypt" usage then "e" else "") +
+ (if builtins.elem "sign" usage then "s" else "") +
+ (if builtins.elem "cert" usage then "c" else "") +
+ (if builtins.elem "auth" usage then "a" else "");
- passOfFingerprint = key:
- # Return shell code
- # which fills a map from the fingerprints of the given key
- # to its password file.
- ''
- # shell.gnupg.pass.passOfFingerprint
- for fpr in $(${cfg.gpg-fingerprint}/bin/gpg-fingerprint -- "=${key.uid}")
- do eval "pass_$fpr=\"${key.passPath}\""
- done
- '';
- forgetPass =
- # Return shell code
- # which installs an exit and keyboard interruption (^C) trap
- # removing any pass from gpg-agent
- # whose keygrip is registered in $keygrips.
- ''
- # forgetPass
- keygrips=
- forgetPass () {
- for keygrip in $keygrips
- do
- echo >&2 "gpg: forget: keygrip=$keygrip"
- GNUPGHOME=${cfg.dir.var} \
- ${pkgs.gnupg}/bin/gpg-connect-agent </dev/null >&2 "CLEAR_PASSPHRASE $keygrip" ||
- true
- done
- keygrips=
- }
- trap 'forgetPass' EXIT INT
- '';
- presetPass = keys: uid:
- # Return shell code
- # which preset the pass of given uid into gpg-agent,
- # using keys to find where the pass is stored.
- ''
- ${unlines (map passOfFingerprint keys)}
- # presetPass
- GNUPGHOME=${cfg.dir.var} \
- ${pkgs.gnupg}/bin/gpgconf --launch gpg-agent
- ${head1}
- fpr="$(${cfg.gpg-fingerprint}/bin/fingerprint -- "${uid}" | head1)"
- eval pass="\''${pass_$fpr}"
- if test -n "$pass"
- then
- for keygrip in $(${cfg.gpg-keygrip}/bin/gpg-keygrip -- "$fpr")
- do
- keygrips="$keygrips $keygrip"
- echo >&2 "gpg: preset: keygrip=$keygrip pass=$pass"
- ${pkgs.pass}/bin/pass "$pass" |
- GNUPGHOME=${cfg.dir.var} \
- ${pkgs.gnupg}/libexec/gpg-preset-passphrase --preset ''${XTRACE:+--verbose} $keygrip
- done
- fi
- '';
+ # Initialize the keyring according to gnupg.keys.
+ gpg-init = pkgs.writeShellScriptBin "gpg-init" (''
+ set -eu
+ set -o pipefail
+ ${info}
+ '' +
+ generateKeys gnupg.keys
+ );
- head1 = ''
- head1(){
- IFS= read -r line
- cat >/dev/null # NOTE: consuming all the input avoids useless triggering of pipefail
- printf %s "$line"
- }
- '';
- info = ''
- info(){
- echo >&2 "INFO: $*"
- }
- '';
+ # A wrapper around gpg to set GNUPGHOME.
+ gpg-with-home = pkgs.writeScriptBin "gpg-with-home" ''
+ GNUPGHOME=${gnupg.gnupgHome} \
+ exec ${pkgs.gnupg}/bin/gpg "$@"
+ '';
+
+ # A wrapper around gpg to get fingerprints.
+ gpg-fingerprint = pkgs.writeScriptBin "gpg-fingerprint" ''
+ set -eu
+ ${gpg-with-home}/bin/gpg-with-home \
+ --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
+ (pub|sub|sec|ssb)
+ while IFS=: read -r t x x x x x x x x fpr x
+ do case $t in (fpr) printf '%s\n' "$fpr"; break;;
+ esac done
+ ;;
+ esac done
+ '';
+
+ # A wrapper around gpg to get keygrips.
+ gpg-keygrip = pkgs.writeScriptBin "gpg-keygrip" ''
+ set -eu
+ ${gpg-with-home}/bin/gpg-with-home \
+ --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
+ '';
+
+ # A wrapper around gpg to get uids.
+ gpg-uid = pkgs.writeScriptBin "gpg-uid" ''
+ set -eu
+ ${gpg-with-home}/bin/gpg-with-home \
+ --with-colons \
+ --list-public-keys "$@" |
+ while IFS=: read -r t st x x x x x id x uid x
+ do case $t in
+ (uid)
+ case $st in
+ (u) printf '%s\n' "$uid";;
+ esac
+ ;;
+ esac done
+ '';
+
+ head1 = ''
+ head1(){
+ IFS= read -r line
+ cat >/dev/null # NOTE: consuming all the input avoids useless triggering of pipefail
+ printf %s "$line"
+ }
+ '';
+ info = ''
+ info(){
+ echo >&2 "gpg-init: $*"
+ }
+ '';
in
{
options.gnupg = {
- enable = lib.mkEnableOption "GnuPG admin utilities";
- dir.var = lib.mkOption {
- type = types.path;
- default = "sec/gnupg";
- description = ''
- '';
- };
- gpg-with-home = lib.mkOption {
+ enable = lib.mkEnableOption "GnuPG shell utilities";
+ gnupgHome = lib.mkOption {
type = types.str;
- apply = pkgs.writeScriptBin "gpg-with-home";
- default = ''
- GNUPGHOME=${cfg.dir.var} \
- exec ${pkgs.gnupg}/bin/gpg "$@"
- '';
- description = ''
- A wrapper around gpg to set GNUPGHOME.
- '';
- };
- gpg-fingerprint = lib.mkOption {
- type = types.str;
- apply = pkgs.writeScriptBin "gpg-fingerprint";
- default = ''
- set -eu
- ${cfg.gpg-with-home}/bin/gpg-with-home \
- --with-colons --fixed-list-mode --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
- (pub|sub|sec|ssb)
- while IFS=: read -r t x x x x x x x x fpr x
- do case $t in (fpr) printf '%s\n' "$fpr"; break;;
- esac done
- ;;
- esac done
- '';
- description = ''
- A wrapper around gpg to get fingerprints.
- '';
- };
- gpg-keygrip = lib.mkOption {
- type = types.str;
- apply = pkgs.writeScriptBin "gpg-keygrip";
- default = ''
- set -eu
- ${cfg.gpg-with-home}/bin/gpg-with-home \
- --with-colons --fixed-list-mode --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
- '';
- description = ''
- A wrapper around gpg to get keygrips.
- '';
- };
- gpg-uid = lib.mkOption {
- type = types.str;
- apply = pkgs.writeScriptBin "gpg-uid";
- default = ''
- set -eu
- ${cfg.gpg-with-home}/bin/gpg-with-home \
- --with-colons --fixed-list-mode \
- --list-public-keys "$@" |
- while IFS=: read -r t st x x x x x id x uid x
- do case $t in
- (uid)
- case $st in
- (u) printf '%s\n' "$uid";;
- esac
- ;;
- esac done
- '';
- description = ''
- A wrapper around gpg to get uids.
- '';
- };
- init = lib.mkOption {
- type = types.str;
- apply = pkgs.writeShellScriptBin "init-gpg";
- default = ''
- set -eu
- set -o pipefail
- ${info}
- info "Init GnuPG"
- ${pkgs.coreutils}/bin/install -dm0700 -D ${cfg.dir.var}
- ${pkgs.coreutils}/bin/ln -snf ${cfg.gpgConf} ${cfg.dir.var}/gpg.conf
- ${pkgs.coreutils}/bin/ln -snf ${cfg.gpgAgentConf} ${cfg.dir.var}/gpg-agent.conf
- ${pkgs.coreutils}/bin/ln -snf ${cfg.dirmngrConf} ${cfg.dir.var}/dirmngr.conf
- '' +
- generateKeys cfg.keys;
+ default = "sec/gnupg";
description = ''
- Setup gpg.
- '';
+ '';
};
keys = lib.mkOption {
- default = {};
+ default = { };
example =
- { "John Doe. <contact@example.coop>" = {
- algo = "rsa4096";
+ {
+ "John Doe. <contact@example.coop>" = {
+ algo = "rsa4096";
expire = "1y";
- usage = ["cert" "sign"];
+ usage = [ "cert" "sign" ];
passPath = "example.coop/gpg/contact";
subKeys = [
- { algo = "rsa4096"; expire = "1y"; usage = ["sign"];}
- { algo = "rsa4096"; expire = "1y"; usage = ["encrypt"];}
- { algo = "rsa4096"; expire = "1y"; usage = ["auth"];}
- ];
- backupRecipients = ["@john@doe.pro"];
+ { algo = "rsa4096"; expire = "1y"; usage = [ "sign" ]; }
+ { algo = "rsa4096"; expire = "1y"; usage = [ "encrypt" ]; }
+ { algo = "rsa4096"; expire = "1y"; usage = [ "auth" ]; }
+ ];
+ 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;
+ type = types.str;
+ example = "John Doe <john.doe@example.coop>";
+ default = name;
description = ''
User ID.
'';
};
algo = lib.mkOption {
- type = types.enum [ "rsa4096" ];
- default = "future-default";
- example = "rsa4096";
+ type = types.enum [ "rsa4096" ];
+ default = "future-default";
+ example = "rsa4096";
description = ''
Cryptographic algorithm.
'';
};
expire = lib.mkOption {
- type = types.str;
- default = "1y";
- example = "1y";
+ type = types.str;
+ default = "0";
+ example = "1y";
description = ''
Expiration timeout.
'';
};
usage = lib.mkOption {
- type = with types; listOf (enum [ "cert" "sign" "encrypt" "auth" "default" ]);
- default = ["default"];
- example = ["cert" "sign" "encrypt" "auth"];
+ type = with types; listOf (enum [ "cert" "sign" "encrypt" "auth" "default" ]);
+ default = [ "default" ];
+ example = [ "cert" "sign" "encrypt" "auth" ];
description = ''
Cryptographic usage.
'';
};
passPath = lib.mkOption {
- type = types.str;
- example = "gnupg/coop/example/contact@";
+ type = types.str;
+ example = "gnupg/coop/example/contact@";
description = ''
Password path.
'';
type = types.listOf (types.submodule {
options = {
algo = lib.mkOption {
- type = types.enum [ "rsa4096" ];
- default = "default";
- example = "rsa4096";
+ type = types.enum [ "rsa4096" ];
+ default = "default";
+ example = "rsa4096";
description = ''
Cryptographic algorithm.
'';
};
expire = lib.mkOption {
- type = types.str;
- default = "1y";
- example = "1y";
+ type = types.str;
+ default = "0";
+ example = "1y";
description = ''
Expiration timeout.
'';
};
usage = lib.mkOption {
- type = with types; listOf (enum [ "sign" "encrypt" "auth" "default" ]);
- default = ["default"];
- example = ["sign" "encrypt" "auth"];
+ type = with types; listOf (enum [ "sign" "encrypt" "auth" "default" ]);
+ default = [ "default" ];
+ example = [ "sign" "encrypt" "auth" ];
description = ''
Cryptographic usage.
'';
});
};
backupRecipients = lib.mkOption {
- type = with types; listOf str;
- default = [];
- example = ["@john@doe.pro"];
+ type = with types; listOf str;
+ default = [ ];
+ example = [ "@john@doe.pro" ];
description = ''
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.
+ '';
+ };
};
}));
};
dirmngrConf = lib.mkOption {
- type = types.str;
+ type = types.lines;
apply = s: pkgs.writeText "dirmngr.conf" s;
default = ''
allow-ocsp
- hkp-cacert ${cfg.keyserverPEM}
+ hkp-cacert ${gnupg.keyserverPEM}
keyserver hkps://keys.mayfirst.org
- use-tor
- #log-file ${cfg.dir.var}/dirmngr.log
+ #use-tor
+ #log-file ${gnupg.gnupgHome}/dirmngr.log
#standard-resolver
'';
description = ''
'';
};
keyserverPEM = lib.mkOption {
- type = types.str;
+ type = types.lines;
apply = s: pkgs.writeText "keyserver.pem" s;
default = builtins.readFile gnupg/keyserver.pem;
description = ''
'';
};
gpgAgentConf = lib.mkOption {
- type = types.str;
- apply = s: pkgs.writeText "gpg-agent.conf" s;
- default = ''
- allow-preset-passphrase
- default-cache-ttl 17200
- default-cache-ttl-ssh 17200
- enable-ssh-support
- max-cache-ttl 17200
- max-cache-ttl-ssh 17200
- '';
+ type = types.lines;
+ 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
+ no-allow-external-cache
+ pinentry-program ${pinentry}
+ '';
description = ''
GnuPG's gpg-agent.conf content.
'';
};
gpgConf = lib.mkOption {
- type = types.str;
- apply = s: pkgs.writeText "gpg.conf" s;
+ type = types.lines;
+ apply = s: pkgs.writeText "gpg.conf" (s + "\n" + gnupg.gpgExtraConf);
default = ''
auto-key-locate keyserver
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
GnuPG's gpg.conf content.
'';
};
+ gpgExtraConf = lib.mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ 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 = [
+ gpg-with-home
+ gpg-fingerprint
+ gpg-keygrip
+ gpg-uid
+ gpg-init
+ ];
+ nix-shell.shellHook = ''
+ # gnupg
+ ${pkgs.coreutils}/bin/install -dm0700 -D ${gnupg.gnupgHome}
+ ${pkgs.coreutils}/bin/ln -snf ${gnupg.gpgConf} ${gnupg.gnupgHome}/gpg.conf
+ ${pkgs.coreutils}/bin/ln -snf ${gnupg.gpgAgentConf} ${gnupg.gnupgHome}/gpg-agent.conf
+ ${pkgs.coreutils}/bin/ln -snf ${gnupg.dirmngrConf} ${gnupg.gnupgHome}/dirmngr.conf
+ export GNUPGHOME=${gnupg.gnupgHome}
+ install -dm700 "$GNUPGHOME"
+ export GPG_TTY=$(${pkgs.coreutils}/bin/tty)
+ ${pkgs.gnupg}/bin/gpgconf --launch gpg-agent
+ export SSH_AUTH_SOCK=$(${pkgs.gnupg}/bin/gpgconf --list-dirs agent-ssh-socket)
+ '';
};
}