Skip to content

Instantly share code, notes, and snippets.

@jow-
Last active October 3, 2017 11:41
Show Gist options
  • Save jow-/3f9576545836b4d9a7d8ba38af15d1c4 to your computer and use it in GitHub Desktop.
Save jow-/3f9576545836b4d9a7d8ba38af15d1c4 to your computer and use it in GitHub Desktop.
A Perl script to update the key signature listing of https://lede-project.org/docs/user-guide/signatures
#!/usr/bin/env perl
use strict;
use warnings;
use locale;
use POSIX qw(mktime strftime setlocale LC_COLLATE);
setlocale(LC_COLLATE, "en_US.UTF-8");
my $page = 'https://lede-project.org/docs/user-guide/signatures';
my @keytypes = (
undef,
'RSA',
'RSA, encrypt only',
'RSA, sign only',
undef,
undef,
undef,
undef,
undef,
undef,
undef,
undef,
undef,
undef,
undef,
undef,
'Elgamal, encrypt only',
'DSA',
'EC',
'ECDSA',
'Elgamal'
);
sub format_title {
my ($key) = @_;
if ($key->{is_system_key}) {
return $key->{comment};
}
return sprintf 'Public key of %s', $key->{name};
}
sub format_keytype {
my ($key, $is_subkey) = @_;
my $type = $key->{$is_subkey ? 'stype' : 'type'};
my $size = $key->{$is_subkey ? 'ssize' : 'size'};
my ($d, $m, $y, $s);
if (defined($size) && $size > 0) {
$s = sprintf '%d Bit %s', $size, $keytypes[$type];
}
else {
$s = $keytypes[$type];
}
(undef, undef, undef, $d, $m, $y) =
localtime $key->{$is_subkey ? 'sctime' : 'ctime'};
$s .= sprintf ', created %04d-%02d-%02d', $y + 1900, $m + 1, $d;
(undef, undef, undef, $d, $m, $y) =
localtime $key->{$is_subkey ? 'setime' : 'etime'};
if ($d && $m && $y) {
$s .= sprintf ', expires %04d-%02d-%02d', $y + 1900, $m + 1, $d;
}
return $s;
}
sub format_fingerprint {
my ($key, $is_subkey) = @_;
my $fprint = $key->{$is_subkey ? 'sfprint' : 'fprint'};
my (@fields) = $fprint =~ m!([A-F0-9]{4})!g;
return join(' ', @fields[0..4]) . ' ' . join(' ', @fields[5..9]);
}
sub format_download {
my ($key) = @_;
my $mtime = $key->{ctime};
if (open GIT, '-|', qw(git log -1 --format=%ct --), $key->{filename}) {
if (defined(my $line = readline GIT)) {
chomp $line;
$mtime = $line;
}
close GIT;
}
my $ts = strftime '%F %T %z', gmtime $mtime;
return sprintf
"[[https://git.lede-project.org/?p=keyring.git;a=history;f=%s|Last change: %s]] | " .
"[[https://git.lede-project.org/?p=keyring.git;a=blob_plain;f=%s|Download]]\n" ,
$key->{filename}, $ts, $key->{filename};
}
sub parse_timestamp {
my ($s) = @_;
if ($s =~ m!^(\d\d\d\d)-(\d\d)-(\d\d)$!) {
return mktime(0, 0, 0, $3 + 0, $2 - 1, $1 - 1900);
}
return int $s;
}
my $markup_template = '';
if (open RAW, '-|', 'curl', '-s', "$page?do=export_raw") {
local $/;
$markup_template = readline RAW;
close RAW;
}
my @pubkeys;
if (open KEYS, '-|', qw(find gpg/ -type f -name *.asc -print)) {
while (defined(my $file = readline KEYS)) {
chomp $file;
if (open GPG, '-|', qw(gpg --with-fingerprint --with-fingerprint --with-colons), $file) {
my %data;
while (defined(my $line = readline GPG)) {
chomp $line;
my @fields = split ':', $line;
if ($fields[0] eq 'uid' && !exists $data{name}) {
($data{name}, $data{comment}, $data{email}) =
$fields[9] =~ m!^([^()]+)(?: \((.+?)\))? <(.+)>$!;
}
elsif ($fields[0] eq 'pub') {
$data{size} = int $fields[2];
$data{type} = int $fields[3];
$data{eid} = $fields[4];
$data{ctime} = parse_timestamp($fields[5]);
$data{etime} = $fields[6] ? parse_timestamp($fields[6]) : 0;
if ($fields[9] && !exists $data{name}) {
($data{name}, $data{comment}, $data{email}) =
$fields[9] =~ m!^([^()]+)(?: \((.+?)\))? <(.+)>$!;
}
}
elsif ($fields[0] eq 'sub') {
$data{ssize} = int $fields[2];
$data{stype} = int $fields[3];
$data{seid} = $fields[4];
$data{sctime} = parse_timestamp($fields[5]);
$data{setime} = $fields[6] ? parse_timestamp($fields[6]) : 0;
}
elsif ($fields[0] eq 'fpr') {
$data{exists($data{stype}) ? 'sfprint' : 'fprint'} = $fields[9];
}
}
close GPG;
$data{filename} = $file;
$data{is_system_key} =
(index($data{email}, 'lede-project.org') >= 0) ||
(index($data{email}, 'lists.infradead.org') >= 0);
push @pubkeys, \%data;
}
}
close KEYS;
}
my $gpg_markup = '';
foreach my $key (sort {
!$a->{is_system_key} <=> !$b->{is_system_key} ||
$a->{name} cmp $b->{name}
} @pubkeys) {
$gpg_markup .= sprintf "---\n\n=== %s ===\n\n",
format_title($key);
$gpg_markup .= sprintf "User ID: **%s** <%s>\\\\\n",
$key->{name}, $key->{email};
$gpg_markup .= sprintf "Public Key: 0x%s**%s** (%s)\\\\\n",
substr($key->{eid}, 0, 8), substr($key->{eid}, 8),
format_keytype($key, 0);
$gpg_markup .= sprintf "Fingerprint: ''%%%%%s%%%%''\\\\\n",
format_fingerprint($key, 0);
if (exists $key->{stype}) {
$gpg_markup .= sprintf "Signing Subkey: 0x%s **%s** (%s)\\\\\n",
substr($key->{seid}, 0, 8), substr($key->{seid}, 8),
format_keytype($key, 1);
$gpg_markup .= sprintf "Fingerprint: ''%%%%%s%%%%''\\\\\n",
format_fingerprint($key, 1);
}
$gpg_markup .= sprintf "%s\n", format_download($key);
}
my @usignkeys;
if (open KEYS, '-|', qw(find usign/ -type f -name *[0-9a-f] -print)) {
while (defined(my $file = readline KEYS)) {
chomp $file;
if (open USIGN, '<', $file) {
my %data;
while (defined(my $line = readline USIGN)) {
chomp $line;
if ($line =~ m!^untrusted comment: (.+)$!) {
$data{comment} = $1;
}
else {
$data{key} = $line;
}
}
close USIGN;
$file =~ m!/([0-9a-f]{16})$!;
$data{id} = $1;
$data{filename} = $file;
push @usignkeys, \%data;
}
}
close KEYS;
}
my $usign_markup = '';
foreach my $key (sort { $a->{comment} cmp $b->{comment} } @usignkeys) {
$usign_markup .= sprintf "---\n\n=== %s ===\n\n",
$key->{comment};
$usign_markup .= sprintf " * Key-ID: ''%%%%%s%%%%''\n",
$key->{id};
$usign_markup .= sprintf " * Key-Data: ''%%%%%s%%%%''\n\n",
$key->{key};
$usign_markup .= sprintf "%s\n",
format_download($key);
}
$markup_template =~ s!
( /\*\sBEGIN\sGPG\sKEYS\s\*/ )
.+
( /\*\sEND\sGPG\sKEYS\s\*/ )
!
$1 . "\n\n" . $gpg_markup . $2;
!esx;
$markup_template =~ s!
( /\*\sBEGIN\sUSIGN\sKEYS\s\*/ )
.+
( /\*\sEND\sUSIGN\sKEYS\s\*/ )
!
$1 . "\n\n" . $usign_markup . $2;
!esx;
print $markup_template;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment