File Coverage

blib/lib/Crypt/OdinAuth.pm
Criterion Covered Total %
statement 46 46 100.0
branch 9 10 90.0
condition n/a
subroutine 11 11 100.0
pod 3 3 100.0
total 69 70 98.5


line stmt bran cond sub pod time code
1             package Crypt::OdinAuth;
2              
3 3     3   40414 use 5.006;
  3         11  
  3         93  
4 3     3   14 use strict;
  3         3  
  3         71  
5 3     3   11 use warnings;
  3         13  
  3         101  
6              
7             =head1 NAME
8              
9             Crypt::OdinAuth - Cryptographic calculations for the OdinAuth SSO system
10              
11             =head1 VERSION
12              
13             Version 0.2.1
14              
15             =cut
16              
17             our $VERSION = '0.2.1';
18              
19 3     3   3676 use Digest;
  3         1613  
  3         93  
20 3     3   1560 use Digest::HMAC;
  3         1572  
  3         159  
21 3     3   1505 use Digest::SHA256;
  3         10451  
  3         164  
22 3     3   1106 use MIME::Base64 qw(encode_base64url decode_base64url);
  3         1351  
  3         241  
23              
24 3     3   25 use constant OLD_COOKIE => 24*60*60; # cookie older than 24h is discarded
  3         4  
  3         1554  
25              
26             =head1 SYNOPSIS
27              
28             This module exports functions for calculating and verifying signed
29             cookies for OdinAuth SSO Apache handler.
30              
31             use Crypt::OdinAuth;
32              
33             Crypt::OdinAuth::hmac_for('secret', 'login_name', 'role1,role2,role3', 1337357387, 'netcat')
34             #=> '349b7135f43bd4c0111564960e7d9d583dde0c5c'
35              
36             Crypt::OdinAuth::cookie_for('secret', 'login_name', 'role1,role2,role3', 'netcat')
37             #=> 'login_name-role1,role2,role3-1337357638-7ec415a6816c8e9dab7b788e1262769ef80af7d8'
38              
39             =head1 SUBROUTINES
40              
41             =head2 hmac_for(secret, user, roles, timestamp, user_agent)
42              
43             =cut
44             sub hmac_for ($$$$$) {
45 16     16 1 541 my ( $secret, $user, $roles, $ts, $ua ) = @_;
46 16         43 my $hmac = Digest::HMAC->new($secret, Digest->new("SHA-256"));
47              
48 16         5276 $hmac->add(join(',',
49             encode_base64url($user),
50             encode_base64url($roles),
51             $ts,
52             encode_base64url($ua)));
53 16         350 return $hmac->hexdigest;
54             }
55              
56             =head2 cookie_for(secret, user, roles, user_agent)
57              
58             =cut
59              
60             sub cookie_for {
61 8     8 1 2805 my ( $secret, $user, $roles, $ua, $ts ) = @_;
62 8 100       24 $ts = time() unless defined($ts);
63              
64 8         17 my $hmac = hmac_for($secret, $user, $roles, $ts, $ua);
65 8         183 return join(',',
66             encode_base64url($user),
67             encode_base64url($roles),
68             $ts,
69             $hmac);
70             }
71              
72             =head2 check_cookie(secret, cookie, user_agent)
73              
74             =cut
75              
76             sub check_cookie ($$$) {
77 6     6 1 125 my ( $secret, $cookie, $ua ) = @_;
78              
79 6 50       37 $cookie =~ /^\s*([a-zA-Z0-9_-]+),([a-zA-Z0-9_-]+),(\d+),([0-9a-f]+)\s*$/
80             or die "Wrong cookie format\n";
81              
82 6         14 my $user = decode_base64url($1);
83 6         57 my $roles = decode_base64url($2);
84 6         34 my $ts = $3;
85 6         11 my $hmac = $4;
86              
87             # Use double hmac to prevent timing attacks
88             # http://codahale.com/a-lesson-in-timing-attacks/
89             # http://www.isecpartners.com/blog/2011/2/18/double-hmac-verification.html
90 6         15 my $hmac_received = Digest::HMAC->new($secret, Digest->new("SHA-256"));
91 6         257 my $hmac_calculated = Digest::HMAC->new($secret, Digest->new("SHA-256"));
92              
93 6         218 $hmac_received->add($hmac);
94 6         28 $hmac_calculated->add(hmac_for($secret, $user, $roles, $ts, $ua));
95              
96 6 100       130 die "Invalid signature\n"
97             if ( $hmac_received->digest ne $hmac_calculated->digest );
98              
99 4 100       147 die "Cookie is old\n"
100             if ( $ts < time() - OLD_COOKIE );
101              
102 3 100       11 die "Cookie is in future\n"
103             if ( $ts > time() + 5*60 );
104              
105 2         7 return $user, $roles;
106             }
107              
108             =head1 AUTHOR
109              
110             Maciej Pasternacki, C<< >>
111              
112             =head1 BUGS
113              
114             Please report any bugs or feature requests to C, or through
115             the web interface at L. I will be notified, and then you'll
116             automatically be notified of progress on your bug as I make changes.
117              
118              
119              
120              
121             =head1 SUPPORT
122              
123             You can find documentation for this module with the perldoc command.
124              
125             perldoc Crypt::OdinAuth
126              
127              
128             You can also look for information at:
129              
130             =over 4
131              
132             =item * RT: CPAN's request tracker (report bugs here)
133              
134             L
135              
136             =item * AnnoCPAN: Annotated CPAN documentation
137              
138             L
139              
140             =item * CPAN Ratings
141              
142             L
143              
144             =item * Search CPAN
145              
146             L
147              
148             =back
149              
150              
151             =head1 ACKNOWLEDGEMENTS
152              
153              
154             =head1 LICENSE AND COPYRIGHT
155              
156             Copyright 2012 Maciej Pasternacki.
157              
158             This program is free software; you can redistribute it and/or modify it
159             under the terms of either: the GNU General Public License as published
160             by the Free Software Foundation; or the Artistic License.
161              
162             See http://dev.perl.org/licenses/ for more information.
163              
164              
165             =cut
166              
167             1; # End of Crypt::OdinAuth