You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

89 lines
2.7 KiB

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