line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
2
|
|
|
2
|
|
842
|
use warnings; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
56
|
|
2
|
2
|
|
|
2
|
|
7
|
use strict; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
64
|
|
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package Net::OAuth2::Scheme::Mixin::HMac; |
5
|
|
|
|
|
|
|
BEGIN { |
6
|
2
|
|
|
2
|
|
27
|
$Net::OAuth2::Scheme::Mixin::HMac::VERSION = '0.020002_099'; |
7
|
|
|
|
|
|
|
} |
8
|
|
|
|
|
|
|
# ABSTRACT: implement http_hmac token scheme |
9
|
|
|
|
|
|
|
|
10
|
2
|
|
|
2
|
|
6
|
use Net::OAuth2::Scheme::Option::Defines; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
97
|
|
11
|
|
|
|
|
|
|
use Net::OAuth2::Scheme::HmacUtil |
12
|
2
|
|
|
2
|
|
8
|
qw(hmac_name_to_len_fn encode_plainstring decode_plainstring timing_indep_eq); |
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
95
|
|
13
|
2
|
|
|
2
|
|
751
|
use MIME::Base64 qw(encode_base64 decode_base64); |
|
2
|
|
|
|
|
923
|
|
|
2
|
|
|
|
|
2129
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
# HMAC token |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# IMPLEMENTATION (transport_)http_hmac |
18
|
|
|
|
|
|
|
# (http_hmac_)nonce_length = 8 |
19
|
|
|
|
|
|
|
# (http_hmac_)ext_body ($request, 'server'|'client') -> ext |
20
|
|
|
|
|
|
|
# SUMMARY |
21
|
|
|
|
|
|
|
# http_hmac token |
22
|
|
|
|
|
|
|
# REQUIRES |
23
|
|
|
|
|
|
|
# random |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
Default_Value http_hmac_token_type => 'mac'; |
26
|
|
|
|
|
|
|
Default_Value http_hmac_scheme => 'MAC'; |
27
|
|
|
|
|
|
|
Default_Value http_hmac_nonce_length => 8; |
28
|
|
|
|
|
|
|
Default_Value http_hmac_ext_body => sub {''}; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub pkg_transport_http_hmac { |
31
|
0
|
|
|
0
|
|
|
my __PACKAGE__ $self = shift; |
32
|
0
|
|
|
|
|
|
$self->parameter_prefix(http_hmac_ => @_); |
33
|
0
|
|
|
|
|
|
$self->make_alias(http_hmac_header => 'transport_header'); |
34
|
0
|
|
|
|
|
|
$self->make_alias(http_hmac_header_re => 'transport_header_re'); |
35
|
0
|
|
|
|
|
|
$self->make_alias(http_hmac_scheme => 'transport_auth_scheme'); |
36
|
0
|
|
|
|
|
|
$self->make_alias(http_hmac_scheme_re => 'transport_auth_scheme_re'); |
37
|
|
|
|
|
|
|
|
38
|
0
|
|
|
|
|
|
$self->install(token_type => $self->uses('http_hmac_token_type')); |
39
|
|
|
|
|
|
|
|
40
|
0
|
|
|
|
|
|
my $http_hmac_ext_body = $self->uses('http_hmac_ext_body'); |
41
|
0
|
0
|
|
|
|
|
if ($self->is_resource_server) { |
42
|
|
|
|
|
|
|
$self->install( psgi_extract => |
43
|
|
|
|
|
|
|
$self->http_header_extractor |
44
|
|
|
|
|
|
|
(parse_auth => sub { |
45
|
0
|
|
|
0
|
|
|
my ($auth, $req) = @_; |
46
|
0
|
|
|
|
|
|
my %attr = (); |
47
|
0
|
|
|
|
|
|
while ($auth =~ m{\G([^=[:space:]]+)\s*=\s*"([^"]*)"\s*,?\s*}gs) { |
48
|
0
|
|
|
|
|
|
$attr{$1} = $2; |
49
|
|
|
|
|
|
|
} |
50
|
0
|
0
|
|
|
|
|
return () if grep {!defined} (my ($id, $nonce, $mac) = @attr{qw(id nonce mac)}); |
|
0
|
|
|
|
|
|
|
51
|
0
|
0
|
|
|
|
|
my $ext = defined($attr{ext}) ? $attr{ext} : ''; |
52
|
|
|
|
|
|
|
|
53
|
0
|
|
|
|
|
|
my $uri = $req->uri; |
54
|
0
|
|
0
|
|
|
|
my ($host,$port) = split ':',($req->headers->{host} || $uri->host_port); |
55
|
0
|
0
|
0
|
|
|
|
$port ||= $uri->scheme eq 'https' ? 443 : 80; |
56
|
|
|
|
|
|
|
|
57
|
0
|
|
|
|
|
|
return ($id, $mac, $nonce, $req->method, $uri->path_query, $host, $port, |
58
|
|
|
|
|
|
|
$ext, $http_hmac_ext_body->($req, 'server')); |
59
|
0
|
|
|
|
|
|
})); |
60
|
|
|
|
|
|
|
} |
61
|
0
|
0
|
|
|
|
|
if ($self->is_client) { |
62
|
0
|
|
|
|
|
|
my $random = $self->uses('random'); |
63
|
0
|
|
|
|
|
|
my $nonce_length = $self->uses('http_hmac_nonce_length'); |
64
|
|
|
|
|
|
|
|
65
|
0
|
|
|
|
|
|
$self->install( accept_needs => [qw(mac_key mac_algorithm mac_received)] ); |
66
|
|
|
|
|
|
|
$self->install( accept_hook => sub { |
67
|
0
|
|
|
0
|
|
|
my $params = shift; |
68
|
0
|
|
|
|
|
|
$params->{mac_received} = time(); |
69
|
|
|
|
|
|
|
return ("unknown_algorithm") |
70
|
0
|
0
|
|
|
|
|
unless hmac_name_to_len_fn($params->{mac_algorithm}); |
71
|
0
|
|
|
|
|
|
return (); |
72
|
0
|
|
|
|
|
|
}); |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
$self->http_header_inserter |
75
|
|
|
|
|
|
|
(make_auth => sub { |
76
|
0
|
|
|
0
|
|
|
my ($http_req, $token, %o) = @_; |
77
|
|
|
|
|
|
|
|
78
|
0
|
|
|
|
|
|
my @missing; |
79
|
|
|
|
|
|
|
my ($key, $alg, $received) = |
80
|
0
|
0
|
|
|
|
|
map {defined $o{$_} ? $o{$_} : do { push @missing, @_; undef }} |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
(qw(mac_key mac_algorithm mac_received)); |
82
|
0
|
0
|
|
|
|
|
return ("missing_$missing[0]") |
83
|
|
|
|
|
|
|
if @missing; |
84
|
|
|
|
|
|
|
|
85
|
0
|
0
|
|
|
|
|
return ("unknown_algorithm") |
86
|
|
|
|
|
|
|
unless my (undef, $alg_fn) = hmac_name_to_len_fn($alg); |
87
|
|
|
|
|
|
|
|
88
|
0
|
|
|
|
|
|
my $nonce = (time() - $received) . ':' . encode_plainstring($random->($nonce_length)); |
89
|
|
|
|
|
|
|
|
90
|
0
|
|
|
|
|
|
my $uri = $http_req->uri; |
91
|
|
|
|
|
|
|
|
92
|
0
|
|
0
|
|
|
|
my ($host,$port) = split ':',($http_req->header('Host') || $uri->host_port); |
93
|
0
|
0
|
0
|
|
|
|
$port ||= $uri->scheme eq 'https' ? 443 : 80; |
94
|
|
|
|
|
|
|
|
95
|
0
|
|
|
|
|
|
my $ext = $http_hmac_ext_body->($http_req, 'client'); |
96
|
|
|
|
|
|
|
|
97
|
0
|
|
|
|
|
|
my $normalized = join "\n", |
98
|
|
|
|
|
|
|
$nonce, $http_req->method, $uri->path_query, $host, $port, $ext, ''; |
99
|
|
|
|
|
|
|
return |
100
|
|
|
|
|
|
|
(undef, |
101
|
0
|
0
|
|
|
|
|
join ",\n ", qq(id="$token"), qq(nonce="$nonce"), |
102
|
|
|
|
|
|
|
qq(mac=").encode_base64($alg_fn->($key,$normalized), '').qq("), |
103
|
|
|
|
|
|
|
(length($ext) ? (qq(ext="$ext")) : ())); |
104
|
0
|
|
|
|
|
|
}); |
105
|
|
|
|
|
|
|
} |
106
|
0
|
|
|
|
|
|
return $self; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# IMPLEMENTATION (format_)http_hmac |
110
|
|
|
|
|
|
|
# (http_hmac_)hmac |
111
|
|
|
|
|
|
|
# SUMMARY |
112
|
|
|
|
|
|
|
# HMAC-HTTP tokens |
113
|
|
|
|
|
|
|
# REQUIRES |
114
|
|
|
|
|
|
|
# v_id_next |
115
|
|
|
|
|
|
|
# v_table_insert |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
sub pkg_format_http_hmac { |
118
|
|
|
|
|
|
|
my __PACKAGE__ $self = shift; |
119
|
|
|
|
|
|
|
$self->parameter_prefix(http_hmac_ => @_); |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# CANNOT be used for authcodes and refresh tokens |
122
|
|
|
|
|
|
|
$self->install(format_no_params => 0); |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
my $mac_alg_name = $self->uses('http_hmac_hmac'); |
125
|
|
|
|
|
|
|
$mac_alg_name =~ y/_/-/; |
126
|
|
|
|
|
|
|
my ($mac_alg_keylen, $mac_alg) = hmac_name_to_len_fn($mac_alg_name) |
127
|
|
|
|
|
|
|
or $self->croak("unknown/unavailable hmac function: $mac_alg_name"); |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
if ($self->is_auth_server) { |
130
|
|
|
|
|
|
|
my ($random, $v_id_next, $vtable_insert, $token_type) = $self->uses_all |
131
|
|
|
|
|
|
|
qw(random v_id_next vtable_insert token_type); |
132
|
|
|
|
|
|
|
$self->install( token_create => sub { |
133
|
|
|
|
|
|
|
my ($now, $expires_in, @bindings) = @_; |
134
|
|
|
|
|
|
|
my $v_id = $v_id_next->(); |
135
|
|
|
|
|
|
|
my $key = encode_plainstring($random->($mac_alg_keylen)); |
136
|
|
|
|
|
|
|
my $error = $vtable_insert->($v_id, $now + $expires_in, $now, $key, @bindings); |
137
|
|
|
|
|
|
|
return ($error, |
138
|
|
|
|
|
|
|
($error ? () : |
139
|
|
|
|
|
|
|
(encode_plainstring($v_id), |
140
|
|
|
|
|
|
|
token_type => $token_type, |
141
|
|
|
|
|
|
|
mac_key => $key, |
142
|
|
|
|
|
|
|
mac_algorithm => $mac_alg_name))); |
143
|
|
|
|
|
|
|
}); |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
if ($self->is_resource_server) { |
147
|
|
|
|
|
|
|
$self->install( token_parse => sub { |
148
|
|
|
|
|
|
|
my ($v_id, @rest) = @_; |
149
|
|
|
|
|
|
|
return (decode_plainstring($v_id), @rest); |
150
|
|
|
|
|
|
|
}); |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
$self->install( token_finish => sub { |
153
|
|
|
|
|
|
|
my ($v, $mac, $nonce, $method, $uri, $host, $port, $ext, $ext_body) = @_; # ($validator, @payload) |
154
|
|
|
|
|
|
|
my ($expiration, $issuance, $key, @bindings) = @$v; |
155
|
|
|
|
|
|
|
$mac = decode_base64($mac); |
156
|
|
|
|
|
|
|
my $normalized = join "\n",$nonce,$method,$uri,$host,$port,$ext,$ext_body; |
157
|
|
|
|
|
|
|
return ('bad_hash') |
158
|
|
|
|
|
|
|
unless |
159
|
|
|
|
|
|
|
length($mac) == $mac_alg_keylen && |
160
|
|
|
|
|
|
|
timing_indep_eq($mac, $mac_alg->($key, $normalized), $mac_alg_keylen) && |
161
|
|
|
|
|
|
|
length ($ext) == length($ext_body) && |
162
|
|
|
|
|
|
|
timing_indep_eq($ext, $ext_body); |
163
|
|
|
|
|
|
|
return (undef, $issuance, $expiration - $issuance, @bindings); |
164
|
|
|
|
|
|
|
}); |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
return $self; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
1; |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
__END__ |