4 * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
12 var crypto = require('crypto')
13 , parse = require('url').parse
38 * Return an "Authorization" header value with the given `options`
39 * in the form of "AWS <key>:<signature>"
41 * @param {Object} options
46 function authorization (options) {
47 return 'AWS ' + options.key + ':' + sign(options)
50 module.exports = authorization
51 module.exports.authorization = authorization
54 * Simple HMAC-SHA1 Wrapper
56 * @param {Object} options
61 function hmacSha1 (options) {
62 return crypto.createHmac('sha1', options.secret).update(options.message).digest('base64')
65 module.exports.hmacSha1 = hmacSha1
68 * Create a base64 sha1 HMAC for `options`.
70 * @param {Object} options
75 function sign (options) {
76 options.message = stringToSign(options)
77 return hmacSha1(options)
79 module.exports.sign = sign
82 * Create a base64 sha1 HMAC for `options`.
84 * Specifically to be used with S3 presigned URLs
86 * @param {Object} options
91 function signQuery (options) {
92 options.message = queryStringToSign(options)
93 return hmacSha1(options)
95 module.exports.signQuery= signQuery
98 * Return a string for sign() with the given `options`.
109 * @param {Object} options
114 function stringToSign (options) {
115 var headers = options.amazonHeaders || ''
116 if (headers) headers += '\n'
120 , options.contentType
121 , options.date.toUTCString()
122 , headers + options.resource
126 module.exports.queryStringToSign = stringToSign
129 * Return a string for sign() with the given `options`, but is meant exclusively
130 * for S3 presigned URLs
137 * @param {Object} options
142 function queryStringToSign (options){
143 return 'GET\n\n\n' + options.date + '\n' + options.resource
145 module.exports.queryStringToSign = queryStringToSign
148 * Perform the following:
150 * - ignore non-amazon headers
152 * - sort lexicographically
153 * - trim whitespace between ":"
154 * - join with newline
156 * @param {Object} headers
161 function canonicalizeHeaders (headers) {
163 , fields = Object.keys(headers)
165 for (var i = 0, len = fields.length; i < len; ++i) {
166 var field = fields[i]
167 , val = headers[field]
168 , field = field.toLowerCase()
170 if (0 !== field.indexOf('x-amz')) continue
171 buf.push(field + ':' + val)
173 return buf.sort().join('\n')
175 module.exports.canonicalizeHeaders = canonicalizeHeaders
178 * Perform the following:
180 * - ignore non sub-resources
181 * - sort lexicographically
183 * @param {String} resource
188 function canonicalizeResource (resource) {
189 var url = parse(resource, true)
190 , path = url.pathname
194 Object.keys(url.query).forEach(function(key){
195 if (!~keys.indexOf(key)) return
196 var val = '' == url.query[key] ? '' : '=' + encodeURIComponent(url.query[key])
200 return path + (buf.length ? '?' + buf.sort().join('&') : '')
202 module.exports.canonicalizeResource = canonicalizeResource