// Load modules | |
var Crypto = require('crypto'); | |
var Url = require('url'); | |
var Utils = require('./utils'); | |
// Declare internals | |
var internals = {}; | |
// MAC normalization format version | |
exports.headerVersion = '1'; // Prevent comparison of mac values generated with different normalized string formats | |
// Supported HMAC algorithms | |
exports.algorithms = ['sha1', 'sha256']; | |
// Calculate the request MAC | |
/* | |
type: 'header', // 'header', 'bewit', 'response' | |
credentials: { | |
key: 'aoijedoaijsdlaksjdl', | |
algorithm: 'sha256' // 'sha1', 'sha256' | |
}, | |
options: { | |
method: 'GET', | |
resource: '/resource?a=1&b=2', | |
host: 'example.com', | |
port: 8080, | |
ts: 1357718381034, | |
nonce: 'd3d345f', | |
hash: 'U4MKKSmiVxk37JCCrAVIjV/OhB3y+NdwoCr6RShbVkE=', | |
ext: 'app-specific-data', | |
app: 'hf48hd83qwkj', // Application id (Oz) | |
dlg: 'd8djwekds9cj' // Delegated by application id (Oz), requires options.app | |
} | |
*/ | |
exports.calculateMac = function (type, credentials, options) { | |
var normalized = exports.generateNormalizedString(type, options); | |
var hmac = Crypto.createHmac(credentials.algorithm, credentials.key).update(normalized); | |
var digest = hmac.digest('base64'); | |
return digest; | |
}; | |
exports.generateNormalizedString = function (type, options) { | |
var resource = options.resource || ''; | |
if (resource && | |
resource[0] !== '/') { | |
var url = Url.parse(resource, false); | |
resource = url.path; // Includes query | |
} | |
var normalized = 'hawk.' + exports.headerVersion + '.' + type + '\n' + | |
options.ts + '\n' + | |
options.nonce + '\n' + | |
(options.method || '').toUpperCase() + '\n' + | |
resource + '\n' + | |
options.host.toLowerCase() + '\n' + | |
options.port + '\n' + | |
(options.hash || '') + '\n'; | |
if (options.ext) { | |
normalized += options.ext.replace('\\', '\\\\').replace('\n', '\\n'); | |
} | |
normalized += '\n'; | |
if (options.app) { | |
normalized += options.app + '\n' + | |
(options.dlg || '') + '\n'; | |
} | |
return normalized; | |
}; | |
exports.calculatePayloadHash = function (payload, algorithm, contentType) { | |
var hash = exports.initializePayloadHash(algorithm, contentType); | |
hash.update(payload || ''); | |
return exports.finalizePayloadHash(hash); | |
}; | |
exports.initializePayloadHash = function (algorithm, contentType) { | |
var hash = Crypto.createHash(algorithm); | |
hash.update('hawk.' + exports.headerVersion + '.payload\n'); | |
hash.update(Utils.parseContentType(contentType) + '\n'); | |
return hash; | |
}; | |
exports.finalizePayloadHash = function (hash) { | |
hash.update('\n'); | |
return hash.digest('base64'); | |
}; | |
exports.calculateTsMac = function (ts, credentials) { | |
var hmac = Crypto.createHmac(credentials.algorithm, credentials.key); | |
hmac.update('hawk.' + exports.headerVersion + '.ts\n' + ts + '\n'); | |
return hmac.digest('base64'); | |
}; | |
exports.timestampMessage = function (credentials, localtimeOffsetMsec) { | |
var now = Utils.nowSecs(localtimeOffsetMsec); | |
var tsm = exports.calculateTsMac(now, credentials); | |
return { ts: now, tsm: tsm }; | |
}; |