2014-04-01 00:47:41 -06:00

161 lines
5.1 KiB
JavaScript

var AWS = require('../core');
var inherit = AWS.util.inherit;
/**
* @api private
*/
var cachedSecret = {};
/**
* @api private
*/
AWS.Signers.V4 = inherit(AWS.Signers.RequestSigner, {
constructor: function V4(request, serviceName) {
AWS.Signers.RequestSigner.call(this, request);
this.serviceName = serviceName;
},
addAuthorization: function addAuthorization(credentials, date) {
var datetime = AWS.util.date.iso8601(date).replace(/[:\-]|\.\d{3}/g, '');
this.addHeaders(credentials, datetime);
this.updateBody(credentials);
this.request.headers['Authorization'] =
this.authorization(credentials, datetime);
},
addHeaders: function addHeaders(credentials, datetime) {
this.request.headers['X-Amz-Date'] = datetime;
if (credentials.sessionToken) {
this.request.headers['x-amz-security-token'] = credentials.sessionToken;
}
},
updateBody: function updateBody(credentials) {
if (this.request.params) {
this.request.params.AWSAccessKeyId = credentials.accessKeyId;
if (credentials.sessionToken) {
this.request.params.SecurityToken = credentials.sessionToken;
}
this.request.body = AWS.util.queryParamsToString(this.request.params);
this.request.headers['Content-Length'] = this.request.body.length;
}
},
authorization: function authorization(credentials, datetime) {
var parts = [];
var credString = this.credentialString(datetime);
parts.push('AWS4-HMAC-SHA256 Credential=' +
credentials.accessKeyId + '/' + credString);
parts.push('SignedHeaders=' + this.signedHeaders());
parts.push('Signature=' + this.signature(credentials, datetime));
return parts.join(', ');
},
signature: function signature(credentials, datetime) {
var cache = cachedSecret[this.serviceName];
var date = datetime.substr(0, 8);
if (!cache ||
cache.akid !== credentials.accessKeyId ||
cache.region !== this.request.region ||
cache.date !== date) {
var kSecret = credentials.secretAccessKey;
var kDate = AWS.util.crypto.hmac('AWS4' + kSecret, date, 'buffer');
var kRegion = AWS.util.crypto.hmac(kDate, this.request.region, 'buffer');
var kService = AWS.util.crypto.hmac(kRegion, this.serviceName, 'buffer');
var kCredentials = AWS.util.crypto.hmac(kService, 'aws4_request', 'buffer');
cachedSecret[this.serviceName] = {
region: this.request.region, date: date,
key: kCredentials, akid: credentials.accessKeyId
};
}
var key = cachedSecret[this.serviceName].key;
return AWS.util.crypto.hmac(key, this.stringToSign(datetime), 'hex');
},
stringToSign: function stringToSign(datetime) {
var parts = [];
parts.push('AWS4-HMAC-SHA256');
parts.push(datetime);
parts.push(this.credentialString(datetime));
parts.push(this.hexEncodedHash(this.canonicalString()));
return parts.join('\n');
},
canonicalString: function canonicalString() {
var parts = [];
parts.push(this.request.method);
parts.push(this.request.pathname());
parts.push(this.request.search());
parts.push(this.canonicalHeaders() + '\n');
parts.push(this.signedHeaders());
parts.push(this.hexEncodedBodyHash());
return parts.join('\n');
},
canonicalHeaders: function canonicalHeaders() {
var headers = [];
AWS.util.each.call(this, this.request.headers, function (key, item) {
headers.push([key, item]);
});
headers.sort(function (a, b) {
return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : 1;
});
var parts = [];
AWS.util.arrayEach.call(this, headers, function (item) {
var key = item[0].toLowerCase();
if (this.isSignableHeader(key)) {
parts.push(key + ':' +
this.canonicalHeaderValues(item[1].toString()));
}
});
return parts.join('\n');
},
canonicalHeaderValues: function canonicalHeaderValues(values) {
return values.replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, '');
},
signedHeaders: function signedHeaders() {
var keys = [];
AWS.util.each.call(this, this.request.headers, function (key) {
key = key.toLowerCase();
if (this.isSignableHeader(key)) keys.push(key);
});
return keys.sort().join(';');
},
credentialString: function credentialString(datetime) {
var parts = [];
parts.push(datetime.substr(0, 8));
parts.push(this.request.region);
parts.push(this.serviceName);
parts.push('aws4_request');
return parts.join('/');
},
hexEncodedHash: function hash(string) {
return AWS.util.crypto.sha256(string, 'hex');
},
hexEncodedBodyHash: function hexEncodedBodyHash() {
if (this.request.headers['X-Amz-Content-Sha256']) {
return this.request.headers['X-Amz-Content-Sha256'];
} else {
return this.hexEncodedHash(this.request.body || '');
}
},
unsignableHeaders: ['authorization', 'content-type', 'user-agent',
'x-amz-user-agent', 'x-amz-content-sha256'],
isSignableHeader: function isSignableHeader(key) {
return this.unsignableHeaders.indexOf(key) < 0;
}
});
module.exports = AWS.Signers.V4;