| Authen-TypeKey documentation | Contained in the Authen-TypeKey distribution. |
Authen::TypeKey - TypeKey authentication verification
use CGI;
use Authen::TypeKey;
my $q = CGI->new;
my $tk = Authen::TypeKey->new;
$tk->token('typekey-token');
my $res = $tk->verify($q) or die $tk->errstr;
Authen::TypeKey is an implementation of verification for signatures generated by TypeKey authentication. For information on the TypeKey protocol and using TypeKey in other applications, see http://www.sixapart.com/typekey/api.
Create a new Authen::TypeKey object.
Your TypeKey token, which you passed to TypeKey when creating the original sign-in link. This is required to successfully validate the signature in TypeKey 1.1 and higher, which includes the token in the plaintext.
This must be set before calling verify.
Verify a TypeKey signature based on the other parameters given. The signature and other parameters are found in the $query object, which should be either a hash reference, or any object that supports a param method--for example, a CGI or Apache::Request object.
If the signature is successfully verified, verify returns a reference to a hash containing the following values.
The unique username of the TypeKey user.
The user's display name.
The user's email address. If the user has chosen not to pass his/her
email address, this will contain the SHA-1 hash of the string
mailto:<email>.
The timestamp at which the signature was generated, expressed as seconds since the epoch.
If verification is unsuccessful, verify will return undef, and the
error message can be found in $tk->errstr.
Provide a caching mechanism for the TypeKey public key.
If $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 Authen::TypeKey object, and the URI of the key. It should return a hash reference with the p, g, q, and pub_key keys set to Math::BigInt objects representing the pieces of the DSA public key.
Otherwise, $cache should be the path to a local file where the public key will be cached/mirrored.
If $cache is not set, the key is not cached. By default, no caching occurs.
Get/set a value indicating whether verify should check the expiration date and time in the TypeKey parameters. The default is to check the expiration date and time.
Get/set the amount of time at which a TypeKey signature is intended to expire. The default value is 600 seconds, i.e. 10 minutes.
Get/set the URL from which the TypeKey public key can be obtained. The default URL is http://www.typekey.com/extras/regkeys.txt.
Get/set the LWP::UserAgent-like object which will be used to retrieve the
regkeys from the network. Needs to support mirror and get methods.
By default, LWP::UserAgent is used, and this method as a getter returns
undef unless the user agent has been previously set.
Get/set the version of the TypeKey protocol to use. The default version
is 1.1.
Authen::TypeKey is free software; you may redistribute it and/or modify it under the same terms as Perl itself.
Except where otherwise noted, Authen::TypeKey is Copyright 2004 Six Apart Ltd, cpan@sixapart.com. All rights reserved.
| Authen-TypeKey documentation | Contained in the Authen-TypeKey distribution. |
# $Id: TypeKey.pm 1915 2006-02-06 06:26:33Z btrott $ package Authen::TypeKey; use strict; use base qw( Class::ErrorHandler ); use Math::BigInt lib => 'GMP,Pari'; use MIME::Base64 qw( decode_base64 ); use Digest::SHA1 qw( sha1 ); use LWP::UserAgent; use HTTP::Status qw( RC_NOT_MODIFIED ); our $VERSION = '0.05'; sub new { my $class = shift; my $tk = bless { }, $class; $tk->skip_expiry_check(0); $tk->expires(600); $tk->key_url('http://www.typekey.com/extras/regkeys.txt'); $tk->version(1.1); $tk->token(''); $tk; } sub _var { my $tk = shift; my $var = shift; $tk->{$var} = shift if @_; $tk->{$var}; } sub key_cache { shift->_var('key_cache', @_) } sub skip_expiry_check { shift->_var('skip_expiry_check', @_) } sub expires { shift->_var('expires', @_) } sub key_url { shift->_var('key_url', @_) } sub token { shift->_var('token', @_) } sub version { shift->_var('version', @_) } sub ua { shift->_var('ua', @_) } sub verify { my $tk = shift; my($email, $username, $name, $ts, $sig); if (@_ == 1) { my $q = $_[0]; if (ref $q eq 'HASH') { ($email, $username, $name, $ts, $sig) = map $_[0]->{$_}, qw( email name nick ts sig ); } else { ($email, $username, $name, $ts, $sig) = map $q->param($_), qw( email name nick ts sig ); } } else { ## Later we could process arguments passed in a hash. return $tk->error("usage: verify(\$query)"); } for ($email, $sig) { tr/ /+/; } return $tk->error("TypeKey data has expired") unless $tk->skip_expiry_check || $ts + $tk->expires >= time; my $key = $tk->_fetch_key($tk->key_url) or return; my($r, $s) = split /:/, $sig; return $tk->error("Invalid signature $sig") unless $r && $s; $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 '::', $email, $username, $name, $ts, $tk->version >= 1.1 ? ($tk->token) : (); unless ($tk->_verify($msg, $key, $sig)) { return $tk->error("TypeKey signature verification failed"); } { name => $username, nick => $name, email => $email, ts => $ts }; } sub _verify { my $tk = 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 _fetch_key { my $tk = shift; my($uri) = @_; my $cache = $tk->key_cache; ## If it's a callback, call it and return the return value. return $cache->($tk, $uri) if $cache && ref($cache) eq 'CODE'; ## Otherwise, load the key. my $data; my $ua = $tk->ua || LWP::UserAgent->new; if ($cache) { my $res = $ua->mirror($uri, $cache); return $tk->error("Failed to fetch key: " . $res->status_line) unless $res->is_success || $res->code == RC_NOT_MODIFIED; open my $fh, $cache or return $tk->error("Can't open $cache: $!"); $data = do { local $/; <$fh> }; close $fh; } else { my $res = $ua->get($uri); return $tk->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; } 1; __END__