package Net::Hawk::Crypto { use v6; use URI; use Digest::SHA; use Digest::HMAC; use MIME::Base64; use Net::Hawk::Utils; sub header_version() { 1 } proto generate_normalized_string(*%x) returns Str is export {*}; multi generate_normalized_string(Str:D :$resource!,*%named) returns Str { return generate_normalized_string(|%named,resource=>URI.new($resource)); }; multi generate_normalized_string( Str:D :$type!, URI :$resource, Int:D :$ts!, Str:D :$nonce!, Str :$method, Str:D :$host!, Int:D :$port!, Str :$hash, Str :$ext, Str :$app, Str :$dlg, *%, ) returns Str is export { 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, $ts, $nonce, uc($method // ''), ( $resource ?? $resource.path_query !! ''), lc($host), $port, $hash // '', ($ext // '').trans(['\\',"\n"] => ['\\\\','\\n']), ); if ($app) { $normalized ~= sprintf( "%s\n%s\n", $app, $dlg // '', ); } return $normalized; }; sub is_valid_hash_algorithm(Str $algorithm) is export { return False unless $algorithm; return True if $algorithm eq 'sha1'; return True if $algorithm eq 'sha256'; return False; } sub digest_for(Str:D $algorithm) { if ($algorithm eq 'sha1') { return &sha1 } elsif ($algorithm eq 'sha256') { return &sha256 } else { die "bad alg $algorithm" } } sub calculate_payload_hash( Str $payload!, Str:D $algorithm!, Str $content_type!, ) returns Str is export { my $hash_function = digest_for($algorithm); return MIME::Base64.encode( $hash_function(sprintf("hawk.%d.payload\n%s\n%s\n", header_version(), parse_content_type($content_type), $payload))); }; sub calc_hmac( Str:D $data, Str:D $algorithm, Str:D $key, ) returns Str { my $hash_function = digest_for($algorithm); return MIME::Base64.encode( hmac($key,$data,$hash_function) ); } sub calculate_mac( Str:D $type, Hash:D $credentials ( Str :$algorithm, Str :$key, *% ), Hash:D $options ) returns Str is export { my $normalized = generate_normalized_string(:$type,|$options); return calc_hmac( $normalized, $algorithm, $key, ); }; sub calculate_ts_mac( Int:D $ts, Hash:D $credentials ( Str :$algorithm, Str :$key, *% ), ) returns Str is export { my $string = sprintf( "hawk.%s.ts\n%d\n", header_version(), $ts, ); return calc_hmac( $string, $algorithm, $key, ); }; sub timestamp_message( %credentials, Int $localtime_offset_msec ) is export { my $ts = now_msecs($localtime_offset_msec); my $tsm = calculate_ts_mac($ts,%credentials); return { :$ts, :$tsm }; }; }