1414 * limitations under the License.
1515 */
1616
17- import utils from "./utils.js" ;
17+ import awscred from "./awscredentials.js" ;
18+ import utils from "./utils.js" ;
1819
1920const mod_hmac = require ( 'crypto' ) ;
2021
@@ -28,8 +29,8 @@ const EMPTY_PAYLOAD_HASH = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495
2829 * Constant defining the headers being signed.
2930 * @type {string }
3031 */
31- const DEFAULT_SIGNED_HEADERS = 'host;x-amz-content-sha256;x-amz-date' ;
32-
32+ // const DEFAULT_SIGNED_HEADERS = 'host;x-amz-content-sha256;x-amz-date';
33+ const DEFAULT_SIGNED_HEADERS = 'host;x-amz-date' ;
3334
3435/**
3536 * Create HTTP Authorization header for authenticating with an AWS compatible
@@ -48,13 +49,13 @@ const DEFAULT_SIGNED_HEADERS = 'host;x-amz-content-sha256;x-amz-date';
4849function signatureV4 ( r , timestamp , region , service , uri , queryParams , host , credentials ) {
4950 const eightDigitDate = utils . getEightDigitDate ( timestamp ) ;
5051 const amzDatetime = utils . getAmzDatetime ( timestamp , eightDigitDate ) ;
51- const canonicalRequest = _buildCanonicalRequest (
52+ const canonicalRequest = _buildCanonicalRequest ( r ,
5253 r . method , uri , queryParams , host , amzDatetime , credentials . sessionToken ) ;
5354 const signature = _buildSignatureV4 ( r , amzDatetime , eightDigitDate ,
5455 credentials , region , service , canonicalRequest ) ;
5556 const authHeader = 'AWS4-HMAC-SHA256 Credential='
5657 . concat ( credentials . accessKeyId , '/' , eightDigitDate , '/' , region , '/' , service , '/aws4_request,' ,
57- 'SignedHeaders=' , _signedHeaders ( credentials . sessionToken ) , ',Signature=' , signature ) ;
58+ 'SignedHeaders=' , _signedHeaders ( r , credentials . sessionToken ) , ',Signature=' , signature ) ;
5859
5960 utils . debug_log ( r , 'AWS v4 Auth header: [' + authHeader + ']' ) ;
6061
@@ -73,22 +74,22 @@ function signatureV4(r, timestamp, region, service, uri, queryParams, host, cred
7374 * @returns {string } string with concatenated request parameters
7475 * @private
7576 */
76- function _buildCanonicalRequest ( method , uri , queryParams , host , amzDatetime , sessionToken ) {
77+ function _buildCanonicalRequest ( r ,
78+ method , uri , queryParams , host , amzDatetime , sessionToken ) {
79+ const payloadHash = awsHeaderPayloadHash ( r ) ;
7780 let canonicalHeaders = 'host:' + host + '\n' +
78- 'x-amz-content-sha256:' + EMPTY_PAYLOAD_HASH + '\n' +
79- 'x-amz-date:' + amzDatetime + '\n' ;
81+ 'x-amz-date:' + amzDatetime + '\n' ;
8082
81- if ( sessionToken ) {
83+ if ( sessionToken && sessionToken . length > 0 ) {
8284 canonicalHeaders += 'x-amz-security-token:' + sessionToken + '\n'
8385 }
8486
8587 let canonicalRequest = method + '\n' ;
8688 canonicalRequest += uri + '\n' ;
8789 canonicalRequest += queryParams + '\n' ;
8890 canonicalRequest += canonicalHeaders + '\n' ;
89- canonicalRequest += _signedHeaders ( sessionToken ) + '\n' ;
90- canonicalRequest += EMPTY_PAYLOAD_HASH ;
91-
91+ canonicalRequest += _signedHeaders ( r , sessionToken ) + '\n' ;
92+ canonicalRequest += payloadHash ;
9293 return canonicalRequest ;
9394}
9495
@@ -119,7 +120,7 @@ function _buildSignatureV4(
119120 const stringToSign = _buildStringToSign (
120121 amzDatetime , eightDigitDate , region , service , canonicalRequestHash ) ;
121122
122- utils . debug_log ( r , 'AWS v4 Auth Signing String: [' + stringToSign + ']' ) ;
123+ utils . debug_log ( r , 'AWS v4 Auth Signing String: [' + stringToSign + ']' ) ;
123124
124125 let kSigningHash ;
125126
@@ -145,13 +146,13 @@ function _buildSignatureV4(
145146 * we encode it as JSON. By doing so we can gracefully decode it
146147 * when reading from the cache. */
147148 kSigningHash = Buffer . from ( JSON . parse ( fields [ 1 ] ) ) ;
148- // Otherwise, generate a new signing key hash and store it in the cache
149+ // Otherwise, generate a new signing key hash and store it in the cache
149150 } else {
150151 kSigningHash = _buildSigningKeyHash ( creds . secretAccessKey , eightDigitDate , region , service ) ;
151152 utils . debug_log ( r , 'Writing key: ' + eightDigitDate + ':' + kSigningHash . toString ( 'hex' ) ) ;
152153 r . variables . signing_key_hash = eightDigitDate + ':' + JSON . stringify ( kSigningHash ) ;
153154 }
154- // Otherwise, don't use caching at all (like when we are using NGINX OSS)
155+ // Otherwise, don't use caching at all (like when we are using NGINX OSS)
155156 } else {
156157 kSigningHash = _buildSigningKeyHash ( creds . secretAccessKey , eightDigitDate , region , service ) ;
157158 }
@@ -190,13 +191,15 @@ function _buildStringToSign(amzDatetime, eightDigitDate, region, service, canoni
190191 * Creates a string containing the headers that need to be signed as part of v4
191192 * signature authentication.
192193 *
194+ * @param r {Request} HTTP request object
193195 * @param sessionToken {string|undefined} AWS session token if present
194196 * @returns {string } semicolon delimited string of the headers needed for signing
195197 * @private
196198 */
197- function _signedHeaders ( sessionToken ) {
198- let headers = DEFAULT_SIGNED_HEADERS ;
199- if ( sessionToken ) {
199+ function _signedHeaders ( r , sessionToken ) {
200+ let headers = '' ;
201+ headers += DEFAULT_SIGNED_HEADERS ;
202+ if ( sessionToken && sessionToken . length > 0 ) {
200203 headers += ';x-amz-security-token' ;
201204 }
202205 return headers ;
@@ -250,8 +253,40 @@ function _splitCachedValues(cached) {
250253 return [ eightDigitDate , kSigningHash ]
251254}
252255
256+ /**
257+ * Outputs the timestamp used to sign the request, so that it can be added to
258+ * the 'x-amz-date' header and sent by NGINX. The output format is
259+ * ISO 8601: YYYYMMDD'T'HHMMSS'Z'.
260+ * @see {@link https://docs.aws.amazon.com/general/latest/gr/sigv4-date-handling.html | Handling dates in Signature Version 4 }
261+ *
262+ * @param r {Request} HTTP request object (not used, but required for NGINX configuration)
263+ * @returns {string } ISO 8601 timestamp
264+ */
265+ function awsHeaderDate ( r ) {
266+ return utils . getAmzDatetime (
267+ awscred . getNow ( ) ,
268+ utils . getEightDigitDate ( awscred . getNow ( ) )
269+ ) ;
270+ }
271+
272+ /**
273+ * Return a payload hash in the header
274+ *
275+ * @param r {Request} HTTP request object
276+ * @returns {string } payload hash
277+ */
278+ function awsHeaderPayloadHash ( r ) {
279+ const reqBody = ( r . variables . request_body === 'undefined' ) ? '' :
280+ r . variables . request_body ;
281+ const payloadHash = mod_hmac . createHash ( 'sha256' , 'utf8' )
282+ . update ( reqBody )
283+ . digest ( 'hex' ) ;
284+ return payloadHash ;
285+ }
253286
254287export default {
288+ awsHeaderDate,
289+ awsHeaderPayloadHash,
255290 signatureV4,
256291 // These functions do not need to be exposed, but they are exposed so that
257292 // unit tests can run against them.
0 commit comments