#!/usr/bin/perl -w ######################################## # Exploring the Web of Trust with GnuPG # by Ralf Hüls # # This program recursively searches the web of signators for a given key in # your GnuPG keyring. It produces a list of signators and signators' # signators by levels of remoteness. It creates Unix style shell scripts # that retrieve missing keys from keyservers (I don't use automatic key # retrieval). It also produces graph data output that can be used with # Graphviz from AT&T and Lucent Bell Labs. # # Usage: graph # # is a key-ID (e.g. 4931A04F) # is a number. # # Output: # levels.txt - List of signators # graph.dot - Input for "dot" in the Graphviz package # getkeys-.sh - Retrieve missing keys for level # # Needs: # GnuPG 1.0.4 (http://www.gnupg.org/) # Perl 5 (http://www.perl.com/) # # For graph visualization, use: # Graphviz (http://www.research.att.com/sw/tools/graphviz/) # # # (c) 17-11-2000 by Ralf Hüls # 03-03-2002 Minor bugfix for Perl 5.6 # # This program is a dirty hack, use it at your own risk ;-) # Non-commercial use and distribution of this program is permitted. # If you modify this program to create something useful or # interesting, I'd like to hear about it. # # Ralf Hüls # http://www.teleute.ping.de/ # DSA key: E131C5A4 # RSA key: 4931A04F # ######################################## # Change this to your favorite Keyserver my $keyserver="wwwkeys.de.pgp.net"; #my $keyserver=certserver.pgp.com; ######################################## use strict; my $top; my $depth; my @known; my @unknown; my $key; my %data; my %graph; my %mail; my %level; my $i; my $a; my $b; my $edge; my $nodeformat; my @color=qw{green red blue magenta black gray}; die "\nUsage: graph \n\n is a key-ID (e.g. 4931A04F)\n is a number.\n" unless (scalar(@ARGV)==2); $top=$ARGV[0]; $depth=$ARGV[1]; $level{$top}=0; $nodeformat='[shape=box,'; print "0 $top\n"; $known[0]{$top}++; getkeys($top,1); open G,">graph.dot"; print G "digraph G {\n"; foreach (keys %graph) { $a=substr($_,0,8); $b=substr($_,8,8); $edge=$level{$a}; if ($level{$b} > $level{$a}) {$edge=$level{$b}}; if ($graph{$_} == 1) { print G "\"$a\" -> \"$b\" [color=$color[$edge]];\n"; print G "\"$a\" $nodeformat color=$color[$level{$a}]];\n"; print G "\"$b\" $nodeformat color=$color[$level{$b}]];\n"; } if ($graph{$_} == 2) { print G "\"$a\" -> \"$b\" [dir=\"both\" color=$color[$edge]];\n"; print G "\"$a\" $nodeformat color=$color[$level{$a}]];\n"; print G "\"$b\" $nodeformat color=$color[$level{$b}]];\n"; } } print G "}\n"; close G; open G ,">levels.txt" or die "$!\n";; for ($i=0;$i<=$depth;$i++) { foreach $key (keys %{$known[$i]}) { if (defined($known[$i]{$key})) { print G "$i $key $data{$key}"; } } foreach $key (keys %{$unknown[$i]}) { if (defined($unknown[$i]{$key})) { print G "$i $key ???\n"; } } print G "\n"; open F,">getkeys-$i.sh" or die "$!\n";; print F "#!/bin/sh\n"; foreach $key (keys %{$unknown[$i]}) { if (defined($unknown[$i]{$key})) { print F "gpg --allow-non-selfsigned-uid --keyserver $keyserver --recv-keys $key\n"; } } close F; } close G; ########################################## sub getkeys{ my $sig; my $d=0; my @output; $d=$_[1]; open(S,"gpg --check-sigs $_[0]|") or die "$!"; @output=; close S; foreach (@output) { if (/^sig\!/) { $sig=substr($_,11,8); $data{$sig}=substr($_,32); unless ( ($sig eq $_[0]) || ($graph{"$sig$_[0]"})) { $graph{"$sig$_[0]"}=1; if ($graph{"$_[0]$sig"}) { $graph{"$sig$_[0]"}=2; $graph{"$_[0]$sig"}=3; } } unless (defined($level{$sig}) && $level{$sig}<=$d) { if (defined($level{$sig})) { undef ($known[$level{$sig}]{$sig}); } $level{$sig}=$d; $known[$d]{$sig}++; if ($d<$depth) { getkeys($sig,$d+1); } } } if (/^sig\?/) { $sig=substr($_,11,8); $data{$sig}=substr($_,32); unless ($graph{"$sig$_[0]"}) { $graph{"$sig$_[0]"}=1; if ($graph{"$_[0]$sig"}) { $graph{"$sig$_[0]"}=2; $graph{"$_[0]$sig"}=3; } } unless (defined($level{$sig}) && $level{$sig}<=$d) { if (defined($level{$sig}) ) { undef ($known[$level{$sig}]{$sig}); } $level{$sig}=$d; $unknown[$d]{$sig}++; } } } }