1import ./make-test-python.nix (
2 { pkgs, lib, ... }:
3
4 {
5 name = "gnupg";
6 meta = with lib.maintainers; {
7 maintainers = [ rnhmjoj ];
8 };
9
10 # server for testing SSH
11 nodes.server =
12 { ... }:
13 {
14 imports = [ ../modules/profiles/minimal.nix ];
15
16 users.users.alice.isNormalUser = true;
17 services.openssh.enable = true;
18 };
19
20 # machine for testing GnuPG
21 nodes.machine =
22 { pkgs, ... }:
23 {
24 imports = [ ../modules/profiles/minimal.nix ];
25
26 users.users.alice.isNormalUser = true;
27 services.getty.autologinUser = "alice";
28
29 environment.shellInit = ''
30 # preset a key passphrase in gpg-agent
31 preset_key() {
32 # find all keys
33 case "$1" in
34 ssh) grips=$(awk '/^[0-9A-F]/{print $1}' "''${GNUPGHOME:-$HOME/.gnupg}/sshcontrol") ;;
35 pgp) grips=$(gpg --with-keygrip --list-secret-keys | awk '/Keygrip/{print $3}') ;;
36 esac
37
38 # try to preset the passphrase for each key found
39 for grip in $grips; do
40 "$(gpgconf --list-dirs libexecdir)/gpg-preset-passphrase" -c -P "$2" "$grip"
41 done
42 }
43 '';
44
45 programs.gnupg.agent.enable = true;
46 programs.gnupg.agent.enableSSHSupport = true;
47 };
48
49 testScript = ''
50 import shlex
51
52
53 def as_alice(command: str) -> str:
54 """
55 Wraps a command to run it as Alice in a login shell
56 """
57 quoted = shlex.quote(command)
58 return "su --login alice --command " + quoted
59
60
61 start_all()
62
63 with subtest("Wait for the autologin"):
64 machine.wait_until_tty_matches("1", "alice@machine")
65
66 with subtest("Can generate a PGP key"):
67 # Note: this needs a tty because of pinentry
68 machine.send_chars("gpg --gen-key\n")
69 machine.wait_until_tty_matches("1", "Real name:")
70 machine.send_chars("Alice\n")
71 machine.wait_until_tty_matches("1", "Email address:")
72 machine.send_chars("alice@machine\n")
73 machine.wait_until_tty_matches("1", "Change")
74 machine.send_chars("O\n")
75 machine.wait_until_tty_matches("1", "Please enter")
76 machine.send_chars("pgp_p4ssphrase")
77 machine.send_key("tab")
78 machine.send_chars("pgp_p4ssphrase")
79 machine.wait_until_tty_matches("1", "Passphrases match")
80 machine.send_chars("\n")
81 machine.wait_until_tty_matches("1", "public and secret key created")
82
83 with subtest("Confirm the key is in the keyring"):
84 machine.wait_until_succeeds(as_alice("gpg --list-secret-keys | grep -q alice@machine"))
85
86 with subtest("Can generate and add an SSH key"):
87 machine.succeed(as_alice("ssh-keygen -t ed25519 -f alice -N ssh_p4ssphrase"))
88
89 # Note: apparently this must be run before using the OpenSSH agent
90 # socket for the first time in a tty. It's not needed for `ssh`
91 # because there's a hook that calls it automatically (only in NixOS).
92 machine.send_chars("gpg-connect-agent updatestartuptty /bye\n")
93
94 # Note: again, this needs a tty because of pinentry
95 machine.send_chars("ssh-add alice\n")
96 machine.wait_until_tty_matches("1", "Enter passphrase")
97 machine.send_chars("ssh_p4ssphrase\n")
98 machine.wait_until_tty_matches("1", "Please enter")
99 machine.send_chars("ssh_agent_p4ssphrase")
100 machine.send_key("tab")
101 machine.send_chars("ssh_agent_p4ssphrase")
102 machine.wait_until_tty_matches("1", "Passphrases match")
103 machine.send_chars("\n")
104
105 with subtest("Confirm the SSH key has been registered"):
106 machine.wait_until_succeeds(as_alice("ssh-add -l | grep -q alice@machine"))
107
108 with subtest("Can preset the key passphrases in the agent"):
109 machine.succeed(as_alice("echo allow-preset-passphrase > .gnupg/gpg-agent.conf"))
110 machine.succeed(as_alice("pkill gpg-agent"))
111 machine.succeed(as_alice("preset_key pgp pgp_p4ssphrase"))
112 machine.succeed(as_alice("preset_key ssh ssh_agent_p4ssphrase"))
113
114 with subtest("Can encrypt and decrypt a message"):
115 machine.succeed(as_alice("echo Hello | gpg -e -r alice | gpg -d | grep -q Hello"))
116
117 with subtest("Can log into the server"):
118 # Install Alice's public key
119 public_key = machine.succeed(as_alice("cat alice.pub"))
120 server.succeed("mkdir /etc/ssh/authorized_keys.d")
121 server.succeed(f"printf '{public_key}' > /etc/ssh/authorized_keys.d/alice")
122
123 server.wait_for_open_port(22)
124 machine.succeed(as_alice("ssh -i alice -o StrictHostKeyChecking=no server exit"))
125 '';
126 }
127)