App-FuguWeb

 view release on metacpan or  search on metacpan

lib/App/FuguWeb/Keys.pm  view on Meta::CPAN

#	the name that every consumer install reads.
#
#	A signify signature holds two lines. The first line starts
#	with 'untrusted comment: ', which signify(1) needs. The second
#	line is the signature body: 100 base64 characters, which
#	decode to 74 bytes whose first two bytes spell Ed.
sub _signature_problem ($bytes)
{
	my @line = split /\n/, $bytes, -1;
	pop @line if @line && $line[-1] eq '';

	unless ( @line == 2 ) {
		return
		      'it holds '
		    . scalar(@line)
		    . ' lines, and a signify signature holds 2';
	}

	unless ( $line[0] =~ /\Auntrusted comment: / ) {
		return 'the first line is no untrusted comment line';
	}

	# 99 characters and one pad always decode to 74 bytes, so the
	# length needs no second test. A key body needs one, because
	# 56 characters carry no pad and decode to 42.
	unless ( $line[1] =~ m{\A[A-Za-z0-9+/]{99}=\z} ) {
		return 'the signature body is not 100 base64 characters';
	}

	my $raw = MIME::Base64::decode_base64( $line[1] );
	unless ( substr( $raw, 0, 2 ) eq 'Ed' ) {
		return 'the signature body names no signify algorithm';
	}

	return;
}

# _addresses(@keys):
#	The Web Key Directory hash of each key, once for each hash, in
#	the order that the keys arrive. Two keys of one address share
#	one published path.
sub _addresses (@keys)
{
	my %seen;

	return grep { !$seen{$_}++ } map { $_->{wkd} } @keys;
}

# $self->_fail($reason):
#	Record the reason and return undef, so each public method
#	fails the same way.
sub _fail ( $self, $reason )
{
	$self->{error} = $reason;

	return;
}

# _index_body($rows, $by_target):
#	The body fragment of the human page: one row for each key, in
#	publication order. Every value is escaped, and a value that
#	the description left out becomes an empty cell.
#
#	The subject and the validity cells hold the two facts of a
#	certificate, per WEB-X509-5, and a key of another type leaves
#	them empty.
#
#	The last cell of a row holds the bindings of that key, per
#	WEB-TRUST-11. Each one names its signer and links its file, so
#	a reader fetches the signature beside the key that it covers.
sub _index_body ( $rows, $by_target )
{
	my @head = (
		'Key',    'Purpose',     'Serial',  'Type',
		'Status', 'Fingerprint', 'Subject', 'Validity',
		'Since',  'Until',       'Bindings'
	);

	my $html = "<h1>Keys</h1>\n<table>\n<thead>\n<tr>";
	$html .= "<th>$_</th>" for @head;
	$html .= "</tr>\n</thead>\n<tbody>\n";

	for my $row (@$rows) {
		my $href = App::FuguWeb::escape_attr( $row->{name} );
		my $stem = App::FuguWeb::escape_html( $row->{stem} );

		$html .= qq{<tr><td><a href="$href">$stem</a></td>};
		$html .= '<td>' . _cell( $row->{$_} ) . '</td>'
		    for qw(purpose serial type status fingerprint
		    subject validity since until);
		$html .= '<td>'
		    . _bindings( $by_target->{ $row->{name} } ) . "</td>";
		$html .= "</tr>\n";
	}

	return $html . "</tbody>\n</table>\n";
}

# _bindings($bindings):
#	The binding cell of one key: one link for each binding, named
#	by the signer of it. A key that no binding covers gives an
#	empty cell, so the row keeps its column count.
sub _bindings ($bindings)
{
	return '' unless $bindings;

	my @link;
	for my $binding ( sort { $a->{name} cmp $b->{name} } @$bindings ) {
		my $href = App::FuguWeb::escape_attr( $binding->{name} );
		my $stem = App::FuguWeb::escape_html(
			$binding->{signer} =~ s/\.[^.]+\z//r );

		push @link, qq{<a href="$href">$stem</a>};
	}

	return join ', ', @link;
}

# _cell($value):
#	One table cell. A value that the description left out becomes
#	an empty cell, so a template tests one thing and the row keeps
#	its column count.
sub _cell ($value)
{
	return defined $value ? App::FuguWeb::escape_html($value) : '';
}

# _digest_of($path):
#	The lowercase hex SHA256 digest of the file, or undef when the
#	file does not open. addfile reads in blocks, so the check
#	never holds a whole key set in memory.
sub _digest_of ($path)
{
	open my $fh, '<', $path or return;
	binmode $fh;

	my $sha = Digest::SHA->new(256);
	$sha->addfile($fh);
	close $fh;

	return lc $sha->hexdigest;
}

1;



( run in 2.502 seconds using v1.01-cache-2.11-cpan-54e63673c56 )