@amazon-dax-sdk/client-dax
Version:
Amazon DAX Client for JavaScript
160 lines (141 loc) • 4.97 kB
JavaScript
/*
* Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
* use this file except in compliance with the License. A copy of the License
* is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
;
const crypto = require('crypto');
/** Class representing a Signature Generation type. */
class SigV4Gen {
/**
* Generate signature
* @param {AWSCredentials} creds
* @param {String} endpoint
* @param {String} region
* @param {String} payload
* @param {Date} time
* @return {SigAndStringToSign}
*/
static generateSigAndStringToSign(creds, endpoint, region, payload, time) {
if(time === undefined || !(time instanceof Date)) {
time = new Date();
}
const headers = SigV4Gen._getHeaders(endpoint, time, creds);
return SigV4Gen._getAuthorizationHeader('POST', time, headers,
payload, creds, region);
}
/**
* Get authorization header
* @param {String} requestType
* @param {Date} now
* @param {Map<String, String>} headers
* @param {String} payload
* @param {AWSCredentials} awsCredentials
* @param {String} region
* @return {SigAndStringToSign}
*/
static _getAuthorizationHeader(requestType, now, headers, payload,
awsCredentials, region) {
const credentialScope = customizedtoISOString(now, true) +
'/' + region + '/dax/aws4_request';
const canonicalRequest = requestType + '\n/\n' /* URI */ + '\n' +
SigV4Gen._getCanonicalHeaders(headers) + '\n' +
SigV4Gen._getSignedHeaders() + '\n' + SigV4Gen._SHA256(payload);
const stringToSign = 'AWS4-HMAC-SHA256\n' +
customizedtoISOString(now, false) + '\n' + credentialScope +
'\n' + SigV4Gen._SHA256(canonicalRequest);
const signingKey = SigV4Gen._getSignatureKey(
awsCredentials.secretAccessKey, customizedtoISOString(now, true),
region, 'dax');
let signature = SigV4Gen._HmacSHA256(stringToSign, signingKey)
.toString('hex');
let sessionToken = awsCredentials.sessionToken !== undefined
? awsCredentials.sessionToken : null;
return {signature: signature,
stringToSign: stringToSign,
sessionToken: sessionToken};
}
static _getCanonicalHeaders(headers) {
let stringBuilder = '';
for(let h of SigV4Gen._SIGNED_HEADERS) {
stringBuilder += (h + ':' + headers.get(h) + '\n');
}
return stringBuilder;
}
static _getSignedHeaders() {
return SigV4Gen._SIGNED_HEADERS.join(';');
}
static _SHA256(data) {
try {
const hash = crypto.createHash('sha256');
hash.update(data);
return hash.digest('hex');
} catch(e) {
throw new DaxClientError('Failed to compute SHA-256. ' +
e.name + ': ' + e.message, DaxErrorCode.Unrecognized);
}
}
static _HmacSHA256(data, key) {
const algorithm = SigV4Gen._HMAC_SHA256;
const hmac = crypto.createHmac(algorithm, key);
hmac.update(data);
return hmac.digest();
}
static _getSignatureKey(key, dateStamp, regionName, serviceName) {
try {
let kSecret = 'AWS4' + key;
let kDate = SigV4Gen._HmacSHA256(dateStamp, kSecret);
let kRegion = SigV4Gen._HmacSHA256(regionName, kDate);
let kService = SigV4Gen._HmacSHA256(serviceName, kRegion);
let kSigning = SigV4Gen._HmacSHA256('aws4_request', kService);
return kSigning;
} catch(e) {
throw e;
}
}
static _getHeaders(hostname, now, awsCredentials) {
const headers = new Map();
if(hostname.startsWith('https://')) {
hostname = hostname.substring(8);
}
headers.set(SigV4Gen._HEADER_NAME_HOST, hostname);
headers.set(SigV4Gen._HEADER_NAME_DATE, customizedtoISOString(now, false));
if(awsCredentials.sessionToken !== undefined) {
headers.set(SigV4Gen._HEADER_NAME_SECURITY_TOKEN,
awsCredentials.sessionToken);
}
return headers;
}
}
SigV4Gen._HMAC_SHA256 = 'sha256';
SigV4Gen._HEADER_NAME_DATE = 'x-amz-date';
SigV4Gen._HEADER_NAME_SECURITY_TOKEN = 'x-amz-security-token';
SigV4Gen._HEADER_NAME_HOST = 'host';
// NOTE: should be in lexicographic order
SigV4Gen._SIGNED_HEADERS = [SigV4Gen._HEADER_NAME_HOST,
SigV4Gen._HEADER_NAME_DATE];
function customizedtoISOString(date, dateOnly) {
function pad(number) {
if(number < 10) {
return '0' + number;
}
return number;
}
return date.getUTCFullYear() +
'' + pad(date.getUTCMonth() + 1) +
'' + pad(date.getUTCDate()) + (dateOnly ? '' :
'T' + pad(date.getUTCHours()) +
'' + pad(date.getUTCMinutes()) +
'' + pad(date.getUTCSeconds()) +
'Z');
}
module.exports = SigV4Gen;