Beta Shell
v2.0 · web2.us.cloudlogin.co
[FM]
[CMD]
[PHP]
[DB]
[INFO]
[SEC]
File Manager
~
/
usr
/
share
/
perl5
/
vendor_perl
/
Net
/
Amazon
/
S3
/
Signature
Upload
5 items
Name
Size
Perms
Modified
Actions
[ .. / .. ]
V2.pm
4.93 KB
-rw-r--r--
2022-07-16 23:46:00
Edit
Del
V4.pm
2.81 KB
-rw-r--r--
2022-07-16 23:46:00
Edit
Del
V4Implementation.pm
10 KB
-rw-r--r--
2022-07-16 23:46:00
Edit
Del
Editing: V4Implementation.pm
(10 KB)
Path: /usr/share/perl5/vendor_perl/Net/Amazon/S3/Signature/V4Implementation.pm
Back
package Net::Amazon::S3::Signature::V4Implementation; # ABSTRACT: Implements the Amazon Web Services signature version 4, AWS4-HMAC-SHA256 (copy of Net::Amazon::Signature::V4) $Net::Amazon::S3::Signature::V4Implementation::VERSION = '0.991'; use strict; use warnings; use sort 'stable'; use Digest::SHA qw/sha256_hex hmac_sha256 hmac_sha256_hex/; use Time::Piece (); use URI::Escape; use URI; use URI::QueryParam; our $ALGORITHM = 'AWS4-HMAC-SHA256'; our $MAX_EXPIRES = 604800; # Max, 7 days our $X_AMZ_ALGORITHM = 'X-Amz-Algorithm'; our $X_AMZ_CONTENT_SHA256 = 'X-Amz-Content-Sha256'; our $X_AMZ_CREDENTIAL = 'X-Amz-Credential'; our $X_AMZ_DATE = 'X-Amz-Date'; our $X_AMZ_EXPIRES = 'X-Amz-Expires'; our $X_AMZ_SIGNEDHEADERS = 'X-Amz-SignedHeaders'; our $X_AMZ_SIGNATURE = 'X-Amz-Signature'; sub new { my $class = shift; my ( $access_key_id, $secret, $endpoint, $service ) = @_; my $self = { access_key_id => $access_key_id, secret => $secret, endpoint => $endpoint, service => $service, }; bless $self, $class; return $self; } sub sign { my ( $self, $request ) = @_; $request = $self->_augment_request( $request ); my $authz = $self->_authorization( $request ); $request->header( Authorization => $authz ); return $request; } sub sign_uri { my ( $self, $uri, $expires_in, $for_method ) = @_; my $request = $self->_augment_uri( $uri, $expires_in, $for_method ); my $signature = $self->_signature( $request ); $uri = $request->uri; my $query = $uri->query; $uri->query( undef ); $uri = $uri . '?' . $self->_sort_query_string( $query ); $uri .= "&$X_AMZ_SIGNATURE=$signature"; return $uri; } # _headers_to_sign: # Return the sorted lower case headers as required by the generation of canonical headers sub _headers_to_sign { my $req = shift; my @headers_to_sign = $req->uri->query_param( $X_AMZ_SIGNEDHEADERS ) ? $req->uri->query_param( $X_AMZ_SIGNEDHEADERS ) : $req->headers->header_field_names ; return sort { $a cmp $b } map { lc } @headers_to_sign } # _augment_request: # Append mandatory header fields sub _augment_request { my ( $self, $request ) = @_; $request->header($X_AMZ_DATE => $self->_format_amz_date( $self->_req_timepiece($request) )) unless $request->header($X_AMZ_DATE); $request->header($X_AMZ_CONTENT_SHA256 => sha256_hex($request->content)) unless $request->header($X_AMZ_CONTENT_SHA256); return $request; } # _augment_uri: # Append mandatory uri parameters sub _augment_uri { my ($self, $uri, $expires_in, $method) = @_; my $request = HTTP::Request->new( $method || GET => $uri ); $request->uri->query_param( $X_AMZ_DATE => $self->_format_amz_date( $self->_now ) ) unless $request->uri->query_param( $X_AMZ_DATE ); $request->uri->query_param( $X_AMZ_ALGORITHM => $ALGORITHM ) unless $request->uri->query_param( $X_AMZ_ALGORITHM ); $request->uri->query_param( $X_AMZ_CREDENTIAL => $self->_credential( $request ) ) unless $request->uri->query_param( $X_AMZ_CREDENTIAL ); $request->uri->query_param( $X_AMZ_EXPIRES => $expires_in || $MAX_EXPIRES ) unless $request->uri->query_param( $X_AMZ_EXPIRES ); $request->uri->query_param( $X_AMZ_EXPIRES => $MAX_EXPIRES ) if $request->uri->query_param( $X_AMZ_EXPIRES ) > $MAX_EXPIRES; $request->uri->query_param( $X_AMZ_SIGNEDHEADERS => 'host' ); return $request; } # _canonical_request: # Construct the canonical request string from an HTTP::Request. sub _canonical_request { my ( $self, $req ) = @_; my $creq_method = $req->method; my ( $creq_canonical_uri, $creq_canonical_query_string ) = ( $req->uri =~ m@([^?]*)\?(.*)$@ ) ? ( $1, $2 ) : ( $req->uri, '' ); $creq_canonical_uri =~ s@^https?://[^/]*/?@/@; # Documentation says "do not normalize URI paths for requests to Amazon S3" # https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html #$creq_canonical_uri = _simplify_uri( $creq_canonical_uri ); $creq_canonical_query_string = $self->_sort_query_string( $creq_canonical_query_string ); # Ensure Host header is present as its required if (!$req->header('host')) { my $host = $req->uri->_port ? $req->uri->host_port : $req->uri->host ; $req->header('Host' => $host); } my $creq_payload_hash = $req->header($X_AMZ_CONTENT_SHA256) # Signed uri doesn't have content || 'UNSIGNED-PAYLOAD'; # There's a bug in AMS4 which causes requests without x-amz-date set to be rejected # so we always add one if its not present. my $amz_date = $req->header($X_AMZ_DATE); my @sorted_headers = _headers_to_sign( $req ); my $creq_canonical_headers = join '', map { sprintf "%s:%s\x0a", lc, join ',', sort {$a cmp $b } _trim_whitespace($req->header($_) ) } @sorted_headers; my $creq_signed_headers = $self->_signed_headers( $req ); my $creq = join "\x0a", $creq_method, $creq_canonical_uri, $creq_canonical_query_string, $creq_canonical_headers, $creq_signed_headers, $creq_payload_hash; return $creq; } # _string_to_sign # Construct the string to sign. sub _string_to_sign { my ( $self, $req ) = @_; my $dt = $self->_req_timepiece( $req ); my $creq = $self->_canonical_request($req); my $sts_request_date = $self->_format_amz_date( $dt ); my $sts_credential_scope = join '/', $dt->strftime('%Y%m%d'), $self->{endpoint}, $self->{service}, 'aws4_request'; my $sts_creq_hash = sha256_hex( $creq ); my $sts = join "\x0a", $ALGORITHM, $sts_request_date, $sts_credential_scope, $sts_creq_hash; return $sts; } # _authorization # Construct the authorization string sub _signature { my ( $self, $req ) = @_; my $dt = $self->_req_timepiece( $req ); my $sts = $self->_string_to_sign( $req ); my $k_date = hmac_sha256( $dt->strftime('%Y%m%d'), 'AWS4' . $self->{secret} ); my $k_region = hmac_sha256( $self->{endpoint}, $k_date ); my $k_service = hmac_sha256( $self->{service}, $k_region ); my $k_signing = hmac_sha256( 'aws4_request', $k_service ); my $authz_signature = hmac_sha256_hex( $sts, $k_signing ); return $authz_signature; } sub _credential { my ( $self, $req ) = @_; my $dt = $self->_req_timepiece( $req ); my $authz_credential = join '/', $self->{access_key_id}, $dt->strftime('%Y%m%d'), $self->{endpoint}, $self->{service}, 'aws4_request'; return $authz_credential; } sub _signed_headers { my ( $self, $req ) = @_; my $authz_signed_headers = join ';', _headers_to_sign( $req ); return $authz_signed_headers; } sub _authorization { my ( $self, $req ) = @_; my $authz_signature = $self->_signature( $req ); my $authz_credential = $self->_credential( $req ); my $authz_signed_headers = $self->_signed_headers( $req ); my $authz = "$ALGORITHM Credential=$authz_credential,SignedHeaders=$authz_signed_headers,Signature=$authz_signature"; return $authz; } sub _simplify_uri { my $orig_uri = shift; my @parts = split /\//, $orig_uri; my @simple_parts = (); for my $part ( @parts ) { if ( ! length $part || $part eq '.' ) { } elsif ( $part eq '..' ) { pop @simple_parts; } else { push @simple_parts, $part; } } my $simple_uri = '/' . join '/', @simple_parts; $simple_uri .= '/' if $orig_uri =~ m@/$@ && $simple_uri !~ m@/$@; return $simple_uri; } sub _sort_query_string { my $self = shift; return '' unless $_[0]; my @params; for my $param ( split /&/, $_[0] ) { my ( $key, $value ) = map { tr/+/ /; uri_escape( uri_unescape( $_ ) ) } # escape all non-unreserved chars split /=/, $param; push @params, [$key, (defined $value ? $value : '')]; #push @params, [$key, $value]; } return join '&', map { join '=', grep defined, @$_ } sort { ( $a->[0] cmp $b->[0] ) || ( $a->[1] cmp $b->[1] ) } @params; } sub _trim_whitespace { return map { my $str = $_; $str =~ s/^\s*//; $str =~ s/\s*$//; $str } @_; } sub _str_to_timepiece { my $date = shift; if ( $date =~ m/^\d{8}T\d{6}Z$/ ) { # assume basic ISO 8601, as demanded by AWS return Time::Piece->strptime($date, '%Y%m%dT%H%M%SZ'); } else { # assume the format given in the AWS4 test suite $date =~ s/^.{5}//; # remove weekday, as Amazon's test suite contains internally inconsistent dates return Time::Piece->strptime($date, '%d %b %Y %H:%M:%S %Z'); } } sub _format_amz_date { my ($self, $dt) = @_; $dt->strftime('%Y%m%dT%H%M%SZ'); } sub _now { return scalar Time::Piece->gmtime; } sub _req_timepiece { my ($self, $req) = @_; my $x_date = $req->header($X_AMZ_DATE) || $req->uri->query_param($X_AMZ_DATE); my $date = $x_date || $req->header('Date'); if (!$date) { # No date set by the caller so set one up my $piece = $self->_now; $req->date($piece->epoch); return $piece } return _str_to_timepiece($date); } 1; __END__ =pod =encoding UTF-8 =head1 NAME Net::Amazon::S3::Signature::V4Implementation - Implements the Amazon Web Services signature version 4, AWS4-HMAC-SHA256 (copy of Net::Amazon::Signature::V4) =head1 VERSION version 0.991 =head1 DESCRIPTION This package clones L<Net::Amazon::Signature::V4> 0.19 adding support for signing URIs (GET request) Until https://github.com/Grinnz/Net-Amazon-Signature-V4/pull/5 will be merged we have to maintain our clone. =head1 Net::Amazon::Signature::S4 AUTHORS Tim Nordenfur, C<< <tim at gurka.se> >> Maintained by Dan Book, C<< <dbook at cpan.org> >> =head2 sign( $request ) Signs a request with your credentials by appending the Authorization header. $request should be an HTTP::Request. The signed request is returned. =head2 sign_uri( $uri, $expires_in?, $for_method? ) Signs an uri with your credentials by appending the Authorization query parameters. C<< $expires_in >> integer value in range 1..604800 (1 second .. 7 days). C<< $expires_in >> default value is its maximum: 604800 C<< $for_method >> HTTP method this uri should be signed for, default C<GET> The signed uri is returned. =head1 AUTHOR Branislav Zahradník <barney@cpan.org> =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2022 by Amazon Digital Services, Leon Brocard, Brad Fitzpatrick, Pedro Figueiredo, Rusty Conover, Branislav Zahradník. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut