{ config, lib, pkgs, ... }: { options.zpha.programs.gpg.enable = lib.mkEnableOption "gpg"; config = lib.mkIf config.zpha.programs.gpg.enable { preservation.preserveAt."/persist".users.zaphyra.directories = lib.singleton { mode = "0700"; directory = ".config/gnupg"; }; users.users.zaphyra.maid = { packages = with pkgs; [ pcsc-tools gnupg ]; environment.sessionVariables = { GNUPGHOME = "/home/zaphyra/.config/gnupg"; }; programs.fish.config = '' set -e SSH_AGENT_PID begin set -l gnupg_val 0 if set -q gnupg_SSH_AUTH_SOCK_by set gnupg_val $gnupg_SSH_AUTH_SOCK_by end if test $gnupg_val -ne %self set -x SSH_AUTH_SOCK (${lib.getExe' pkgs.gnupg "gpgconf"} --list-dirs agent-ssh-socket) end end ''; programs.git.settings = { user.signingKey = "BFE6386C8D66BCD4DAE14FC895F0FE7CD7E6A022"; commit.gpgSign = true; }; file.xdg_config = { "gnupg/gpg.conf".text = '' keyserver "hkps://keyserver.ubuntu.com:443" ''; "gnupg/scdaemon.conf".text = '' disable-ccid ''; "gnupg/gpg-agent.conf".text = '' enable-ssh-support default-cache-ttl 600 default-cache-ttl-ssh 600 pinentry-program "${lib.getExe pkgs.pinentry-gnome3}" ''; "gnupg/sshcontrol".text = '' 595A90924C65444EF00F0890BA85DE2A57DD79B2 ''; }; systemd = { services.gpg-agent = { unitConfig = { Description = "GnuPG cryptographic agent and passphrase cache"; Documentation = "man:gpg-agent(1)"; Requires = "gpg-agent.socket"; After = "gpg-agent.socket"; # This is a socket-activated service: RefuseManualStart = true; }; serviceConfig = { ExecStart = "${lib.getExe' pkgs.gnupg "gpg-agent"} --supervised"; ExecReload = "${lib.getExe' pkgs.gnupg "gpgconf"} --reload gpg-agent"; Environment = [ "GNUPGHOME=~/.config/gnupg" ]; }; }; sockets = let hexStringToBase32 = let mod = a: b: a - a / b * b; pow2 = lib.elemAt [ 1 2 4 8 16 32 64 128 256 ]; base32Alphabet = lib.stringToCharacters "ybndrfg8ejkmcpqxot1uwisza345h769"; hexToIntTable = lib.listToAttrs ( lib.genList (x: { name = lib.toLower (lib.toHexString x); value = x; }) 16 ); initState = { ret = ""; buf = 0; bufBits = 0; }; go = { ret, buf, bufBits, }: hex: let buf' = buf * pow2 4 + hexToIntTable.${hex}; bufBits' = bufBits + 4; extraBits = bufBits' - 5; in if bufBits >= 5 then { ret = ret + lib.elemAt base32Alphabet (buf' / pow2 extraBits); buf = mod buf' (pow2 extraBits); bufBits = bufBits' - 5; } else { inherit ret; buf = buf'; bufBits = bufBits'; }; in hexString: (lib.foldl' go initState (lib.stringToCharacters hexString)).ret; gpgconf = dir: let hash = lib.substring 0 24 ( hexStringToBase32 (builtins.hashString "sha1" "/home/zaphyra/.config/gnupg") ); subdir = "d.${hash}/${dir}"; in "%t/gnupg/${subdir}"; mkSocket = { desc, docs, stream, fdName, }: { wantedBy = [ "sockets.target" ]; unitConfig = { Description = desc; Documentation = docs; }; socketConfig = { ListenStream = gpgconf "${stream}"; FileDescriptorName = fdName; Service = "gpg-agent.service"; SocketMode = "0600"; DirectoryMode = "0700"; }; }; in { gpg-agent = mkSocket { desc = "GnuPG cryptographic agent and passphrase cache"; docs = "man:gpg-agent(1)"; stream = "S.gpg-agent"; fdName = "std"; }; gpg-agent-ssh = mkSocket { desc = "GnuPG cryptographic agent (ssh-agent emulation)"; docs = "man:gpg-agent(1) man:ssh-add(1) man:ssh-agent(1) man:ssh(1)"; stream = "S.gpg-agent.ssh"; fdName = "ssh"; }; }; }; }; }; }