g10: Be more careful when checking cross signatures.

* g10/tofu.c (cross_sigs): When checking cross signatures, only
consider the signatures on the specified user id.
* tests/openpgp/tofu.scm: Add test for the above.
* tests/openpgp/tofu/cross-sigs/
  1938C3A0E4674B6C217AC0B987DB2814EC38277E-1.gpg:
  New file.
* tests/openpgp/tofu/cross-sigs/
  1938C3A0E4674B6C217AC0B987DB2814EC38277E-1.txt: New file.
* tests/openpgp/tofu/cross-sigs/
  1938C3A0E4674B6C217AC0B987DB2814EC38277E-2.gpg: New file.
* tests/openpgp/tofu/cross-sigs/
  1938C3A0E4674B6C217AC0B987DB2814EC38277E-2.txt: New file.
* tests/openpgp/tofu/cross-sigs/
  1938C3A0E4674B6C217AC0B987DB2814EC38277E-3.txt: New file.
* tests/openpgp/tofu/cross-sigs/
  1938C3A0E4674B6C217AC0B987DB2814EC38277E-secret.gpg: New file.
* tests/openpgp/tofu/cross-sigs/
  DC463A16E42F03240D76E8BA8B48C6BD871C2247-1.gpg: New file.
* tests/openpgp/tofu/cross-sigs/
  DC463A16E42F03240D76E8BA8B48C6BD871C2247-1.txt: New file.
* tests/openpgp/tofu/cross-sigs/
  DC463A16E42F03240D76E8BA8B48C6BD871C2247-2.gpg: New file.
* tests/openpgp/tofu/cross-sigs/
  DC463A16E42F03240D76E8BA8B48C6BD871C2247-2.txt: New file.
* tests/openpgp/tofu/cross-sigs/
  DC463A16E42F03240D76E8BA8B48C6BD871C2247-3.gpg: New file.
* tests/openpgp/tofu/cross-sigs/
  DC463A16E42F03240D76E8BA8B48C6BD871C2247-3.txt: New file.
* tests/openpgp/tofu/cross-sigs/
  DC463A16E42F03240D76E8BA8B48C6BD871C2247-4.gpg: New file.
* tests/openpgp/tofu/cross-sigs/
  DC463A16E42F03240D76E8BA8B48C6BD871C2247-secret.gpg: New file.
* tests/openpgp/tofu/cross-sigs/README: New file.

--
Signed-off-by: Neal H. Walfield
This commit is contained in:
Neal H. Walfield 2016-10-13 12:44:59 +02:00
parent e09166c772
commit 4c0389f8eb
17 changed files with 178 additions and 2 deletions

View File

@ -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;
}

View File

@ -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")

View File

@ -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 <spy@cow.com>'
gpg --quick-gen-key 'Spy R. Cow <spy@cow.de>'
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 "<spy@cow.com>" -s > tofu-$KEYIDA-2.txt
echo foo | gpg --default-key "<spy@cow.de>" -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 <spy@cow.com>'
gpg --export $KEYIDB > tofu-$KEYIDB-3.gpg
echo foo | gpg --default-key "<spy@cow.com>" -s > tofu-$KEYIDA-3.txt
echo foo | gpg --default-key "<spy@cow.com>" -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