diff options
author | dakkar <dakkar@thenautilus.net> | 2014-12-20 14:27:55 +0000 |
---|---|---|
committer | dakkar <dakkar@thenautilus.net> | 2014-12-20 14:27:55 +0000 |
commit | a237abd8e01e2783b9bb9d6eb3c3c26ef8832f92 (patch) | |
tree | ed88c9fed05152c9ce2ee77e3bf42dc600a1d24b | |
download | net-hawk-a237abd8e01e2783b9bb9d6eb3c3c26ef8832f92.tar.gz net-hawk-a237abd8e01e2783b9bb9d6eb3c3c26ef8832f92.tar.bz2 net-hawk-a237abd8e01e2783b9bb9d6eb3c3c26ef8832f92.zip |
first tests passing
-rw-r--r-- | .gitignore | 14 | ||||
-rw-r--r-- | lib/Net/Hawk/Crypto.pm | 91 | ||||
-rw-r--r-- | lib/Net/Hawk/Utils.pm | 14 | ||||
-rw-r--r-- | t/tests/Net/Hawk/Crypto.t | 73 |
4 files changed, 192 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..263b16e --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +blib +pm_to_blib +*.sw? +Makefile +Makefile.old +MANIFEST.bak +*.tar.gz +/inc/ +/META.* +/MYMETA.* +.prove +*~ +/.build/ +/Net-Hawk-*/ diff --git a/lib/Net/Hawk/Crypto.pm b/lib/Net/Hawk/Crypto.pm new file mode 100644 index 0000000..a730ca6 --- /dev/null +++ b/lib/Net/Hawk/Crypto.pm @@ -0,0 +1,91 @@ +package Net::Hawk::Crypto; +use strict; +use warnings; +use 5.010; +use Moo; +use Types::Standard 1.000003 qw(Str Int Object Dict Optional Undef Any HasMethods slurpy); +use Types::URI qw(Uri); +use Type::Params qw(compile); +use Try::Tiny; +use Net::Hawk::Utils; +use Digest; + +sub header_version() { 1 } + +sub generate_normalized_string { + state $argcheck = compile(Object,Str,Dict[ + resource => Uri, + ts => Int, + nonce => Str, + method => Optional[Str], + host => Str, + port => Int, + hash => Optional[Str], + ext => Optional[Str], + app => Optional[Str], + dlg => Optional[Str], + slurpy Any, + ]); + my ($self,$type,$options) = $argcheck->(@_); + + my $normalized = sprintf( + "hawk.%d.%s\n%d\n%s\n%s\n%s\n%s\n%d\n%s\n%s\n", + header_version(), $type, + $options->{ts}, + $options->{nonce}, + uc($options->{method} // ''), + $options->{resource}->path_query, + lc($options->{host}), + $options->{port}, + $options->{hash} // '', + ($options->{ext} // '') =~ s{\\}{\\\\}gr =~ s{\n}{\\n}gr, + ); + + if ($options->{app}) { + $normalized .= sprintf( + "%s\n%s\n", + $options->{app}, + $options->{dlg} // '', + ); + } + + return $normalized; +} + +sub calculate_payload_hash { + state $argcheck = compile(Object,Str|Undef,Str,Str); + my ($self,$payload,$algorithm,$content_type) = $argcheck->(@_); + + my $hash = $self->initialize_payload_hash($algorithm,$content_type); + $hash->add($payload//''); + return $self->finalize_payload_hash($hash); +} + +sub initialize_payload_hash { + state $argcheck = compile(Object,Str,Str); + my ($self,$algorithm,$content_type) = $argcheck->(@_); + + my $digest = try { + Digest->new($algorithm); + } + catch { + $algorithm =~ s{(?<=[a-z])(?=[0-9])}{-}; + Digest->new(uc($algorithm)); + }; + + $digest->add(sprintf("hawk.%d.payload\n",header_version())); + $digest->add(Net::Hawk::Utils::parse_content_type($content_type),"\n"); + return $digest; +} + +sub finalize_payload_hash { + state $argcheck = compile(Object,HasMethods[qw(add b64digest)]); + my ($self,$digest) = $argcheck->(@_); + + $digest->add("\n"); + my $ret = $digest->b64digest(); + $ret .= '=' while length($ret) % 4; + return $ret; +} + +1; diff --git a/lib/Net/Hawk/Utils.pm b/lib/Net/Hawk/Utils.pm new file mode 100644 index 0000000..527021d --- /dev/null +++ b/lib/Net/Hawk/Utils.pm @@ -0,0 +1,14 @@ +package Net::Hawk::Utils; +use strict; +use warnings; +use 5.010; + +sub parse_content_type { + my ($header) = @_; + return '' unless defined $header; + + my ($ret) = $header =~ m{^\s*(\S+?)\s*(;|$)}; + return lc($ret); +} + +1; diff --git a/t/tests/Net/Hawk/Crypto.t b/t/tests/Net/Hawk/Crypto.t new file mode 100644 index 0000000..eb1a52f --- /dev/null +++ b/t/tests/Net/Hawk/Crypto.t @@ -0,0 +1,73 @@ +#!perl +use strict; +use warnings; +use Test::More; +use Net::Hawk::Crypto; + +my $c = Net::Hawk::Crypto->new(); + +my %credentials = ( + id => 'dh37fgj492je', + key => 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', + algorithm => 'sha256', +); +my %options = ( + credentials => \%credentials, + timestamp => 1353832234, + nonce => 'j4h3g2', + ext => 'some-app-ext-data' +); + +subtest GET => sub { + my $string = $c->generate_normalized_string( + header => { + credentials => \%credentials, + ts => $options{timestamp}, + nonce => $options{nonce}, + method => 'GET', + resource => '/resource?a=1&b=2', + host => 'example.com', + port => 8000, + ext => $options{ext}, + } + ); + + is( + $string, + "hawk.1.header\n1353832234\nj4h3g2\nGET\n/resource?a=1&b=2\nexample.com\n8000\n\nsome-app-ext-data\n", + 'normalized string generated ok', + ); +}; + +subtest POST => sub { + my $payload = 'Thank you for flying Hawk'; + my $content_type = 'text/plain'; + + my $payload_hash = $c->calculate_payload_hash( + $payload, + $credentials{algorithm}, + $content_type, + ); + + my $string = $c->generate_normalized_string( + header => { + credentials => \%credentials, + ts => $options{timestamp}, + nonce => $options{nonce}, + method => 'POST', + resource => '/resource?a=1&b=2', + host => 'example.com', + port => 8000, + hash => $payload_hash, + ext => $options{ext}, + } + ); + + is( + $string, + "hawk.1.header\n1353832234\nj4h3g2\nPOST\n/resource?a=1&b=2\nexample.com\n8000\nYi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=\nsome-app-ext-data\n", + 'normalized string generated ok', + ); +}; + +done_testing; |