Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add X-Amz-Content-Sha256 header to all SigV4 requests. #563

Merged
merged 1 commit into from
Apr 7, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions lib/event_listeners.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,24 @@ AWS.EventListeners = {
new AWS.ParamValidator().validate(rules, req.params);
});

addAsync('COMPUTE_SHA256', 'afterBuild', function COMPUTE_SHA256(req, done) {
if (!req.service.api.signatureVersion) return done(); // none
if (req.service.getSignerClass(req) === AWS.Signers.V4) {
var body = req.httpRequest.body || '';
AWS.util.computeSha256(body, function(err, sha) {
if (err) {
done(err);
}
else {
req.httpRequest.headers['X-Amz-Content-Sha256'] = sha;
done();
}
});
} else {
done();
}
});

add('SET_CONTENT_LENGTH', 'afterBuild', function SET_CONTENT_LENGTH(req) {
if (req.httpRequest.headers['Content-Length'] === undefined) {
var length = AWS.util.string.byteLength(req.httpRequest.body);
Expand Down
35 changes: 1 addition & 34 deletions lib/services/s3.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ AWS.util.update(AWS.S3.prototype, {
request.addListener('build', this.addContentType);
request.addListener('build', this.populateURI);
request.addListener('build', this.computeContentMd5);
request.onAsync('build', this.computeSha256);
request.addListener('build', this.computeSseCustomerKeyMd5);
request.addListener('afterBuild', this.addExpect100Continue);
request.removeListener('validate',
Expand Down Expand Up @@ -199,38 +198,6 @@ AWS.util.update(AWS.S3.prototype, {
}
},

/**
* @api private
*/
computeSha256: function computeSha256(req, done) {
if (req.service.getSignerClass(req) === AWS.Signers.V4) {
var body = req.httpRequest.body || '';

if (AWS.util.isNode()) {
var Stream = AWS.util.nodeRequire('stream').Stream;
var fs = AWS.util.nodeRequire('fs');
if (body instanceof Stream) {
if (typeof body.path === 'string') { // assume file object
body = fs.createReadStream(body.path);
} else { // TODO support other stream types
done(new Error('Non-file stream objects are ' +
'not supported with SigV4 in AWS.S3'));
return;
}
}
}

AWS.util.crypto.sha256(body, 'hex', function(err, sha) {
if (!err) {
req.httpRequest.headers['X-Amz-Content-Sha256'] = sha;
}
done(err);
});
} else {
done();
}
},

/**
* @api private
*/
Expand Down Expand Up @@ -420,7 +387,7 @@ AWS.util.update(AWS.S3.prototype, {
if (!request.params.Body) {
// no Content-MD5/SHA-256 if body is not provided
request.removeListener('build', request.service.computeContentMd5);
request.removeListener('build', request.service.computeSha256);
request.removeListener('afterBuild', AWS.EventListeners.Core.COMPUTE_SHA256);
}
},

Expand Down
2 changes: 2 additions & 0 deletions lib/signers/presign.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ AWS.Signers.Presign = inherit({
request.on('sign', signedUrlSigner);
request.removeListener('afterBuild',
AWS.EventListeners.Core.SET_CONTENT_LENGTH);
request.removeListener('afterBuild',
AWS.EventListeners.Core.COMPUTE_SHA256);

request.emit('beforePresign', [request]);

Expand Down
25 changes: 25 additions & 0 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,31 @@ var util = {
});
}
}
},

/**
* Compute SHA-256 checksums of streams
*
* @api private
*/
computeSha256: function computeSha256(body, done) {
if (AWS.util.isNode()) {
var Stream = AWS.util.nodeRequire('stream').Stream;
var fs = AWS.util.nodeRequire('fs');
if (body instanceof Stream) {
if (typeof body.path === 'string') { // assume file object
body = fs.createReadStream(body.path);
} else { // TODO support other stream types
return done(new Error('Non-file stream objects are ' +
'not supported with SigV4'));
}
}
}

AWS.util.crypto.sha256(body, 'hex', function(err, sha) {
if (err) done(err);
else done(null, sha);
});
}

};
Expand Down
49 changes: 28 additions & 21 deletions test/event_listeners.spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -114,34 +114,41 @@ describe 'AWS.EventListeners', ->
expect(response.error).to.equal("ERROR")

describe 'afterBuild', ->
sendRequest = (body) ->
request = null
fs = null

sendRequest = (body, callback) ->
request = makeRequest()
request.removeAllListeners('sign')
request.on('build', (req) -> req.httpRequest.body = body)
request.build()
request
request.removeAllListeners 'sign'
request.on 'build', (req) -> req.httpRequest.body = body
if callback
request.send(callback)
else
request.send()
request

contentLength = (body) ->
sendRequest(body).httpRequest.headers['Content-Length']
describe 'adds Content-Length header', ->
contentLength = (body) ->
sendRequest(body).httpRequest.headers['Content-Length']

it 'builds Content-Length in the request headers for string content', ->
expect(contentLength('FOOBAR')).to.equal(6)
it 'builds Content-Length in the request headers for string content', ->
expect(contentLength('FOOBAR')).to.equal(6)

it 'builds Content-Length for string "0"', ->
expect(contentLength('0')).to.equal(1)
it 'builds Content-Length for string "0"', ->
expect(contentLength('0')).to.equal(1)

it 'builds Content-Length for utf-8 string body', ->
expect(contentLength('tï№')).to.equal(6)
it 'builds Content-Length for utf-8 string body', ->
expect(contentLength('tï№')).to.equal(6)

it 'builds Content-Length for buffer body', ->
expect(contentLength(new AWS.util.Buffer('tï№'))).to.equal(6)
it 'builds Content-Length for buffer body', ->
expect(contentLength(new AWS.util.Buffer('tï№'))).to.equal(6)

if AWS.util.isNode()
it 'builds Content-Length for file body', ->
fs = require('fs')
file = fs.createReadStream(__filename)
fileLen = fs.lstatSync(file.path).size
expect(contentLength(file)).to.equal(fileLen)
if AWS.util.isNode()
it 'builds Content-Length for file body', (done) ->
fs = require('fs')
file = fs.createReadStream(__filename)
sendRequest file, (err) ->
done()

describe 'sign', ->
it 'takes the request object as a parameter', ->
Expand Down
5 changes: 3 additions & 2 deletions test/signers/v4.spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ describe 'AWS.Signers.V4', ->
date = new Date(1935346573456)
datetime = AWS.util.date.iso8601(date).replace(/[:\-]|\.\d{3}/g, '')
creds = null
signature = '0e24aaa0cc86cdc1b73143a147e731cf8c93d450cfcf1d18b2b7473f810b7a1d'
signature = '31fac5ed29db737fbcafac527470ca6d9283283197c5e6e94ea40ddcec14a9c1'
authorization = 'AWS4-HMAC-SHA256 Credential=akid/20310430/region/dynamodb/aws4_request, ' +
'SignedHeaders=host;x-amz-date;x-amz-security-token;x-amz-target;x-amz-user-agent, ' +
'SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-target;x-amz-user-agent, ' +
'Signature=' + signature
signer = null

Expand Down Expand Up @@ -124,6 +124,7 @@ describe 'AWS.Signers.V4', ->
it 'should return headers', ->
expect(signer.canonicalHeaders()).to.eql [
'host:localhost',
'x-amz-content-sha256:3128b8d4f3108b3e1677a38eb468d1c6dec926a58eaea235d034b9c71c3864d4',
'x-amz-date:' + datetime,
'x-amz-security-token:session',
'x-amz-target:DynamoDB_20111205.ListTables',
Expand Down