/usr/share/perl5/Authen/Bitcard.pm is in libauthen-bitcard-perl 0.90-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 | package Authen::Bitcard;
BEGIN {
$Authen::Bitcard::VERSION = '0.90';
}
use strict;
use base qw( Class::ErrorHandler );
use Math::BigInt;
use MIME::Base64 qw( decode_base64 );
use Digest::SHA qw( sha1 sha1_hex );
use LWP::UserAgent;
use HTTP::Status qw( RC_NOT_MODIFIED );
use URI;
use URI::QueryParam;
use Carp qw(croak);
use JSON qw(decode_json);
sub new {
my $class = shift;
my $bc = bless { }, $class;
$bc->skip_expiry_check(0);
$bc->expires(600);
$bc->bitcard_url('https://www.bitcard.org/');
$bc->version(4);
$bc->token('');
my %args = @_;
for my $k (keys %args) {
next unless $bc->can($k);
$bc->$k($args{$k});
}
$bc;
}
sub _var {
my $bc = shift;
my $var = shift;
$bc->{$var} = shift if @_;
$bc->{$var};
}
sub key_cache { shift->_var('key_cache', @_) }
sub skip_expiry_check { shift->_var('skip_expiry_check', @_) }
sub expires { shift->_var('expires', @_) }
sub token { shift->_var('token', @_) }
sub api_secret { shift->_var('api_secret', @_) }
sub version { shift->_var('version', @_) }
sub ua { shift->_var('ua', @_) }
sub bitcard_url { shift->_var('bitcard_url', @_) }
sub info_optional { shift->_var('io', @_) }
sub info_required { shift->_var('ir', @_) }
sub _url {
my ($bc, $url) = (shift, shift);
my $args = ($_[0] && ref $_[0]) ? $_[0] : { @_ };
$args->{"bc_$_"} = delete $args->{$_} for keys %$args;
$args->{bc_t} = $bc->token;
$args->{bc_v} = $bc->version;
$args->{bc_io} = ref $bc->info_optional ? join ",", @{$bc->info_optional} : $bc->info_optional;
$args->{bc_ir} = ref $bc->info_required ? join ",", @{$bc->info_required} : $bc->info_required;
delete $args->{bc_io} unless $args->{bc_io};
delete $args->{bc_ir} unless $args->{bc_ir};
my $base = $bc->bitcard_url;
$base = "$base/" unless $base =~ m!/$!;
my $uri = URI->new($base . $url);
unless ($url =~ m/regkey.txt/) {
if ($url =~ m!^api/!) {
croak "Bitcard API Secret required for API calls" unless $bc->api_secret;
$args->{bc_ts} = time;
my @fields = sort keys %$args;
$args->{bc_fields} = join ",", @fields, 'bc_fields';
my $string = join "::", (map { "$args->{$_}" } @fields, 'bc_fields'), $bc->api_secret;
warn "ST: $string";
$args->{bc_sig} = sha1_hex($string);
}
$uri->query_form_hash($args);
}
$uri->as_string;
}
sub key_url{
shift->_url("regkey.txt");
}
sub login_url {
shift->_url('login', @_)
}
sub logout_url {
shift->_url('logout', @_)
}
sub account_url {
shift->_url('account', @_)
}
sub register_url {
shift->_url('register', @_)
}
sub _api_url {
my ($self, $method) = (shift, shift);
$self->_url("api/$method", @_);
}
sub verify {
my $bc = shift;
my %data;
my $fields;
if (@_ == 1) {
my $q = $_[0];
if (ref $q eq 'HASH') {
$fields = $_[0]->{bc_fields} || '';
%data = map { $_ => $_[0]->{$_} } grep { defined $_[0]->{$_} } split(/,/, $fields), 'bc_sig';
}
else {
$fields = $q->param('bc_fields') || '';
%data = map { $_ => $q->param($_) } grep { defined $q->param($_) } split(/,/, $fields), 'bc_sig';
}
}
else {
## Later we could process arguments passed in a hash.
return $bc->error("usage: verify(\$query)");
}
#warn Data::Dumper->Dump([\%data], [qw(data)]);
for ($data{bc_email}, $data{bc_sig}) {
defined $_ and tr/ /+/;
}
return $bc->error("Bitcard data has expired")
unless $bc->skip_expiry_check or ($data{bc_ts}||0) + $bc->expires >= time;
my $key = $bc->_fetch_key($bc->key_url) or return;
my($r, $s) = split /:/, $data{bc_sig};
my $sig = {};
$sig->{r} = Math::BigInt->new("0b" . unpack("B*", decode_base64($r)));
$sig->{s} = Math::BigInt->new("0b" . unpack("B*", decode_base64($s)));
my $msg = join '::', (map { $data{$_} || '' } split /,/, $data{bc_fields} ), $bc->token;
unless ($bc->_verify($msg, $key, $sig)) {
return $bc->error("Bitcard signature verification failed");
}
for my $k (keys %data) {
my $nk = $k;
$nk =~ s/^bc_//;
$data{$nk} = delete $data{$k};
}
if ($bc->version >= 4) {
unless ($data{version} == $bc->version) {
$data{version} =~ s/\D//g;
return $bc->error(sprintf "Expected Bitcard protocol version [%i], got version [%i].", $bc->version, $data{version});
}
unless ($data{confirmed}) {
return $bc->error('Account not confirmed');
}
}
\%data;
}
sub _verify {
my $bc = shift;
my($msg, $key, $sig) = @_;
my $u1 = Math::BigInt->new("0b" . unpack("B*", sha1($msg)));
$sig->{s}->bmodinv($key->{q});
$u1 = ($u1 * $sig->{s}) % $key->{q};
$sig->{s} = ($sig->{r} * $sig->{s}) % $key->{q};
$key->{g}->bmodpow($u1, $key->{p});
$key->{pub_key}->bmodpow($sig->{s}, $key->{p});
$u1 = ($key->{g} * $key->{pub_key}) % $key->{p};
$u1 %= $key->{q};
$u1 == $sig->{r};
}
sub _get_ua {
shift->ua || LWP::UserAgent->new;
}
sub _fetch_key {
my $bc = shift;
my($uri) = @_;
my $cache = $bc->key_cache;
## If it's a callback, call it and return the return value.
return $cache->($bc, $uri) if $cache && ref($cache) eq 'CODE';
## Otherwise, load the key.
my $data;
my $ua = $bc->_get_ua;
if ($cache) {
my $res = $ua->mirror($uri, $cache);
return $bc->error("Failed to fetch key: " . $res->status_line)
unless $res->is_success || $res->code == RC_NOT_MODIFIED;
open my $fh, $cache
or return $bc->error("Can't open $cache: $!");
$data = do { local $/; <$fh> };
close $fh;
} else {
my $res = $ua->get($uri);
return $bc->error("Failed to fetch key: " . $res->status_line)
unless $res->is_success;
$data = $res->content;
}
chomp $data;
my $key = {};
for my $f (split /\s+/, $data) {
my($k, $v) = split /=/, $f, 2;
$key->{$k} = Math::BigInt->new($v);
}
$key;
}
sub add_invite {
my $self = shift;
my $url = $self->_api_url('invite/add_invite', @_);
warn "URL: $url\n";
my $res = $self->_get_ua->get($url);
return $self->error("Failed to retrive invitation code: " . $res->status_line)
unless $res->is_success;
my $data = decode_json($res->content);
$data;
}
1;
__END__
=head1 NAME
Authen::Bitcard - Bitcard authentication verification
=head1 SYNOPSIS
use CGI;
use Authen::Bitcard;
my $q = CGI->new;
my $bc = Authen::Bitcard->new;
$bc->token('bitcard-token');
# send user to $bc->login_url(r => $return_url);
# when the user comes back, get the user id with:
my $user = $bc->verify($q) or die $bc->errstr;
=head1 DESCRIPTION
I<Authen::Bitcard> is an implementation of verification for signatures
generated by Bitcard authentication. For information on the Bitcard
protocol and using Bitcard in other applications, see
I<http://www.bitcard.org/api>.
The module and the protocol are heavily based on I<Authen::Typekey>.
(In fact, the Bitcard authentication server also supports the TypeKey
API!)
=head1 USAGE
=head2 Authen::Bitcard->new
Create a new I<Authen::Bitcard> object.
=head2 $bc->token([ $bitcard_token ])
Your Bitcard token, which you passed to Bitcard when creating the original
sign-in link.
This must be set B<before> calling I<verify> or I<login_url> (etc).
=head2 $bc->bitcard_url( [ $url ])
Get/set the base URL for the Bitcard service. The default URL is
I<https://www.bitcard.org/>. The other *_url methods are build based
on the C<bitcard_url> value.
=head2 $bc->login_url( r => $return_url )
Returns the URL for the user to login. Takes a hash or hash ref with
extra parameters to put in the URL. One of them must be the C<r>
parameter with the URL the user will get returned to after logging in
(or canceling the login).
=head2 $bc->logout_url( r => $return_url )
Returns the URL you can send the user if they wish to logout. Also
needs the C<r> parameter for the URL the Bitcard server should send
the user back to after logging out.
=head2 $bc->account_url( r => $return_url )
Returns the URL the user can edit his Bitcard account information at.
Also needs the C<r> parameter like C<login_url> and C<logout_url>.
=head2 $bc->register_url( r => $return_url )
Returns the URL for a user to register a new Bitcard account. Also
needs the C<r> parameter as above.
=head2 $bc->key_url()
Get the URL from which the Bitcard public key can be obtained.
=head2 $bc->info_required( $string | [ array ref ] )
With info_required you specify what user data you require. The
possible fields are "username", "name" and "email" (see C<verify> for
more information).
The method takes either a comma separated string or a reference to an
array.
This must be called before C<login_url>.
NOTE: "name" is currently not implemented well in the Bitcard server,
so we recommend you require "username", but mark "name" as optional if
you want the "display name" of the user returned.
=head2 $bc->info_optional( $string | [ array ref ] )
As C<info_required> except the Bitcard server will ask the user to
allow the information to be forwarded, but not require it to proceed.
The Bitcard server will always have a confirmed email address on file
before letting a user login.
=head2 $bc->verify($query)
Verify a Bitcard signature based on the other parameters given. The signature
and other parameters are found in the I<$query> object, which should be
either a hash reference, or any object that supports a I<param> method--for
example, a I<CGI> or I<Apache::Request> object.
If the signature is successfully verified, I<verify> returns a reference to
a hash containing the following values.
=over 4
=item * id
The unique user id of the Bitcard user on your site. It's a 128bit
number as a 40 byte hex value.
The id is always returned when the verification was successful (all
other user data fields are optional, see C<info_required> and
C<info_optional>).
=item * username
The unique username of the Bitcard user.
=item * name
The user's display name.
=item * email
The user's email address.
=item * ts
The timestamp at which the signature was generated, expressed as seconds
since the epoch.
=back
If verification is unsuccessful, I<verify> will return C<undef>, and the
error message can be found in C<$bc-E<gt>errstr>.
=head2 $bc->key_cache([ $cache ])
Provide a caching mechanism for the public key.
If I<$cache> is a CODE reference, it is treated as a callback that should
return the public key. The callback will be passed two arguments: the
I<Authen::TypeKey> object, and the URI of the key. It should return a
hash reference with the I<p>, I<g>, I<q>, and I<pub_key> keys set to
I<Math::BigInt> objects representing the pieces of the DSA public key.
Otherwise, I<$cache> should be the path to a local file where the public
key will be cached/mirrored.
If I<$cache> is not set, the key is not cached. By default, no caching
occurs.
=head2 $bc->skip_expiry_check([ $boolean ])
Get/set a value indicating whether I<verify> should check the expiration
date and time in the TypeKey parameters. The default is to check the
expiration date and time.
=head2 $bc->expires([ $secs ])
Get/set the amount of time at which a Bitcard signature is intended to expire.
The default value is 600 seconds, i.e. 10 minutes.
=head2 $bc->ua([ $user_agent ])
Get/set the LWP::UserAgent-like object which will be used to retrieve the
regkeys from the network. Needs to support I<mirror> and I<get> methods.
By default, LWP::UserAgent is used, and this method as a getter returns
C<undef> unless the user agent has been previously set.
=head2 $bc->version([ $version ])
Get/set the version of the Bitcard protocol to use. The default version
is C<3>.
=head2 $bc->api_secret( $secret )
Get/set the api_secret (needed for some API calls, add_invite for
example).
=head2 $bc->add_invite
Returns a hashref with C<invite_url> and C<invite_key>. Can be used
for "invitation only" sites where you have to login before you can
access the site.
=head1 LICENSE
I<Authen::Bitcard> is distributed under the Apache License; see the
LICENSE file in the distribution for details.
=head1 AUTHOR & COPYRIGHT
Except where otherwise noted, I<Authen::Bitcard> is Copyright
2004-2010 Develooper LLC, ask@develooper.com.
Parts are Copyright 2004 Six Apart Ltd, cpan@sixapart.com.
All rights reserved.
=cut
|