#!/usr/bin/perl # sig2dot v0.27 (c) Darxus@ChaosReigns.com, released under the GPL # Download from: http://www.chaosreigns.com/code/sig2dot/ # # Parses output of "gpg --list-sigs" into a format # suitable for rendering into a graph by graphviz # (http://www.research.att.com/sw/tools/graphviz/) like so: # # $ gpg --list-sigs --keyring ./phillylinux.gpg --with-colons | ./sig2dot.pl > phillylinux.dot # $ neato -Tps phillylinux.dot > phillylinux.ps # $ convert phillylinux.ps phillylinux.jpg # # Commandline options: # # -b # Black and white / do not colorize. # # -d # Render graph as it appeared on (ignores more recent # signatures). Date must be in the format "YYYY-MM-DD". # Will also ignore keys that have since been revoked. # # Changes: # # v0.9 2000-09-14 19:20 strip trailing whitespace from $id more cleanly # v0.10 2000-09-14 19:33 skip revoked keys at the request of Peter Palfrader # v0.11 Nov 22 21:38 use ID for node name instead of username for uniqueness # v0.12 Dec 15 16:20 use names instead of IDs again in stats.html # v0.13 Jun 19 03:15 red is proportional to signatures # v0.14 Jun 19 03:25 blue is proportional to other keys signed # v0.15 Jun 20 17:16 fixed blue, green is proportional to ratio # v0.16 Jun 20 18:55 uniqed %signedby # v0.17 Jan 10 19:10 Use overlap=scale instead of fixed edge lengths. Requires new version of graphviz. # v0.18 Jan 23 11:53 stats.html is now valid html (v.01 transitional) # v0.23 May 3 18:52 bunch of new stuff, including -b flag (black & white), and fixes devision by zero error # v0.24 May 3 18:59 add black outline to nodes, prettier (changed node attribute "color" to "fillcolor") # v0.25 May 3 19:06 cleaned up anti- devision by zero code a little # v0.26 May 4 00:08 strip all non-digit characters from $renderdate # v0.27 May 10 00:23:49 2002 use {}'s to write 1 line per public key instead of one line per signature (much shorter) # v0.28 2002-06-27 Changed key parsing to use expect 'gpg --with-colons'. # Added escaping of quotes. $chartchar = "*"; require "getopts.pl"; &Getopts('d:b'); if ($opt_d) { $renderdate = $opt_d; print STDERR "Printing from date: $renderdate.\n"; $renderdate =~ s/\D+//g; } if ($opt_b) { $color = 0; print STDERR "Black and White.\n"; } else { $color = 1; print STDERR "Color.\n"; } while ($line = ) { @fields = split /:/, $line; $type = $fields[0]; $id = $fields[4]; $date = $fields[5]; $name = $fields[9]; $date =~ tr/-//d; $name =~ s/"/\\"/g; if ($type eq "pub" or $renderdate eq "" or $date <= $renderdate) { print STDERR "Using: $line"; # Strip whitespace more cleanly:. $name =~ s/^\s+|\s+$//g; # Just the name. if (not $name =~ /^]+>$//; } $name{$id} = $name; if ($type eq "pub") { $owner = $id; } # Skip revoked keys. next if ($fields[1] eq 'r'); # unless (defined @{$sigs{$owner}}) # { # @{$sigs{$owner}} = (); # } if ($type eq "sig" and $id ne $owner and $name ne '[User id not found]') { push (@{$sigs{$owner}},$id); push (@{$signedby{$id}},$owner); push (@names,$id,$owner); } } else { print STDERR "Skipping: $line"; } } print "digraph \"debian-keyring\" {\noverlap=scale\nsplines=true\nsep=.1\n"; undef %saw; @saw{@names} = (); @names = keys %saw; undef %saw; for $owner (sort {$sigs{$a} <=> $sigs{$b}} keys %sigs) { undef %saw; @saw{@{$sigs{$owner}}} = (); @{$sigs{$owner}} = keys %saw; undef %saw; undef %saw; @saw{@{$signedby{$owner}}} = (); @{$signedby{$owner}} = keys %saw; undef %saw; $sigcount{$owner} = scalar(@{$sigs{$owner}}); if ($sigcount{$owner} > $maxsigcount) { $maxsigcount = $sigcount{$owner}; } $signedbycount{$owner} = scalar(@{$signedby{$owner}}); if ($signedbycount{$owner} > $maxsignedbycount) { $maxsignedbycount = $signedbycount{$owner}; } if ($signedbycount{$owner} / $sigcount{$owner} > $maxratio) { $maxratio = $signedbycount{$owner} / $sigcount{$owner}; } } print "//$maxratio\n"; open (STATS,">stats.html"); print STATS "\nKeyring Statistics\n"; for $owner (sort {$sigcount{$b} <=> $sigcount{$a}} keys %sigs) { print STATS "
$name{$owner}$sigcount{$owner}\"".\n"; } print STATS "
\n"; close STATS; print "node [style=filled]\n"; for $id (@names) { if ($color) { $red = $sigcount{$id} / $maxsigcount; #$green = .25; if ($sigcount{$id}) { $green = ($signedbycount{$id} / $sigcount{$id} / $maxratio * .75) * 2/3 + 1/3; } else { $green = 1/3; } $blue = ($signedbycount{$id} / $maxsignedbycount) * 2/3 + 1/3; ($hue,$saturation,$value) = rgb2hsv($red,$green,$blue); #print "//$red,$green,$blue\n"; print "//$sigcount{$id} $signedbycount{$id} $red,$green,$blue\n"; print "\"$id\" [fillcolor=\"$hue,$saturation,$value\",label=\"$name{$id}\"]\n"; } else { print "\"$id\" [label=\"$name{$id}\"]\n"; } } #print "node [style=solid]\n"; for $owner (sort keys %sigs) { print "{ "; for $id (@{$sigs{$owner}}) { print "\"$id\" "; } print "} -> \"$owner\"\n"; } print "}\n"; # Converts rgb to hsv. All numbers are within range 0 to 1 # from http://twiki.org/cgi-bin/view/Codev/WebMap sub rgb2hsv { my ($r, $g ,$b) = @_; my $max = maxof($r, maxof($g, $b)); my $min = minof($r, minof($g, $b)); $v = $max; if ($max > 0.0) { $s = ($max - $min) / $max; } else { $s = 0; } if ($s > 0.0) { my ($rc, $gc, $bc, $diff); $diff = $max - $min; $rc = ($max - $r) / $diff; $gc = ($max - $g) / $diff; $bc = ($max - $b) / $diff; if ($r == $max) { $h = ($bc - $gc) / 6.0; } elsif ($g == $max) { $h = (2.0 + $rc - $bc) / 6.0; } else { $h = (4.0 + $gc - $rc) / 6.0; } } else { $h = 0.0; } if ($h < 0.0) { $h += 1.0; } return ($h, $s, $v); } sub maxof { my ($a, $b) = @_; return $a>$b?$a:$b; } sub minof { my ($a, $b) = @_; return $a<$b?$a:$b; }