/usr/local/CPAN/Net-OpenID-JanRain/Net/OpenID/JanRain/Association.pm
package Net::OpenID::JanRain::Association;
# for equality testing. Below and above must be the same
my $CLASSNAME = "Net::OpenID::JanRain::Association";
# vi:ts=4:sw=4
use warnings;
use strict;
use Carp;
use Net::OpenID::JanRain::Util qw( kvToHash hashToPairs pairsToKV fromBase64 toBase64);
use Net::OpenID::JanRain::CryptUtil qw( hmacSha1 );
# This is a HMAC-SHA1 specific value.
my $SIG_LENGTH = 20;
# The ordering and name of keys as stored by serialize
# Note that if you change this you must change sub serialize below
my @assoc_keys = (
'version',
'handle',
'secret',
'issued',
'lifetime',
'assoc_type',
);
########################################################################
# fromExpiresIn
# This is an alternate constructor used by the OpenID consumer
# library to create associations. OpenIDStore implementations
# shouldn't use this constructor.
# Like new, but uses the current time as the issue date
sub fromExpiresIn {
my $caller = shift;
my ($expires_in, $handle, $secret, $assoc_type) = @_;
my $class = ref($caller) || $caller;
my $issued = time;
my $self = {handle => $handle,
secret => $secret,
issued => $issued,
lifetime => $expires_in,
assoc_type => $assoc_type};
bless($self, $class);
} # end fromExpiresIn
########################################################################
# new
# Create a new association object with the given:
# handle: an identifying string, provided by the server
# secret: the shared secret, a string provided by the server
# issued: the time when the association was issued, in seconds since the epoch
# lifetime: after this many seconds since issued, association is invalid
# assoc_type: the 'type' of association. currently only 'HMAC-SHA1' is valid.
sub new {
my $caller = shift;
my ($handle, $secret, $issued, $lifetime, $assoc_type) = @_;
my $class = ref($caller) || $caller;
my $self = {handle => $handle,
secret => $secret,
issued => $issued,
lifetime => $lifetime,
assoc_type => $assoc_type};
bless($self, $class);
} # end new
########################################################################
# expiresIn
# if we are expired return 0, otherwise the number of seconds we have left
sub expiresIn {
my $self = shift;
my $timeleft = $self->{issued} + $self->{lifetime} - time;
return 0 if $timeleft < 0;
return $timeleft;
} # end expiresIn
########################################################################
# equals
# Check to see if we are the same association as another object
sub equals {
my $self = shift;
my ($other) = @_;
return ($other->isa($CLASSNAME)
and $self->{handle} eq $other->{handle}
and $self->{secret} eq $other->{secret}
and $self->{issued} eq $other->{issued}
and $self->{lifetime} eq $other->{lifetime}
and $self->{assoc_type} eq $other->{assoc_type});
} # end equals
########################################################################
# serialize
# return a newline separated key:value string containing the object data
# XXX: If the contents of the association object change, this function
# must also change. Possibly use hashToPairs and pairsToKV instead
sub serialize {
my $self = shift;
my $enc_secret = toBase64($self->{secret});
my $assoc_s = "version:2\n".
"handle:$self->{handle}\n".
"secret:$enc_secret\n".
"issued:$self->{issued}\n".
"lifetime:$self->{lifetime}\n".
"assoc_type:$self->{assoc_type}\n";
return $assoc_s;
} # end serialize
########################################################################
# deserialize
# This is a constructor that builds an association object from
# a newline separated key:value string
sub deserialize {
my $caller = shift;
my ($assoc_s) = @_;
my $class = ref($caller) || $caller;
my $assoc = kvToHash($assoc_s);
my $key;
#check for validity
foreach $key (@assoc_keys) {
return undef unless $assoc->{$key};
}
return undef if $assoc->{'version'} != 2;
$assoc->{secret} = fromBase64($assoc->{secret});
bless($assoc, $class);
} # end deserialize
########################################################################
# sign
# Sign a list of key,value pairs with our association key.
# takes a list of pairs, returns a string.
sub signPairs {
my $self = shift;
my ($pairs) = @_;
carp "Association type '$self->{assoc_type}' cannot sign."
unless $self->{assoc_type} eq 'HMAC-SHA1';
my $kv = pairsToKV($pairs);
carp "Got no kvform to sign" unless $kv;
return hmacSha1($self->{secret}, $kv);
}
# Sign stuff with our association key
# 1st arg is a hash ref, i.e. an http query
# 2nd arg is a list containing keys of the hash. Sign the values in the hash
# at these keys: convert to a (k:v\n)+ string and sign the string with the
# association secret.
sub signHash {
my $self = shift;
my $args = shift;
my $sign_list = shift;
my $prefix = shift;
return toBase64($self->signPairs(hashToPairs($args, $sign_list, $prefix)));
}
sub addSignature {
my $self = shift;
my $args = shift;
my $sign_list = shift;
my $prefix = shift || '';
#args must be a hash ref
$args->{$prefix.'signed'} = join (',', @$sign_list);
$args->{$prefix.'sig'} = $self->signHash($args, $sign_list, $prefix);
return $args;
}
sub handle {
my $self = shift;
return $self->{handle};
}
sub assoc_type {
my $self = shift;
return $self->{assoc_type};
}
sub secret {
my $self = shift;
return $self->{secret};
}
1;