File Coverage

blib/lib/Authen/TypeKey/Sign.pm
Criterion Covered Total %
statement 6 6 100.0
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 8 8 100.0


line stmt bran cond sub pod time code
1             # Copyright (c) 2004 Timothy Appnel (tima@cpan.org)
2             # http://www.timaoutloud.org/
3             # This code is released under the Artistic License.
4              
5             package Authen::TypeKey::Sign;
6 1     1   861 use strict;
  1         2  
  1         41  
7 1     1   4 use base qw( Class::ErrorHandler );
  1         2  
  1         2994  
8              
9             use Crypt::DSA;
10             use Crypt::DSA::Key;
11             use Crypt::DSA::Signature;
12             use MIME::Base64 qw( encode_base64 );
13             use Math::Pari;
14              
15             use vars qw( $VERSION );
16             $VERSION = '0.07';
17              
18             sub new {
19             my $class = shift;
20             my $tk = bless {}, $class;
21             $tk->hide_email(1);
22             $tk->version(1.1);
23             $tk->token('');
24             $tk;
25             }
26              
27             sub hide_email { shift->stash('hide_email', @_) }
28             sub version { shift->stash('version', @_) }
29             sub token { shift->stash('token', @_) }
30              
31             sub key {
32             my ($tk, $in) = @_;
33             return $tk->stash('key') if $tk->stash('key');
34             my $key;
35             unless (ref($in)) { # read from file
36             open my $fh, $in
37             or return $tk->error("Can't open $in: $!");
38             my $data = do { local $/; <$fh> };
39             close $fh;
40             $key = Crypt::DSA::Key->new;
41             for my $f (split /\s+/, $data) {
42             my ($k, $v) = split /=/, $f, 2;
43             $key->$k($v);
44             }
45             } else {
46             if (ref($in) eq 'HASH') { # from hash
47             $key = Crypt::DSA::Key->new();
48             map { $key->$_($in->{$_}) } keys %$in;
49             } elsif (ref($key) ne 'Crypt::DSA::Key') {
50             return $tk->error(
51             ref($key) . ' is unsupported by ' . 'the key method.');
52             } else {
53             $key = $in;
54             } # is DSA key
55             }
56             $tk->stash('key', $key);
57             $key;
58             }
59              
60             sub sign {
61             my ($tk, $in) = @_;
62             if (ref($in) ne 'HASH') {
63             return $tk->error(ref($in) . ' cannot param.')
64             unless ($in->can('param'));
65             my %in;
66             map { $in{$_} = $in->param($_) } qw( name nick email );
67             $in = \%in;
68             }
69              
70             # tbd: more validation?
71             $in->{nick} = substr($in->{nick}, 0, 50);
72             $in->{ts} = time;
73             if ($tk->hide_email) {
74             require Digest::SHA1;
75             my $sha1 = Digest::SHA1->new;
76             $sha1->add('mailto:' . $in->{email});
77             $in->{email} = $sha1->hexdigest();
78             }
79             my $msg =
80             $in->{email} . '::' . $in->{name} . '::' . $in->{nick} . '::' . $in->{ts};
81             $msg .= '::' . $tk->token if ($tk->version > 1);
82             my $key = $tk->key;
83             my $dsa = Crypt::DSA->new;
84             my $sig = $dsa->sign(Message => $msg, Key => $key);
85             require MIME::Base64;
86             my $r = MIME::Base64::encode_base64(mp2bin($sig->r()), '');
87             my $s = MIME::Base64::encode_base64(mp2bin($sig->s()), '');
88             $in->{sig} = "$r:$s";
89             my @qs = map { "$_=" . encode_url($in->{$_} || '') } qw( name nick );
90             push(@qs,
91             map { "$_=" . encode_url($in->{$_}) }
92             grep { defined($in->{$_}) } qw( email ts token sig ));
93             join('&', @qs);
94             }
95              
96             #--- utility methods
97              
98             sub stash {
99             $_[0]->{$_[1]} = $_[2] if defined $_[2];
100             $_[0]->{$_[1]};
101             }
102              
103             sub mp2bin {
104             my ($p) = @_;
105             $p = PARI($p);
106             my $base = PARI(1) << PARI(4 * 8);
107             my $res = '';
108             while ($p != 0) {
109             my $r = $p % $base;
110             $p = ($p - $r) / $base;
111             my $buf = pack 'N', $r;
112             if ($p == 0) {
113             $buf =
114             $r >= 16777216 ? $buf
115             : $r >= 65536 ? substr($buf, -3, 3)
116             : $r >= 256 ? substr($buf, -2, 2)
117             : substr($buf, -1, 1);
118             }
119             $res = $buf . $res;
120             }
121             $res;
122             }
123              
124             sub encode_url {
125             (my $str = $_[0]) =~ s!([^a-zA-Z0-9_.-])!uc sprintf "%%%02x", ord($1)!eg;
126             $str;
127             }
128              
129             1;
130              
131             __END__