| 1 | # We always set GNUPGHOME, even if no usable GPG was found, as |
| 2 | # |
| 3 | # - It does not hurt, and |
| 4 | # |
| 5 | # - we cannot set global environment variables in lazy prereqs because they are |
| 6 | # executed in an eval'ed subshell that changes the working directory to a |
| 7 | # temporary one. |
| 8 | |
| 9 | GNUPGHOME="$(pwd)/gpghome" |
| 10 | export GNUPGHOME |
| 11 | |
| 12 | # All the "test_lazy_prereq GPG*" below should use |
| 13 | # `prepare_gnupghome()` either directly or through a call to |
| 14 | # `test_have_prereq GPG*`. That's because `gpg` and `gpgsm` |
| 15 | # only create the directory specified using "$GNUPGHOME" or |
| 16 | # `--homedir` if it's the default (usually "~/.gnupg"). |
| 17 | prepare_gnupghome() { |
| 18 | mkdir -p "$GNUPGHOME" && |
| 19 | chmod 0700 "$GNUPGHOME" |
| 20 | } |
| 21 | |
| 22 | test_lazy_prereq GPG ' |
| 23 | gpg_version=$(gpg --version 2>&1) |
| 24 | test $? != 127 || exit 1 |
| 25 | |
| 26 | # As said here: https://web.archive.org/web/20130212022238/https://www.gnupg.org/faq/gnupg-faq.html#why-does-gnupg-1.0.6-bail-out-on-keyrings-used-with-1.0.7 |
| 27 | # the gpg version 1.0.6 did not parse trust packets correctly, so for |
| 28 | # that version, creation of signed tags using the generated key fails. |
| 29 | case "$gpg_version" in |
| 30 | "gpg (GnuPG) 1.0.6"*) |
| 31 | say "Your version of gpg (1.0.6) is too buggy for testing" |
| 32 | exit 1 |
| 33 | ;; |
| 34 | *) |
| 35 | # Available key info: |
| 36 | # * Type DSA and Elgamal, size 2048 bits, no expiration date, |
| 37 | # name and email: C O Mitter <committer@example.com> |
| 38 | # * Type RSA, size 2048 bits, no expiration date, |
| 39 | # name and email: Eris Discordia <discord@example.net> |
| 40 | # No password given, to enable non-interactive operation. |
| 41 | # To generate new key: |
| 42 | # gpg --homedir /tmp/gpghome --gen-key |
| 43 | # To write armored exported key to keyring: |
| 44 | # gpg --homedir /tmp/gpghome --export-secret-keys \ |
| 45 | # --armor 0xDEADBEEF >> lib-gpg/keyring.gpg |
| 46 | # gpg --homedir /tmp/gpghome --export \ |
| 47 | # --armor 0xDEADBEEF >> lib-gpg/keyring.gpg |
| 48 | # To export ownertrust: |
| 49 | # gpg --homedir /tmp/gpghome --export-ownertrust \ |
| 50 | # > lib-gpg/ownertrust |
| 51 | prepare_gnupghome && |
| 52 | (gpgconf --kill all || : ) && |
| 53 | gpg --homedir "${GNUPGHOME}" --import \ |
| 54 | "$TEST_DIRECTORY"/lib-gpg/keyring.gpg && |
| 55 | gpg --homedir "${GNUPGHOME}" --import-ownertrust \ |
| 56 | "$TEST_DIRECTORY"/lib-gpg/ownertrust && |
| 57 | gpg --homedir "${GNUPGHOME}" --update-trustdb && |
| 58 | gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null \ |
| 59 | --sign -u committer@example.com |
| 60 | ;; |
| 61 | esac |
| 62 | ' |
| 63 | |
| 64 | test_lazy_prereq GPG2 ' |
| 65 | gpg_version=$(gpg --version 2>&1) |
| 66 | test $? != 127 || exit 1 |
| 67 | |
| 68 | case "$gpg_version" in |
| 69 | "gpg (GnuPG) "[01].*) |
| 70 | say "This test requires a GPG version >= v2.0.0" |
| 71 | exit 1 |
| 72 | ;; |
| 73 | *) |
| 74 | prepare_gnupghome && |
| 75 | (gpgconf --kill all || : ) && |
| 76 | |
| 77 | # NEEDSWORK: prepare_gnupghome() should definitely be |
| 78 | # called here, but it looks like it exposes a |
| 79 | # pre-existing, hidden bug by allowing some tests in |
| 80 | # t1016-compatObjectFormat.sh to run instead of being |
| 81 | # skipped. See: |
| 82 | # https://lore.kernel.org/git/ZoV8b2RvYxLOotSJ@teonanacatl.net/ |
| 83 | |
| 84 | gpg --homedir "${GNUPGHOME}" --import \ |
| 85 | "$TEST_DIRECTORY"/lib-gpg/keyring.gpg && |
| 86 | gpg --homedir "${GNUPGHOME}" --import-ownertrust \ |
| 87 | "$TEST_DIRECTORY"/lib-gpg/ownertrust && |
| 88 | gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null \ |
| 89 | --sign -u committer@example.com |
| 90 | ;; |
| 91 | esac |
| 92 | ' |
| 93 | |
| 94 | test_lazy_prereq GPGSM ' |
| 95 | test_have_prereq GPG && |
| 96 | # Available key info: |
| 97 | # * see t/lib-gpg/gpgsm-gen-key.in |
| 98 | # To generate new certificate: |
| 99 | # * no passphrase |
| 100 | # gpgsm --homedir /tmp/gpghome/ \ |
| 101 | # -o /tmp/gpgsm.crt.user \ |
| 102 | # --generate-key \ |
| 103 | # --batch t/lib-gpg/gpgsm-gen-key.in |
| 104 | # To import certificate: |
| 105 | # gpgsm --homedir /tmp/gpghome/ \ |
| 106 | # --import /tmp/gpgsm.crt.user |
| 107 | # To export into a .p12 we can later import: |
| 108 | # gpgsm --homedir /tmp/gpghome/ \ |
| 109 | # -o t/lib-gpg/gpgsm_cert.p12 \ |
| 110 | # --export-secret-key-p12 "committer@example.com" |
| 111 | echo | gpgsm --homedir "${GNUPGHOME}" \ |
| 112 | --passphrase-fd 0 --pinentry-mode loopback \ |
| 113 | --import "$TEST_DIRECTORY"/lib-gpg/gpgsm_cert.p12 && |
| 114 | |
| 115 | gpgsm --homedir "${GNUPGHOME}" -K --with-colons | |
| 116 | awk -F ":" "/^fpr:/ {printf \"%s S relax\\n\", \$10}" \ |
| 117 | >"${GNUPGHOME}/trustlist.txt" && |
| 118 | (gpgconf --reload all || : ) && |
| 119 | |
| 120 | echo hello | gpgsm --homedir "${GNUPGHOME}" >/dev/null \ |
| 121 | -u committer@example.com -o /dev/null --sign - |
| 122 | ' |
| 123 | |
| 124 | test_lazy_prereq RFC1991 ' |
| 125 | test_have_prereq GPG && |
| 126 | echo | gpg --homedir "${GNUPGHOME}" -b --rfc1991 >/dev/null |
| 127 | ' |
| 128 | |
| 129 | GPGSSH_KEY_PRIMARY="${GNUPGHOME}/ed25519_ssh_signing_key" |
| 130 | GPGSSH_KEY_SECONDARY="${GNUPGHOME}/rsa_2048_ssh_signing_key" |
| 131 | GPGSSH_KEY_UNTRUSTED="${GNUPGHOME}/untrusted_ssh_signing_key" |
| 132 | GPGSSH_KEY_EXPIRED="${GNUPGHOME}/expired_ssh_signing_key" |
| 133 | GPGSSH_KEY_NOTYETVALID="${GNUPGHOME}/notyetvalid_ssh_signing_key" |
| 134 | GPGSSH_KEY_TIMEBOXEDVALID="${GNUPGHOME}/timeboxed_valid_ssh_signing_key" |
| 135 | GPGSSH_KEY_TIMEBOXEDINVALID="${GNUPGHOME}/timeboxed_invalid_ssh_signing_key" |
| 136 | GPGSSH_KEY_WITH_PASSPHRASE="${GNUPGHOME}/protected_ssh_signing_key" |
| 137 | GPGSSH_KEY_ECDSA="${GNUPGHOME}/ecdsa_ssh_signing_key" |
| 138 | GPGSSH_KEY_PASSPHRASE="super_secret" |
| 139 | GPGSSH_ALLOWED_SIGNERS="${GNUPGHOME}/ssh.all_valid.allowedSignersFile" |
| 140 | |
| 141 | GPGSSH_GOOD_SIGNATURE_TRUSTED='Good "git" signature for' |
| 142 | GPGSSH_GOOD_SIGNATURE_UNTRUSTED='Good "git" signature with' |
| 143 | GPGSSH_KEY_NOT_TRUSTED="No principal matched" |
| 144 | GPGSSH_BAD_SIGNATURE="Signature verification failed" |
| 145 | |
| 146 | test_lazy_prereq GPGSSH ' |
| 147 | ssh_version=$(ssh-keygen -Y find-principals -n "git" 2>&1) |
| 148 | test $? != 127 || exit 1 |
| 149 | echo $ssh_version | grep -q "find-principals:missing signature file" |
| 150 | test $? = 0 || exit 1; |
| 151 | |
| 152 | # Setup some keys and an allowed signers file |
| 153 | prepare_gnupghome && |
| 154 | (setfacl -k "${GNUPGHOME}" 2>/dev/null || true) && |
| 155 | ssh-keygen -t ed25519 -N "" -C "git ed25519 key" -f "${GPGSSH_KEY_PRIMARY}" >/dev/null && |
| 156 | ssh-keygen -t rsa -b 2048 -N "" -C "git rsa2048 key" -f "${GPGSSH_KEY_SECONDARY}" >/dev/null && |
| 157 | ssh-keygen -t ed25519 -N "${GPGSSH_KEY_PASSPHRASE}" -C "git ed25519 encrypted key" -f "${GPGSSH_KEY_WITH_PASSPHRASE}" >/dev/null && |
| 158 | ssh-keygen -t ecdsa -N "" -f "${GPGSSH_KEY_ECDSA}" >/dev/null && |
| 159 | ssh-keygen -t ed25519 -N "" -C "git ed25519 key" -f "${GPGSSH_KEY_UNTRUSTED}" >/dev/null && |
| 160 | |
| 161 | cat >"${GPGSSH_ALLOWED_SIGNERS}" <<-EOF && |
| 162 | "principal with number 1" $(cat "${GPGSSH_KEY_PRIMARY}.pub")" |
| 163 | "principal with number 2" $(cat "${GPGSSH_KEY_SECONDARY}.pub")" |
| 164 | "principal with number 3" $(cat "${GPGSSH_KEY_WITH_PASSPHRASE}.pub")" |
| 165 | "principal with number 4" $(cat "${GPGSSH_KEY_ECDSA}.pub")" |
| 166 | EOF |
| 167 | |
| 168 | # Verify if at least one key and ssh-keygen works as expected |
| 169 | echo "testpayload" | |
| 170 | ssh-keygen -Y sign -n "git" -f "${GPGSSH_KEY_PRIMARY}" >gpgssh_prereq.sig && |
| 171 | ssh-keygen -Y find-principals -f "${GPGSSH_ALLOWED_SIGNERS}" -s gpgssh_prereq.sig && |
| 172 | echo "testpayload" | |
| 173 | ssh-keygen -Y verify -n "git" -f "${GPGSSH_ALLOWED_SIGNERS}" -I "principal with number 1" -s gpgssh_prereq.sig |
| 174 | ' |
| 175 | |
| 176 | test_lazy_prereq GPGSSH_VERIFYTIME ' |
| 177 | test_have_prereq GPGSSH && |
| 178 | # Check if ssh-keygen has a verify-time option by passing an invalid date to it |
| 179 | ssh-keygen -Overify-time=INVALID -Y check-novalidate -n "git" -s doesnotmatter 2>&1 | grep -q -F "Invalid \"verify-time\"" && |
| 180 | |
| 181 | # Set up keys with key lifetimes |
| 182 | ssh-keygen -t ed25519 -N "" -C "timeboxed valid key" -f "${GPGSSH_KEY_TIMEBOXEDVALID}" >/dev/null && |
| 183 | key_valid=$(cat "${GPGSSH_KEY_TIMEBOXEDVALID}.pub") && |
| 184 | ssh-keygen -t ed25519 -N "" -C "timeboxed invalid key" -f "${GPGSSH_KEY_TIMEBOXEDINVALID}" >/dev/null && |
| 185 | key_invalid=$(cat "${GPGSSH_KEY_TIMEBOXEDINVALID}.pub") && |
| 186 | ssh-keygen -t ed25519 -N "" -C "expired key" -f "${GPGSSH_KEY_EXPIRED}" >/dev/null && |
| 187 | key_expired=$(cat "${GPGSSH_KEY_EXPIRED}.pub") && |
| 188 | ssh-keygen -t ed25519 -N "" -C "not yet valid key" -f "${GPGSSH_KEY_NOTYETVALID}" >/dev/null && |
| 189 | key_notyetvalid=$(cat "${GPGSSH_KEY_NOTYETVALID}.pub") && |
| 190 | |
| 191 | # Timestamps outside of test_tick span |
| 192 | ts2005a=20050401000000 ts2005b=200504020000 && |
| 193 | # Timestamps within test_tick span |
| 194 | ts2005c=20050407000000 ts2005d=200504100000 && |
| 195 | # Definitely not yet valid / expired timestamps |
| 196 | ts2000=20000101000000 ts2999=29990101000000 && |
| 197 | |
| 198 | cat >>"${GPGSSH_ALLOWED_SIGNERS}" <<-EOF && |
| 199 | "timeboxed valid key" valid-after="$ts2005c",valid-before="$ts2005d" $key_valid" |
| 200 | "timeboxed invalid key" valid-after="$ts2005a",valid-before="$ts2005b" $key_invalid" |
| 201 | "principal with expired key" valid-before="$ts2000" $key_expired" |
| 202 | "principal with not yet valid key" valid-after="$ts2999" $key_notyetvalid" |
| 203 | EOF |
| 204 | |
| 205 | # and verify ssh-keygen verifies the key lifetime |
| 206 | echo "testpayload" | |
| 207 | ssh-keygen -Y sign -n "git" -f "${GPGSSH_KEY_EXPIRED}" >gpgssh_verifytime_prereq.sig && |
| 208 | ! (ssh-keygen -Y verify -n "git" -f "${GPGSSH_ALLOWED_SIGNERS}" -I "principal with expired key" -s gpgssh_verifytime_prereq.sig) |
| 209 | ' |
| 210 | |
| 211 | sanitize_pgp() { |
| 212 | sed "/^-----BEGIN PGP/,/^-----END PGP/{/^-/p;d;}" |
| 213 | } |