[6a3a178] | 1 | 'use strict'
|
---|
| 2 |
|
---|
| 3 | var crypto = require('crypto')
|
---|
| 4 |
|
---|
| 5 | function randomString (size) {
|
---|
| 6 | var bits = (size + 1) * 6
|
---|
| 7 | var buffer = crypto.randomBytes(Math.ceil(bits / 8))
|
---|
| 8 | var string = buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
|
---|
| 9 | return string.slice(0, size)
|
---|
| 10 | }
|
---|
| 11 |
|
---|
| 12 | function calculatePayloadHash (payload, algorithm, contentType) {
|
---|
| 13 | var hash = crypto.createHash(algorithm)
|
---|
| 14 | hash.update('hawk.1.payload\n')
|
---|
| 15 | hash.update((contentType ? contentType.split(';')[0].trim().toLowerCase() : '') + '\n')
|
---|
| 16 | hash.update(payload || '')
|
---|
| 17 | hash.update('\n')
|
---|
| 18 | return hash.digest('base64')
|
---|
| 19 | }
|
---|
| 20 |
|
---|
| 21 | exports.calculateMac = function (credentials, opts) {
|
---|
| 22 | var normalized = 'hawk.1.header\n' +
|
---|
| 23 | opts.ts + '\n' +
|
---|
| 24 | opts.nonce + '\n' +
|
---|
| 25 | (opts.method || '').toUpperCase() + '\n' +
|
---|
| 26 | opts.resource + '\n' +
|
---|
| 27 | opts.host.toLowerCase() + '\n' +
|
---|
| 28 | opts.port + '\n' +
|
---|
| 29 | (opts.hash || '') + '\n'
|
---|
| 30 |
|
---|
| 31 | if (opts.ext) {
|
---|
| 32 | normalized = normalized + opts.ext.replace('\\', '\\\\').replace('\n', '\\n')
|
---|
| 33 | }
|
---|
| 34 |
|
---|
| 35 | normalized = normalized + '\n'
|
---|
| 36 |
|
---|
| 37 | if (opts.app) {
|
---|
| 38 | normalized = normalized + opts.app + '\n' + (opts.dlg || '') + '\n'
|
---|
| 39 | }
|
---|
| 40 |
|
---|
| 41 | var hmac = crypto.createHmac(credentials.algorithm, credentials.key).update(normalized)
|
---|
| 42 | var digest = hmac.digest('base64')
|
---|
| 43 | return digest
|
---|
| 44 | }
|
---|
| 45 |
|
---|
| 46 | exports.header = function (uri, method, opts) {
|
---|
| 47 | var timestamp = opts.timestamp || Math.floor((Date.now() + (opts.localtimeOffsetMsec || 0)) / 1000)
|
---|
| 48 | var credentials = opts.credentials
|
---|
| 49 | if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
|
---|
| 50 | return ''
|
---|
| 51 | }
|
---|
| 52 |
|
---|
| 53 | if (['sha1', 'sha256'].indexOf(credentials.algorithm) === -1) {
|
---|
| 54 | return ''
|
---|
| 55 | }
|
---|
| 56 |
|
---|
| 57 | var artifacts = {
|
---|
| 58 | ts: timestamp,
|
---|
| 59 | nonce: opts.nonce || randomString(6),
|
---|
| 60 | method: method,
|
---|
| 61 | resource: uri.pathname + (uri.search || ''),
|
---|
| 62 | host: uri.hostname,
|
---|
| 63 | port: uri.port || (uri.protocol === 'http:' ? 80 : 443),
|
---|
| 64 | hash: opts.hash,
|
---|
| 65 | ext: opts.ext,
|
---|
| 66 | app: opts.app,
|
---|
| 67 | dlg: opts.dlg
|
---|
| 68 | }
|
---|
| 69 |
|
---|
| 70 | if (!artifacts.hash && (opts.payload || opts.payload === '')) {
|
---|
| 71 | artifacts.hash = calculatePayloadHash(opts.payload, credentials.algorithm, opts.contentType)
|
---|
| 72 | }
|
---|
| 73 |
|
---|
| 74 | var mac = exports.calculateMac(credentials, artifacts)
|
---|
| 75 |
|
---|
| 76 | var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''
|
---|
| 77 | var header = 'Hawk id="' + credentials.id +
|
---|
| 78 | '", ts="' + artifacts.ts +
|
---|
| 79 | '", nonce="' + artifacts.nonce +
|
---|
| 80 | (artifacts.hash ? '", hash="' + artifacts.hash : '') +
|
---|
| 81 | (hasExt ? '", ext="' + artifacts.ext.replace(/\\/g, '\\\\').replace(/"/g, '\\"') : '') +
|
---|
| 82 | '", mac="' + mac + '"'
|
---|
| 83 |
|
---|
| 84 | if (artifacts.app) {
|
---|
| 85 | header = header + ', app="' + artifacts.app + (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"'
|
---|
| 86 | }
|
---|
| 87 |
|
---|
| 88 | return header
|
---|
| 89 | }
|
---|