diff options
17 files changed, 178 insertions, 2 deletions
diff --git a/g10/tofu.c b/g10/tofu.c index 8184c6f98..dcee6e70f 100644 --- a/g10/tofu.c +++ b/g10/tofu.c @@ -1211,7 +1211,7 @@ format_conflict_msg_part1 (int policy, strlist_t conflict_set, /* Return 1 if A signed B and B signed A. */ static int -cross_sigs (kbnode_t a, kbnode_t b) +cross_sigs (const char *email, kbnode_t a, kbnode_t b) { int i; @@ -1240,12 +1240,36 @@ cross_sigs (kbnode_t a, kbnode_t b) u32 *signer_kid = pk_main_keyid (signer_pk); kbnode_t n; + int saw_email = 0; + /* Iterate over SIGNEE's keyblock and see if there is a valid signature from SIGNER. */ for (n = signee; n; n = n->next) { PKT_signature *sig; + if (n->pkt->pkttype == PKT_USER_ID) + { + if (saw_email) + /* We're done: we've processed all signatures on the + user id. */ + break; + else + { + /* See if this is the matching user id. */ + PKT_user_id *user_id = n->pkt->pkt.user_id; + char *email2 = email_from_user_id (user_id->name); + + if (strcmp (email, email2) == 0) + saw_email = 1; + + xfree (email2); + } + } + + if (! saw_email) + continue; + if (n->pkt->pkttype != PKT_SIGNATURE) continue; @@ -1974,7 +1998,7 @@ build_conflict_set (tofu_dbs_t dbs, const char *fingerprint, const char *email) for (j = i + 1; j < conflict_set_count; j ++) /* Be careful: we might not have a key block for a key. */ - if (kb_all[i] && kb_all[j] && cross_sigs (kb_all[i], kb_all[j])) + if (kb_all[i] && kb_all[j] && cross_sigs (email, kb_all[i], kb_all[j])) die[j] = 1; } diff --git a/tests/openpgp/tofu.scm b/tests/openpgp/tofu.scm index e514ddfd1..96f7abe7a 100755 --- a/tests/openpgp/tofu.scm +++ b/tests/openpgp/tofu.scm @@ -159,3 +159,76 @@ (checkpolicy "BC15C85A" "ask") (checkpolicy "2183839A" "bad") (checkpolicy "EE37CF96" "ask") + + + +;; Check that we detect the following attack: +;; +;; Alice and Bob each have a key and cross sign them. Bob then adds a +;; new user id, "Alice". TOFU should now detect a conflict, because +;; Alice only signed Bob's "Bob" user id. + +(display "Checking cross sigs...\n") +(define GPG `(,(tool 'gpg) --no-permission-warning + --faked-system-time=1476304861)) + +;; Carefully remove the TOFU db. +(catch '() (unlink (string-append GNUPGHOME "/tofu.db"))) + +(define DIR "tofu/cross-sigs") +;; The test keys. +(define KEYA "1938C3A0E4674B6C217AC0B987DB2814EC38277E") +(define KEYB "DC463A16E42F03240D76E8BA8B48C6BD871C2247") + +(define (verify-messages) + (for-each + (lambda (key) + (for-each + (lambda (i) + (let ((fn (in-srcdir DIR (string-append key "-" i ".txt")))) + (call-check `(,@GPG --trust-model=tofu --verify ,fn)))) + (list "1" "2"))) + (list KEYA KEYB))) + +;; Import the public keys. +(display " > Two keys. ") +(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYA "-1.gpg")))) +(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYB "-1.gpg")))) +;; Make sure the tofu engine registers the keys. +(verify-messages) +(display "<\n") + +;; Since their is no conflict, the policy should be auto. +(checkpolicy KEYA "auto") +(checkpolicy KEYB "auto") + +;; Import the cross sigs. +(display " > Adding cross signatures. ") +(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYA "-2.gpg")))) +(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYB "-2.gpg")))) +(verify-messages) +(display "<\n") + +;; There is still no conflict, so the policy shouldn't have changed. +(checkpolicy KEYA "auto") +(checkpolicy KEYB "auto") + +;; Import the conflicting user id. +(display " > Adding conflicting user id. ") +(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYB "-3.gpg")))) +(call-check `(,@GPG --trust-model=tofu + --verify ,(in-srcdir DIR (string-append KEYB "-1.txt")))) +(verify-messages) +(display "<\n") + +(checkpolicy KEYA "ask") +(checkpolicy KEYB "ask") + +;; Import Alice's signature on the conflicting user id. +(display " > Adding cross signature on user id. ") +(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYB "-4.gpg")))) +(verify-messages) +(display "<\n") + +(checkpolicy KEYA "auto") +(checkpolicy KEYB "auto") diff --git a/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-1.gpg b/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-1.gpg Binary files differnew file mode 100644 index 000000000..e6becec47 --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-1.gpg diff --git a/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-1.txt b/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-1.txt Binary files differnew file mode 100644 index 000000000..92236be2e --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-1.txt diff --git a/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-2.gpg b/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-2.gpg Binary files differnew file mode 100644 index 000000000..d26bd541d --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-2.gpg diff --git a/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-2.txt b/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-2.txt Binary files differnew file mode 100644 index 000000000..b4013d364 --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-2.txt diff --git a/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-3.txt b/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-3.txt Binary files differnew file mode 100644 index 000000000..9b2d49d10 --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-3.txt diff --git a/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-secret.gpg b/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-secret.gpg Binary files differnew file mode 100644 index 000000000..1839e3a3e --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/1938C3A0E4674B6C217AC0B987DB2814EC38277E-secret.gpg diff --git a/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-1.gpg b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-1.gpg Binary files differnew file mode 100644 index 000000000..f706f7037 --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-1.gpg diff --git a/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-1.txt b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-1.txt Binary files differnew file mode 100644 index 000000000..0bdc1fc52 --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-1.txt diff --git a/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-2.gpg b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-2.gpg Binary files differnew file mode 100644 index 000000000..0b2485feb --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-2.gpg diff --git a/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-2.txt b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-2.txt Binary files differnew file mode 100644 index 000000000..4d3aaaa44 --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-2.txt diff --git a/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-3.gpg b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-3.gpg Binary files differnew file mode 100644 index 000000000..eb2c43532 --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-3.gpg diff --git a/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-3.txt b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-3.txt Binary files differnew file mode 100644 index 000000000..9b2d49d10 --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-3.txt diff --git a/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-4.gpg b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-4.gpg Binary files differnew file mode 100644 index 000000000..9c98ec114 --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-4.gpg diff --git a/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-secret.gpg b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-secret.gpg Binary files differnew file mode 100644 index 000000000..a87c61b4f --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/DC463A16E42F03240D76E8BA8B48C6BD871C2247-secret.gpg diff --git a/tests/openpgp/tofu/cross-sigs/README b/tests/openpgp/tofu/cross-sigs/README new file mode 100644 index 000000000..439962bd7 --- /dev/null +++ b/tests/openpgp/tofu/cross-sigs/README @@ -0,0 +1,79 @@ +# How I generate the keys and messages to verify: + +# Generate and export two non-conflicting keys. +gpg --quick-gen-key 'Spy Cow <[email protected]>' +gpg --quick-gen-key 'Spy R. Cow <[email protected]>' + +KEYIDA=1938C3A0E4674B6C217AC0B987DB2814EC38277E +KEYIDB=DC463A16E42F03240D76E8BA8B48C6BD871C2247 + +for KEYID in $KEYIDA $KEYIDB +do + gpg --export $KEYID > tofu-$KEYID.gpg + gpg --export-secret-keys $KEYID > tofu-$KEYID-secret.gpg +done + +# Sign some data. +echo foo | gpg --default-key $KEYIDA -s > tofu-$KEYIDA-1.txt +echo foo | gpg --default-key $KEYIDB -s > tofu-$KEYIDB-1.txt + +# Again, but with an issuer. +echo foo | gpg --default-key "<[email protected]>" -s > tofu-$KEYIDA-2.txt +echo foo | gpg --default-key "<[email protected]>" -s > tofu-$KEYIDB-2.txt + +# Have A sign B and vice versa. +gpg --default-key $KEYIDA --quick-sign $KEYIDB +gpg --default-key $KEYIDB --quick-sign $KEYIDA + +gpg --export $KEYIDA > tofu-$KEYIDA-2.gpg +gpg --export $KEYIDB > tofu-$KEYIDB-2.gpg + +# Cause A and B to conflict. +gpg --quick-adduid $KEYIDB 'Spy R. Cow <[email protected]>' +gpg --export $KEYIDB > tofu-$KEYIDB-3.gpg + +echo foo | gpg --default-key "<[email protected]>" -s > tofu-$KEYIDA-3.txt +echo foo | gpg --default-key "<[email protected]>" -s > tofu-$KEYIDB-3.txt + +# Have A sign B's conflicting user id. +gpg --default-key $KEYIDA --quick-sign $KEYIDB +gpg --export $KEYIDB > tofu-$KEYIDB-4.gpg + +exit 0 + +# In a new directory (so the keys are not ultimately trusted). + +D=~/neal/work/gpg/test +echo 'trust-model tofu+pgp' > gpg.conf +gpg --import $D/tofu-$KEYIDA.gpg +gpg --import $D/tofu-$KEYIDB.gpg +gpg -k + +gpg --verify $D/tofu-$KEYIDA-1.txt +gpg --verify $D/tofu-$KEYIDB-1.txt +# With an issuer. +gpg --verify $D/tofu-$KEYIDA-2.txt +gpg --verify $D/tofu-$KEYIDB-2.txt + +# Import the cross signatures. +gpg --import $D/tofu-$KEYIDA-2.gpg +gpg --import $D/tofu-$KEYIDB-2.gpg +gpg -k + +gpg --verify $D/tofu-$KEYIDA-1.txt +gpg --verify $D/tofu-$KEYIDB-1.txt +# With an issuer. +gpg --verify $D/tofu-$KEYIDA-2.txt +gpg --verify $D/tofu-$KEYIDB-2.txt + + +gpg --status-fd=1 --batch --verify $D/tofu-$KEYIDA-3.txt | grep TRUST_UNDEFINED +gpg --status-fd=1 --batch --verify $D/tofu-$KEYIDB-3.txt | grep TRUST_UNDEFINED + +# Import the conflicting user id. +gpg --import $D/tofu-$KEYIDB-3.gpg +gpg -k + +# Import the cross signature, which should remove the conflict. +gpg --import $D/tofu-$KEYIDB-4.gpg +gpg -k |