1
0
mirror of https://github.com/S2-/gitlit synced 2025-08-04 05:10:05 +02:00

initial commit

This commit is contained in:
s2
2018-05-18 10:48:24 +02:00
commit f96e61fd63
1674 changed files with 277039 additions and 0 deletions

653
app/node_modules/hawk/lib/browser.js generated vendored Normal file
View File

@@ -0,0 +1,653 @@
'use strict';
/*
HTTP Hawk Authentication Scheme
Copyright (c) 2012-2016, Eran Hammer <eran@hammer.io>
BSD Licensed
*/
// Declare namespace
const hawk = {
internals: {}
};
hawk.client = {
// Generate an Authorization header for a given request
/*
uri: 'http://example.com/resource?a=b' or object generated by hawk.utils.parseUri()
method: HTTP verb (e.g. 'GET', 'POST')
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
timestamp: Date.now() / 1000, // A pre-calculated timestamp in seconds
nonce: '2334f34f', // A pre-generated nonce
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)
contentType: 'application/json', // Payload content-type (ignored if hash provided)
hash: 'U4MKKSmiVxk37JCCrAVIjV=', // Pre-calculated payload hash
app: '24s23423f34dx', // Oz application id
dlg: '234sz34tww3sd' // Oz delegated-by application id
}
*/
header: function (uri, method, options) {
const result = {
field: '',
artifacts: {}
};
// Validate inputs
if (!uri || (typeof uri !== 'string' && typeof uri !== 'object') ||
!method || typeof method !== 'string' ||
!options || typeof options !== 'object') {
result.err = 'Invalid argument type';
return result;
}
// Application time
const timestamp = options.timestamp || hawk.utils.nowSec(options.localtimeOffsetMsec);
// Validate credentials
const credentials = options.credentials;
if (!credentials ||
!credentials.id ||
!credentials.key ||
!credentials.algorithm) {
result.err = 'Invalid credentials object';
return result;
}
if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
result.err = 'Unknown algorithm';
return result;
}
// Parse URI
if (typeof uri === 'string') {
uri = hawk.utils.parseUri(uri);
}
// Calculate signature
const artifacts = {
ts: timestamp,
nonce: options.nonce || hawk.utils.randomString(6),
method,
resource: uri.resource,
host: uri.host,
port: uri.port,
hash: options.hash,
ext: options.ext,
app: options.app,
dlg: options.dlg
};
result.artifacts = artifacts;
// Calculate payload hash
if (!artifacts.hash &&
(options.payload || options.payload === '')) {
artifacts.hash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
}
const mac = hawk.crypto.calculateMac('header', credentials, artifacts);
// Construct header
const hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
let header = 'Hawk id="' + credentials.id +
'", ts="' + artifacts.ts +
'", nonce="' + artifacts.nonce +
(artifacts.hash ? '", hash="' + artifacts.hash : '') +
(hasExt ? '", ext="' + hawk.utils.escapeHeaderAttribute(artifacts.ext) : '') +
'", mac="' + mac + '"';
if (artifacts.app) {
header += ', app="' + artifacts.app +
(artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';
}
result.field = header;
return result;
},
// Generate a bewit value for a given URI
/*
uri: 'http://example.com/resource?a=b'
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
ttlSec: 60 * 60, // TTL in seconds
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
localtimeOffsetMsec: 400 // Time offset to sync with server time
};
*/
bewit: function (uri, options) {
// Validate inputs
if (!uri ||
(typeof uri !== 'string') ||
!options ||
typeof options !== 'object' ||
!options.ttlSec) {
return '';
}
options.ext = (options.ext === null || options.ext === undefined ? '' : options.ext); // Zero is valid value
// Application time
const now = hawk.utils.nowSec(options.localtimeOffsetMsec);
// Validate credentials
const credentials = options.credentials;
if (!credentials ||
!credentials.id ||
!credentials.key ||
!credentials.algorithm) {
return '';
}
if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return '';
}
// Parse URI
uri = hawk.utils.parseUri(uri);
// Calculate signature
const exp = now + options.ttlSec;
const mac = hawk.crypto.calculateMac('bewit', credentials, {
ts: exp,
nonce: '',
method: 'GET',
resource: uri.resource, // Maintain trailing '?' and query params
host: uri.host,
port: uri.port,
ext: options.ext
});
// Construct bewit: id\exp\mac\ext
const bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;
return hawk.utils.base64urlEncode(bewit);
},
// Validate server response
/*
request: object created via 'new XMLHttpRequest()' after response received or fetch API 'Response'
artifacts: object received from header().artifacts
options: {
payload: optional payload received
required: specifies if a Server-Authorization header is required. Defaults to 'false'
}
*/
authenticate: function (request, credentials, artifacts, options) {
options = options || {};
const getHeader = function (name) {
// Fetch API or plain headers
if (request.headers) {
return (typeof request.headers.get === 'function' ? request.headers.get(name) : request.headers[name]);
}
// XMLHttpRequest
return (request.getResponseHeader ? request.getResponseHeader(name) : request.getHeader(name));
};
const wwwAuthenticate = getHeader('www-authenticate');
if (wwwAuthenticate) {
// Parse HTTP WWW-Authenticate header
const wwwAttributes = hawk.utils.parseAuthorizationHeader(wwwAuthenticate, ['ts', 'tsm', 'error']);
if (!wwwAttributes) {
return false;
}
if (wwwAttributes.ts) {
const tsm = hawk.crypto.calculateTsMac(wwwAttributes.ts, credentials);
if (tsm !== wwwAttributes.tsm) {
return false;
}
hawk.utils.setNtpSecOffset(wwwAttributes.ts - Math.floor(Date.now() / 1000)); // Keep offset at 1 second precision
}
}
// Parse HTTP Server-Authorization header
const serverAuthorization = getHeader('server-authorization');
if (!serverAuthorization &&
!options.required) {
return true;
}
const attributes = hawk.utils.parseAuthorizationHeader(serverAuthorization, ['mac', 'ext', 'hash']);
if (!attributes) {
return false;
}
const modArtifacts = {
ts: artifacts.ts,
nonce: artifacts.nonce,
method: artifacts.method,
resource: artifacts.resource,
host: artifacts.host,
port: artifacts.port,
hash: attributes.hash,
ext: attributes.ext,
app: artifacts.app,
dlg: artifacts.dlg
};
const mac = hawk.crypto.calculateMac('response', credentials, modArtifacts);
if (mac !== attributes.mac) {
return false;
}
if (!options.payload &&
options.payload !== '') {
return true;
}
if (!attributes.hash) {
return false;
}
const calculatedHash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, getHeader('content-type'));
return (calculatedHash === attributes.hash);
},
message: function (host, port, message, options) {
// Validate inputs
if (!host || typeof host !== 'string' ||
!port || typeof port !== 'number' ||
message === null || message === undefined || typeof message !== 'string' ||
!options || typeof options !== 'object') {
return null;
}
// Application time
const timestamp = options.timestamp || hawk.utils.nowSec(options.localtimeOffsetMsec);
// Validate credentials
const credentials = options.credentials;
if (!credentials ||
!credentials.id ||
!credentials.key ||
!credentials.algorithm) {
// Invalid credential object
return null;
}
if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return null;
}
// Calculate signature
const artifacts = {
ts: timestamp,
nonce: options.nonce || hawk.utils.randomString(6),
host,
port,
hash: hawk.crypto.calculatePayloadHash(message, credentials.algorithm)
};
// Construct authorization
const result = {
id: credentials.id,
ts: artifacts.ts,
nonce: artifacts.nonce,
hash: artifacts.hash,
mac: hawk.crypto.calculateMac('message', credentials, artifacts)
};
return result;
},
authenticateTimestamp: function (message, credentials, updateClock) { // updateClock defaults to true
const tsm = hawk.crypto.calculateTsMac(message.ts, credentials);
if (tsm !== message.tsm) {
return false;
}
if (updateClock !== false) {
hawk.utils.setNtpSecOffset(message.ts - Math.floor(Date.now() / 1000)); // Keep offset at 1 second precision
}
return true;
}
};
hawk.crypto = {
headerVersion: '1',
algorithms: ['sha1', 'sha256'],
calculateMac: function (type, credentials, options) {
const normalized = hawk.crypto.generateNormalizedString(type, options);
const hmac = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()](normalized, credentials.key);
return hmac.toString(CryptoJS.enc.Base64);
},
generateNormalizedString: function (type, options) {
let normalized = 'hawk.' + hawk.crypto.headerVersion + '.' + type + '\n' +
options.ts + '\n' +
options.nonce + '\n' +
(options.method || '').toUpperCase() + '\n' +
(options.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;
},
calculatePayloadHash: function (payload, algorithm, contentType) {
const hash = CryptoJS.algo[algorithm.toUpperCase()].create();
hash.update('hawk.' + hawk.crypto.headerVersion + '.payload\n');
hash.update(hawk.utils.parseContentType(contentType) + '\n');
hash.update(payload);
hash.update('\n');
return hash.finalize().toString(CryptoJS.enc.Base64);
},
calculateTsMac: function (ts, credentials) {
const hash = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()]('hawk.' + hawk.crypto.headerVersion + '.ts\n' + ts + '\n', credentials.key);
return hash.toString(CryptoJS.enc.Base64);
}
};
// localStorage compatible interface
hawk.internals.LocalStorage = function () {
this._cache = {};
this.length = 0;
this.getItem = function (key) {
return this._cache.hasOwnProperty(key) ? String(this._cache[key]) : null;
};
this.setItem = function (key, value) {
this._cache[key] = String(value);
this.length = Object.keys(this._cache).length;
};
this.removeItem = function (key) {
delete this._cache[key];
this.length = Object.keys(this._cache).length;
};
this.clear = function () {
this._cache = {};
this.length = 0;
};
this.key = function (i) {
return Object.keys(this._cache)[i || 0];
};
};
hawk.utils = {
storage: new hawk.internals.LocalStorage(),
setStorage: function (storage) {
const ntpOffset = hawk.utils.storage.getItem('hawk_ntp_offset');
hawk.utils.storage = storage;
if (ntpOffset) {
hawk.utils.setNtpSecOffset(ntpOffset);
}
},
setNtpSecOffset: function (offset) {
try {
hawk.utils.storage.setItem('hawk_ntp_offset', offset);
}
catch (err) {
console.error('[hawk] could not write to storage.');
console.error(err);
}
},
getNtpSecOffset: function () {
const offset = hawk.utils.storage.getItem('hawk_ntp_offset');
if (!offset) {
return 0;
}
return parseInt(offset, 10);
},
now: function (localtimeOffsetMsec) {
return Date.now() + (localtimeOffsetMsec || 0) + (hawk.utils.getNtpSecOffset() * 1000);
},
nowSec: function (localtimeOffsetMsec) {
return Math.floor(hawk.utils.now(localtimeOffsetMsec) / 1000);
},
escapeHeaderAttribute: function (attribute) {
return attribute.replace(/\\/g, '\\\\').replace(/\"/g, '\\"');
},
parseContentType: function (header) {
if (!header) {
return '';
}
return header.split(';')[0].replace(/^\s+|\s+$/g, '').toLowerCase();
},
parseAuthorizationHeader: function (header, keys) {
if (!header) {
return null;
}
const headerParts = header.match(/^(\w+)(?:\s+(.*))?$/); // Header: scheme[ something]
if (!headerParts) {
return null;
}
const scheme = headerParts[1];
if (scheme.toLowerCase() !== 'hawk') {
return null;
}
const attributesString = headerParts[2];
if (!attributesString) {
return null;
}
const attributes = {};
const verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, ($0, $1, $2) => {
// Check valid attribute names
if (keys.indexOf($1) === -1) {
return;
}
// Allowed attribute value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9
if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) {
return;
}
// Check for duplicates
if (attributes.hasOwnProperty($1)) {
return;
}
attributes[$1] = $2;
return '';
});
if (verify !== '') {
return null;
}
return attributes;
},
randomString: function (size) {
const randomSource = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const len = randomSource.length;
const result = [];
for (let i = 0; i < size; ++i) {
result[i] = randomSource[Math.floor(Math.random() * len)];
}
return result.join('');
},
// 1 2 3 4
uriRegex: /^([^:]+)\:\/\/(?:[^@/]*@)?([^\/:]+)(?:\:(\d+))?([^#]*)(?:#.*)?$/, // scheme://credentials@host:port/resource#fragment
parseUri: function (input) {
const parts = input.match(hawk.utils.uriRegex);
if (!parts) {
return { host: '', port: '', resource: '' };
}
const scheme = parts[1].toLowerCase();
const uri = {
host: parts[2],
port: parts[3] || (scheme === 'http' ? '80' : (scheme === 'https' ? '443' : '')),
resource: parts[4]
};
return uri;
},
base64urlEncode: function (value) {
const wordArray = CryptoJS.enc.Utf8.parse(value);
const encoded = CryptoJS.enc.Base64.stringify(wordArray);
return encoded.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
}
};
// $lab:coverage:off$
/* eslint-disable */
// Based on: Crypto-JS v3.1.2
// Copyright (c) 2009-2013, Jeff Mott. All rights reserved.
// http://code.google.com/p/crypto-js/
// http://code.google.com/p/crypto-js/wiki/License
var CryptoJS = CryptoJS || function (h, r) { var k = {}, l = k.lib = {}, n = function () { }, f = l.Base = { extend: function (a) { n.prototype = this; var b = new n; a && b.mixIn(a); b.hasOwnProperty("init") || (b.init = function () { b.$super.init.apply(this, arguments) }); b.init.prototype = b; b.$super = this; return b }, create: function () { var a = this.extend(); a.init.apply(a, arguments); return a }, init: function () { }, mixIn: function (a) { for (let b in a) a.hasOwnProperty(b) && (this[b] = a[b]); a.hasOwnProperty("toString") && (this.toString = a.toString) }, clone: function () { return this.init.prototype.extend(this) } }, j = l.WordArray = f.extend({ init: function (a, b) { a = this.words = a || []; this.sigBytes = b != r ? b : 4 * a.length }, toString: function (a) { return (a || s).stringify(this) }, concat: function (a) { var b = this.words, d = a.words, c = this.sigBytes; a = a.sigBytes; this.clamp(); if (c % 4) for (let e = 0; e < a; e++) b[c + e >>> 2] |= (d[e >>> 2] >>> 24 - 8 * (e % 4) & 255) << 24 - 8 * ((c + e) % 4); else if (65535 < d.length) for (let e = 0; e < a; e += 4) b[c + e >>> 2] = d[e >>> 2]; else b.push.apply(b, d); this.sigBytes += a; return this }, clamp: function () { var a = this.words, b = this.sigBytes; a[b >>> 2] &= 4294967295 << 32 - 8 * (b % 4); a.length = h.ceil(b / 4) }, clone: function () { var a = f.clone.call(this); a.words = this.words.slice(0); return a }, random: function (a) { for (let b = [], d = 0; d < a; d += 4) b.push(4294967296 * h.random() | 0); return new j.init(b, a) } }), m = k.enc = {}, s = m.Hex = { stringify: function (a) { var b = a.words; a = a.sigBytes; for (var d = [], c = 0; c < a; c++) { var e = b[c >>> 2] >>> 24 - 8 * (c % 4) & 255; d.push((e >>> 4).toString(16)); d.push((e & 15).toString(16)) } return d.join("") }, parse: function (a) { for (var b = a.length, d = [], c = 0; c < b; c += 2) d[c >>> 3] |= parseInt(a.substr(c, 2), 16) << 24 - 4 * (c % 8); return new j.init(d, b / 2) } }, p = m.Latin1 = { stringify: function (a) { var b = a.words; a = a.sigBytes; for (var d = [], c = 0; c < a; c++) d.push(String.fromCharCode(b[c >>> 2] >>> 24 - 8 * (c % 4) & 255)); return d.join("") }, parse: function (a) { for (var b = a.length, d = [], c = 0; c < b; c++) d[c >>> 2] |= (a.charCodeAt(c) & 255) << 24 - 8 * (c % 4); return new j.init(d, b) } }, t = m.Utf8 = { stringify: function (a) { try { return decodeURIComponent(escape(p.stringify(a))) } catch (b) { throw Error("Malformed UTF-8 data"); } }, parse: function (a) { return p.parse(unescape(encodeURIComponent(a))) } }, q = l.BufferedBlockAlgorithm = f.extend({ reset: function () { this._data = new j.init; this._nDataBytes = 0 }, _append: function (a) { "string" == typeof a && (a = t.parse(a)); this._data.concat(a); this._nDataBytes += a.sigBytes }, _process: function (a) { var b = this._data, d = b.words, c = b.sigBytes, e = this.blockSize, f = c / (4 * e), f = a ? h.ceil(f) : h.max((f | 0) - this._minBufferSize, 0); a = f * e; c = h.min(4 * a, c); if (a) { for (var g = 0; g < a; g += e) this._doProcessBlock(d, g); g = d.splice(0, a); b.sigBytes -= c } return new j.init(g, c) }, clone: function () { var a = f.clone.call(this); a._data = this._data.clone(); return a }, _minBufferSize: 0 }); l.Hasher = q.extend({ cfg: f.extend(), init: function (a) { this.cfg = this.cfg.extend(a); this.reset() }, reset: function () { q.reset.call(this); this._doReset() }, update: function (a) { this._append(a); this._process(); return this }, finalize: function (a) { a && this._append(a); return this._doFinalize() }, blockSize: 16, _createHelper: function (a) { return function (b, d) { return (new a.init(d)).finalize(b) } }, _createHmacHelper: function (a) { return function (b, d) { return (new u.HMAC.init(a, d)).finalize(b) } } }); var u = k.algo = {}; return k }(Math);
(() => { var k = CryptoJS, b = k.lib, m = b.WordArray, l = b.Hasher, d = [], b = k.algo.SHA1 = l.extend({ _doReset: function () { this._hash = new m.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520]) }, _doProcessBlock: function (n, p) { for (var a = this._hash.words, e = a[0], f = a[1], h = a[2], j = a[3], b = a[4], c = 0; 80 > c; c++) { if (16 > c) d[c] = n[p + c] | 0; else { var g = d[c - 3] ^ d[c - 8] ^ d[c - 14] ^ d[c - 16]; d[c] = g << 1 | g >>> 31 } g = (e << 5 | e >>> 27) + b + d[c]; g = 20 > c ? g + ((f & h | ~f & j) + 1518500249) : 40 > c ? g + ((f ^ h ^ j) + 1859775393) : 60 > c ? g + ((f & h | f & j | h & j) - 1894007588) : g + ((f ^ h ^ j) - 899497514); b = j; j = h; h = f << 30 | f >>> 2; f = e; e = g } a[0] = a[0] + e | 0; a[1] = a[1] + f | 0; a[2] = a[2] + h | 0; a[3] = a[3] + j | 0; a[4] = a[4] + b | 0 }, _doFinalize: function () { var b = this._data, d = b.words, a = 8 * this._nDataBytes, e = 8 * b.sigBytes; d[e >>> 5] |= 128 << 24 - e % 32; d[(e + 64 >>> 9 << 4) + 14] = Math.floor(a / 4294967296); d[(e + 64 >>> 9 << 4) + 15] = a; b.sigBytes = 4 * d.length; this._process(); return this._hash }, clone: function () { var b = l.clone.call(this); b._hash = this._hash.clone(); return b } }); k.SHA1 = l._createHelper(b); k.HmacSHA1 = l._createHmacHelper(b) })();
(function (k) { for (var g = CryptoJS, h = g.lib, v = h.WordArray, j = h.Hasher, h = g.algo, s = [], t = [], u = function (q) { return 4294967296 * (q - (q | 0)) | 0 }, l = 2, b = 0; 64 > b;) { var d; a: { d = l; for (var w = k.sqrt(d), r = 2; r <= w; r++) if (!(d % r)) { d = !1; break a } d = !0 } d && (8 > b && (s[b] = u(k.pow(l, 0.5))), t[b] = u(k.pow(l, 1 / 3)), b++); l++ } var n = [], h = h.SHA256 = j.extend({ _doReset: function () { this._hash = new v.init(s.slice(0)) }, _doProcessBlock: function (q, h) { for (var a = this._hash.words, c = a[0], d = a[1], b = a[2], k = a[3], f = a[4], g = a[5], j = a[6], l = a[7], e = 0; 64 > e; e++) { if (16 > e) n[e] = q[h + e] | 0; else { var m = n[e - 15], p = n[e - 2]; n[e] = ((m << 25 | m >>> 7) ^ (m << 14 | m >>> 18) ^ m >>> 3) + n[e - 7] + ((p << 15 | p >>> 17) ^ (p << 13 | p >>> 19) ^ p >>> 10) + n[e - 16] } m = l + ((f << 26 | f >>> 6) ^ (f << 21 | f >>> 11) ^ (f << 7 | f >>> 25)) + (f & g ^ ~f & j) + t[e] + n[e]; p = ((c << 30 | c >>> 2) ^ (c << 19 | c >>> 13) ^ (c << 10 | c >>> 22)) + (c & d ^ c & b ^ d & b); l = j; j = g; g = f; f = k + m | 0; k = b; b = d; d = c; c = m + p | 0 } a[0] = a[0] + c | 0; a[1] = a[1] + d | 0; a[2] = a[2] + b | 0; a[3] = a[3] + k | 0; a[4] = a[4] + f | 0; a[5] = a[5] + g | 0; a[6] = a[6] + j | 0; a[7] = a[7] + l | 0 }, _doFinalize: function () { var d = this._data, b = d.words, a = 8 * this._nDataBytes, c = 8 * d.sigBytes; b[c >>> 5] |= 128 << 24 - c % 32; b[(c + 64 >>> 9 << 4) + 14] = k.floor(a / 4294967296); b[(c + 64 >>> 9 << 4) + 15] = a; d.sigBytes = 4 * b.length; this._process(); return this._hash }, clone: function () { var b = j.clone.call(this); b._hash = this._hash.clone(); return b } }); g.SHA256 = j._createHelper(h); g.HmacSHA256 = j._createHmacHelper(h) })(Math);
(() => { var c = CryptoJS, k = c.enc.Utf8; c.algo.HMAC = c.lib.Base.extend({ init: function (a, b) { a = this._hasher = new a.init; "string" == typeof b && (b = k.parse(b)); var c = a.blockSize, e = 4 * c; b.sigBytes > e && (b = a.finalize(b)); b.clamp(); for (var f = this._oKey = b.clone(), g = this._iKey = b.clone(), h = f.words, j = g.words, d = 0; d < c; d++) h[d] ^= 1549556828, j[d] ^= 909522486; f.sigBytes = g.sigBytes = e; this.reset() }, reset: function () { var a = this._hasher; a.reset(); a.update(this._iKey) }, update: function (a) { this._hasher.update(a); return this }, finalize: function (a) { var b = this._hasher; a = b.finalize(a); b.reset(); return b.finalize(this._oKey.clone().concat(a)) } }) })();
(() => { var h = CryptoJS, j = h.lib.WordArray; h.enc.Base64 = { stringify: function (b) { var e = b.words, f = b.sigBytes, c = this._map; b.clamp(); b = []; for (var a = 0; a < f; a += 3) for (var d = (e[a >>> 2] >>> 24 - 8 * (a % 4) & 255) << 16 | (e[a + 1 >>> 2] >>> 24 - 8 * ((a + 1) % 4) & 255) << 8 | e[a + 2 >>> 2] >>> 24 - 8 * ((a + 2) % 4) & 255, g = 0; 4 > g && a + 0.75 * g < f; g++) b.push(c.charAt(d >>> 6 * (3 - g) & 63)); if (e = c.charAt(64)) for (; b.length % 4;) b.push(e); return b.join("") }, parse: function (b) { var e = b.length, f = this._map, c = f.charAt(64); c && (c = b.indexOf(c), -1 != c && (e = c)); for (var c = [], a = 0, d = 0; d < e; d++) if (d % 4) { var g = f.indexOf(b.charAt(d - 1)) << 2 * (d % 4), h = f.indexOf(b.charAt(d)) >>> 6 - 2 * (d % 4); c[a >>> 2] |= (g | h) << 24 - 8 * (a % 4); a++ } return j.create(c, a) }, _map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" } })();
hawk.crypto.utils = CryptoJS;
// Export if used as a module
if (typeof module !== 'undefined' && module.exports) {
module.exports = hawk;
}
/* eslint-enable */
// $lab:coverage:on$

394
app/node_modules/hawk/lib/client.js generated vendored Normal file
View File

@@ -0,0 +1,394 @@
'use strict';
// Load modules
const Url = require('url');
const Hoek = require('hoek');
const Cryptiles = require('cryptiles');
const Crypto = require('./crypto');
const Utils = require('./utils');
// Declare internals
const internals = {};
// Generate an Authorization header for a given request
/*
uri: 'http://example.com/resource?a=b' or object from Url.parse()
method: HTTP verb (e.g. 'GET', 'POST')
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
timestamp: Date.now() / 1000, // A pre-calculated timestamp in seconds
nonce: '2334f34f', // A pre-generated nonce
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)
contentType: 'application/json', // Payload content-type (ignored if hash provided)
hash: 'U4MKKSmiVxk37JCCrAVIjV=', // Pre-calculated payload hash
app: '24s23423f34dx', // Oz application id
dlg: '234sz34tww3sd' // Oz delegated-by application id
}
*/
exports.header = function (uri, method, options) {
const result = {
field: '',
artifacts: {}
};
// Validate inputs
if (!uri || (typeof uri !== 'string' && typeof uri !== 'object') ||
!method || typeof method !== 'string' ||
!options || typeof options !== 'object') {
result.err = 'Invalid argument type';
return result;
}
// Application time
const timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);
// Validate credentials
const credentials = options.credentials;
if (!credentials ||
!credentials.id ||
!credentials.key ||
!credentials.algorithm) {
result.err = 'Invalid credential object';
return result;
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
result.err = 'Unknown algorithm';
return result;
}
// Parse URI
if (typeof uri === 'string') {
uri = Url.parse(uri);
}
// Calculate signature
const artifacts = {
ts: timestamp,
nonce: options.nonce || Cryptiles.randomString(6),
method,
resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'
host: uri.hostname,
port: uri.port || (uri.protocol === 'http:' ? 80 : 443),
hash: options.hash,
ext: options.ext,
app: options.app,
dlg: options.dlg
};
result.artifacts = artifacts;
// Calculate payload hash
if (!artifacts.hash &&
(options.payload || options.payload === '')) {
artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
}
const mac = Crypto.calculateMac('header', credentials, artifacts);
// Construct header
const hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
let header = 'Hawk id="' + credentials.id +
'", ts="' + artifacts.ts +
'", nonce="' + artifacts.nonce +
(artifacts.hash ? '", hash="' + artifacts.hash : '') +
(hasExt ? '", ext="' + Hoek.escapeHeaderAttribute(artifacts.ext) : '') +
'", mac="' + mac + '"';
if (artifacts.app) {
header = header + ', app="' + artifacts.app +
(artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';
}
result.field = header;
return result;
};
// Validate server response
/*
res: node's response object
artifacts: object received from header().artifacts
options: {
payload: optional payload received
required: specifies if a Server-Authorization header is required. Defaults to 'false'
}
*/
exports.authenticate = function (res, credentials, artifacts, options, callback) {
artifacts = Hoek.clone(artifacts);
options = options || {};
let wwwAttributes = null;
let serverAuthAttributes = null;
const finalize = function (err) {
if (callback) {
const headers = {
'www-authenticate': wwwAttributes,
'server-authorization': serverAuthAttributes
};
return callback(err, headers);
}
return !err;
};
if (res.headers['www-authenticate']) {
// Parse HTTP WWW-Authenticate header
wwwAttributes = Utils.parseAuthorizationHeader(res.headers['www-authenticate'], ['ts', 'tsm', 'error']);
if (wwwAttributes instanceof Error) {
wwwAttributes = null;
return finalize(new Error('Invalid WWW-Authenticate header'));
}
// Validate server timestamp (not used to update clock since it is done via the SNPT client)
if (wwwAttributes.ts) {
const tsm = Crypto.calculateTsMac(wwwAttributes.ts, credentials);
if (tsm !== wwwAttributes.tsm) {
return finalize(new Error('Invalid server timestamp hash'));
}
}
}
// Parse HTTP Server-Authorization header
if (!res.headers['server-authorization'] &&
!options.required) {
return finalize();
}
serverAuthAttributes = Utils.parseAuthorizationHeader(res.headers['server-authorization'], ['mac', 'ext', 'hash']);
if (serverAuthAttributes instanceof Error) {
serverAuthAttributes = null;
return finalize(new Error('Invalid Server-Authorization header'));
}
artifacts.ext = serverAuthAttributes.ext;
artifacts.hash = serverAuthAttributes.hash;
const mac = Crypto.calculateMac('response', credentials, artifacts);
if (mac !== serverAuthAttributes.mac) {
return finalize(new Error('Bad response mac'));
}
if (!options.payload &&
options.payload !== '') {
return finalize();
}
if (!serverAuthAttributes.hash) {
return finalize(new Error('Missing response hash attribute'));
}
const calculatedHash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, res.headers['content-type']);
if (calculatedHash !== serverAuthAttributes.hash) {
return finalize(new Error('Bad response payload mac'));
}
return finalize();
};
// Generate a bewit value for a given URI
/*
uri: 'http://example.com/resource?a=b' or object from Url.parse()
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
ttlSec: 60 * 60, // TTL in seconds
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
localtimeOffsetMsec: 400 // Time offset to sync with server time
};
*/
exports.getBewit = function (uri, options) {
// Validate inputs
if (!uri ||
(typeof uri !== 'string' && typeof uri !== 'object') ||
!options ||
typeof options !== 'object' ||
!options.ttlSec) {
return '';
}
options.ext = (options.ext === null || options.ext === undefined ? '' : options.ext); // Zero is valid value
// Application time
const now = Utils.now(options.localtimeOffsetMsec);
// Validate credentials
const credentials = options.credentials;
if (!credentials ||
!credentials.id ||
!credentials.key ||
!credentials.algorithm) {
return '';
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return '';
}
// Parse URI
if (typeof uri === 'string') {
uri = Url.parse(uri);
}
// Calculate signature
const exp = Math.floor(now / 1000) + options.ttlSec;
const mac = Crypto.calculateMac('bewit', credentials, {
ts: exp,
nonce: '',
method: 'GET',
resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'
host: uri.hostname,
port: uri.port || (uri.protocol === 'http:' ? 80 : 443),
ext: options.ext
});
// Construct bewit: id\exp\mac\ext
const bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;
return Hoek.base64urlEncode(bewit);
};
// Generate an authorization string for a message
/*
host: 'example.com',
port: 8000,
message: '{"some":"payload"}', // UTF-8 encoded string for body hash generation
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
// Optional
timestamp: Date.now() / 1000, // A pre-calculated timestamp in seconds
nonce: '2334f34f', // A pre-generated nonce
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
}
*/
exports.message = function (host, port, message, options) {
// Validate inputs
if (!host || typeof host !== 'string' ||
!port || typeof port !== 'number' ||
message === null || message === undefined || typeof message !== 'string' ||
!options || typeof options !== 'object') {
return null;
}
// Application time
const timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);
// Validate credentials
const credentials = options.credentials;
if (!credentials ||
!credentials.id ||
!credentials.key ||
!credentials.algorithm) {
// Invalid credential object
return null;
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return null;
}
// Calculate signature
const artifacts = {
ts: timestamp,
nonce: options.nonce || Cryptiles.randomString(6),
host,
port,
hash: Crypto.calculatePayloadHash(message, credentials.algorithm)
};
// Construct authorization
const result = {
id: credentials.id,
ts: artifacts.ts,
nonce: artifacts.nonce,
hash: artifacts.hash,
mac: Crypto.calculateMac('message', credentials, artifacts)
};
return result;
};

128
app/node_modules/hawk/lib/crypto.js generated vendored Normal file
View File

@@ -0,0 +1,128 @@
'use strict';
// Load modules
const Crypto = require('crypto');
const Url = require('url');
const Utils = require('./utils');
// Declare internals
const 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) {
const normalized = exports.generateNormalizedString(type, options);
const hmac = Crypto.createHmac(credentials.algorithm, credentials.key).update(normalized);
const digest = hmac.digest('base64');
return digest;
};
exports.generateNormalizedString = function (type, options) {
let resource = options.resource || '';
if (resource &&
resource[0] !== '/') {
const url = Url.parse(resource, false);
resource = url.path; // Includes query
}
let 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 = normalized + options.ext.replace('\\', '\\\\').replace('\n', '\\n');
}
normalized = normalized + '\n';
if (options.app) {
normalized = normalized + options.app + '\n' +
(options.dlg || '') + '\n';
}
return normalized;
};
exports.calculatePayloadHash = function (payload, algorithm, contentType) {
const hash = exports.initializePayloadHash(algorithm, contentType);
hash.update(payload || '');
return exports.finalizePayloadHash(hash);
};
exports.initializePayloadHash = function (algorithm, contentType) {
const 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) {
const 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) {
const now = Utils.nowSecs(localtimeOffsetMsec);
const tsm = exports.calculateTsMac(now, credentials);
return { ts: now, tsm };
};

17
app/node_modules/hawk/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,17 @@
'use strict';
// Export sub-modules
exports.error = exports.Error = require('boom');
exports.sntp = require('sntp');
exports.server = require('./server');
exports.client = require('./client');
exports.crypto = require('./crypto');
exports.utils = require('./utils');
exports.uri = {
authenticate: exports.server.authenticateBewit,
getBewit: exports.client.getBewit
};

550
app/node_modules/hawk/lib/server.js generated vendored Normal file
View File

@@ -0,0 +1,550 @@
'use strict';
// Load modules
const Boom = require('boom');
const Hoek = require('hoek');
const Cryptiles = require('cryptiles');
const Crypto = require('./crypto');
const Utils = require('./utils');
// Declare internals
const internals = {};
// Hawk authentication
/*
req: node's HTTP request object or an object as follows:
const request = {
method: 'GET',
url: '/resource/4?a=1&b=2',
host: 'example.com',
port: 8080,
authorization: 'Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", ext="some-app-ext-data", mac="6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE="'
};
credentialsFunc: required function to lookup the set of Hawk credentials based on the provided credentials id.
The credentials include the MAC key, MAC algorithm, and other attributes (such as username)
needed by the application. This function is the equivalent of verifying the username and
password in Basic authentication.
const credentialsFunc = function (id, callback) {
// Lookup credentials in database
db.lookup(id, function (err, item) {
if (err || !item) {
return callback(err);
}
const credentials = {
// Required
key: item.key,
algorithm: item.algorithm,
// Application specific
user: item.user
};
return callback(null, credentials);
});
};
options: {
hostHeaderName: optional header field name, used to override the default 'Host' header when used
behind a cache of a proxy. Apache2 changes the value of the 'Host' header while preserving
the original (which is what the module must verify) in the 'x-forwarded-host' header field.
Only used when passed a node Http.ServerRequest object.
nonceFunc: optional nonce validation function. The function signature is function(key, nonce, ts, callback)
where 'callback' must be called using the signature function(err).
timestampSkewSec: optional number of seconds of permitted clock skew for incoming timestamps. Defaults to 60 seconds.
Provides a +/- skew which means actual allowed window is double the number of seconds.
localtimeOffsetMsec: optional local clock time offset express in a number of milliseconds (positive or negative).
Defaults to 0.
payload: optional payload for validation. The client calculates the hash value and includes it via the 'hash'
header attribute. The server always ensures the value provided has been included in the request
MAC. When this option is provided, it validates the hash value itself. Validation is done by calculating
a hash value over the entire payload (assuming it has already be normalized to the same format and
encoding used by the client to calculate the hash on request). If the payload is not available at the time
of authentication, the authenticatePayload() method can be used by passing it the credentials and
attributes.hash returned in the authenticate callback.
host: optional host name override. Only used when passed a node request object.
port: optional port override. Only used when passed a node request object.
}
callback: function (err, credentials, artifacts) { }
*/
exports.authenticate = function (req, credentialsFunc, options, callback) {
callback = Hoek.nextTick(callback);
// Default options
options.nonceFunc = options.nonceFunc || internals.nonceFunc;
options.timestampSkewSec = options.timestampSkewSec || 60; // 60 seconds
// Application time
const now = Utils.now(options.localtimeOffsetMsec); // Measure now before any other processing
// Convert node Http request object to a request configuration object
const request = Utils.parseRequest(req, options);
if (request instanceof Error) {
return callback(Boom.badRequest(request.message));
}
// Parse HTTP Authorization header
const attributes = Utils.parseAuthorizationHeader(request.authorization);
if (attributes instanceof Error) {
return callback(attributes);
}
// Construct artifacts container
const artifacts = {
method: request.method,
host: request.host,
port: request.port,
resource: request.url,
ts: attributes.ts,
nonce: attributes.nonce,
hash: attributes.hash,
ext: attributes.ext,
app: attributes.app,
dlg: attributes.dlg,
mac: attributes.mac,
id: attributes.id
};
// Verify required header attributes
if (!attributes.id ||
!attributes.ts ||
!attributes.nonce ||
!attributes.mac) {
return callback(Boom.badRequest('Missing attributes'), null, artifacts);
}
// Fetch Hawk credentials
credentialsFunc(attributes.id, (err, credentials) => {
if (err) {
return callback(err, credentials || null, artifacts);
}
if (!credentials) {
return callback(Utils.unauthorized('Unknown credentials'), null, artifacts);
}
if (!credentials.key ||
!credentials.algorithm) {
return callback(Boom.internal('Invalid credentials'), credentials, artifacts);
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return callback(Boom.internal('Unknown algorithm'), credentials, artifacts);
}
// Calculate MAC
const mac = Crypto.calculateMac('header', credentials, artifacts);
if (!Cryptiles.fixedTimeComparison(mac, attributes.mac)) {
return callback(Utils.unauthorized('Bad mac'), credentials, artifacts);
}
// Check payload hash
if (options.payload ||
options.payload === '') {
if (!attributes.hash) {
return callback(Utils.unauthorized('Missing required payload hash'), credentials, artifacts);
}
const hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, request.contentType);
if (!Cryptiles.fixedTimeComparison(hash, attributes.hash)) {
return callback(Utils.unauthorized('Bad payload hash'), credentials, artifacts);
}
}
// Check nonce
options.nonceFunc(credentials.key, attributes.nonce, attributes.ts, (err) => {
if (err) {
return callback(Utils.unauthorized('Invalid nonce'), credentials, artifacts);
}
// Check timestamp staleness
if (Math.abs((attributes.ts * 1000) - now) > (options.timestampSkewSec * 1000)) {
const tsm = Crypto.timestampMessage(credentials, options.localtimeOffsetMsec);
return callback(Utils.unauthorized('Stale timestamp', tsm), credentials, artifacts);
}
// Successful authentication
return callback(null, credentials, artifacts);
});
});
};
// Authenticate payload hash - used when payload cannot be provided during authenticate()
/*
payload: raw request payload
credentials: from authenticate callback
artifacts: from authenticate callback
contentType: req.headers['content-type']
*/
exports.authenticatePayload = function (payload, credentials, artifacts, contentType) {
const calculatedHash = Crypto.calculatePayloadHash(payload, credentials.algorithm, contentType);
return Cryptiles.fixedTimeComparison(calculatedHash, artifacts.hash);
};
// Authenticate payload hash - used when payload cannot be provided during authenticate()
/*
calculatedHash: the payload hash calculated using Crypto.calculatePayloadHash()
artifacts: from authenticate callback
*/
exports.authenticatePayloadHash = function (calculatedHash, artifacts) {
return Cryptiles.fixedTimeComparison(calculatedHash, artifacts.hash);
};
// Generate a Server-Authorization header for a given response
/*
credentials: {}, // Object received from authenticate()
artifacts: {} // Object received from authenticate(); 'mac', 'hash', and 'ext' - ignored
options: {
ext: 'application-specific', // Application specific data sent via the ext attribute
payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)
contentType: 'application/json', // Payload content-type (ignored if hash provided)
hash: 'U4MKKSmiVxk37JCCrAVIjV=' // Pre-calculated payload hash
}
*/
exports.header = function (credentials, artifacts, options) {
// Prepare inputs
options = options || {};
if (!artifacts ||
typeof artifacts !== 'object' ||
typeof options !== 'object') {
return '';
}
artifacts = Hoek.clone(artifacts);
delete artifacts.mac;
artifacts.hash = options.hash;
artifacts.ext = options.ext;
// Validate credentials
if (!credentials ||
!credentials.key ||
!credentials.algorithm) {
// Invalid credential object
return '';
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return '';
}
// Calculate payload hash
if (!artifacts.hash &&
(options.payload || options.payload === '')) {
artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
}
const mac = Crypto.calculateMac('response', credentials, artifacts);
// Construct header
let header = 'Hawk mac="' + mac + '"' +
(artifacts.hash ? ', hash="' + artifacts.hash + '"' : '');
if (artifacts.ext !== null &&
artifacts.ext !== undefined &&
artifacts.ext !== '') { // Other falsey values allowed
header = header + ', ext="' + Hoek.escapeHeaderAttribute(artifacts.ext) + '"';
}
return header;
};
/*
* Arguments and options are the same as authenticate() with the exception that the only supported options are:
* 'hostHeaderName', 'localtimeOffsetMsec', 'host', 'port'
*/
// 1 2 3 4
internals.bewitRegex = /^(\/.*)([\?&])bewit\=([^&$]*)(?:&(.+))?$/;
exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
callback = Hoek.nextTick(callback);
// Application time
const now = Utils.now(options.localtimeOffsetMsec);
// Convert node Http request object to a request configuration object
const request = Utils.parseRequest(req, options);
if (request instanceof Error) {
return callback(Boom.badRequest(request.message));
}
// Extract bewit
if (request.url.length > Utils.limits.maxMatchLength) {
return callback(Boom.badRequest('Resource path exceeds max length'));
}
const resource = request.url.match(internals.bewitRegex);
if (!resource) {
return callback(Utils.unauthorized());
}
// Bewit not empty
if (!resource[3]) {
return callback(Utils.unauthorized('Empty bewit'));
}
// Verify method is GET
if (request.method !== 'GET' &&
request.method !== 'HEAD') {
return callback(Utils.unauthorized('Invalid method'));
}
// No other authentication
if (request.authorization) {
return callback(Boom.badRequest('Multiple authentications'));
}
// Parse bewit
const bewitString = Hoek.base64urlDecode(resource[3]);
if (bewitString instanceof Error) {
return callback(Boom.badRequest('Invalid bewit encoding'));
}
// Bewit format: id\exp\mac\ext ('\' is used because it is a reserved header attribute character)
const bewitParts = bewitString.split('\\');
if (bewitParts.length !== 4) {
return callback(Boom.badRequest('Invalid bewit structure'));
}
const bewit = {
id: bewitParts[0],
exp: parseInt(bewitParts[1], 10),
mac: bewitParts[2],
ext: bewitParts[3] || ''
};
if (!bewit.id ||
!bewit.exp ||
!bewit.mac) {
return callback(Boom.badRequest('Missing bewit attributes'));
}
// Construct URL without bewit
let url = resource[1];
if (resource[4]) {
url = url + resource[2] + resource[4];
}
// Check expiration
if (bewit.exp * 1000 <= now) {
return callback(Utils.unauthorized('Access expired'), null, bewit);
}
// Fetch Hawk credentials
credentialsFunc(bewit.id, (err, credentials) => {
if (err) {
return callback(err, credentials || null, bewit.ext);
}
if (!credentials) {
return callback(Utils.unauthorized('Unknown credentials'), null, bewit);
}
if (!credentials.key ||
!credentials.algorithm) {
return callback(Boom.internal('Invalid credentials'), credentials, bewit);
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return callback(Boom.internal('Unknown algorithm'), credentials, bewit);
}
// Calculate MAC
const mac = Crypto.calculateMac('bewit', credentials, {
ts: bewit.exp,
nonce: '',
method: 'GET',
resource: url,
host: request.host,
port: request.port,
ext: bewit.ext
});
if (!Cryptiles.fixedTimeComparison(mac, bewit.mac)) {
return callback(Utils.unauthorized('Bad mac'), credentials, bewit);
}
// Successful authentication
return callback(null, credentials, bewit);
});
};
/*
* options are the same as authenticate() with the exception that the only supported options are:
* 'nonceFunc', 'timestampSkewSec', 'localtimeOffsetMsec'
*/
exports.authenticateMessage = function (host, port, message, authorization, credentialsFunc, options, callback) {
callback = Hoek.nextTick(callback);
// Default options
options.nonceFunc = options.nonceFunc || internals.nonceFunc;
options.timestampSkewSec = options.timestampSkewSec || 60; // 60 seconds
// Application time
const now = Utils.now(options.localtimeOffsetMsec); // Measure now before any other processing
// Validate authorization
if (!authorization.id ||
!authorization.ts ||
!authorization.nonce ||
!authorization.hash ||
!authorization.mac) {
return callback(Boom.badRequest('Invalid authorization'));
}
// Fetch Hawk credentials
credentialsFunc(authorization.id, (err, credentials) => {
if (err) {
return callback(err, credentials || null);
}
if (!credentials) {
return callback(Utils.unauthorized('Unknown credentials'));
}
if (!credentials.key ||
!credentials.algorithm) {
return callback(Boom.internal('Invalid credentials'), credentials);
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return callback(Boom.internal('Unknown algorithm'), credentials);
}
// Construct artifacts container
const artifacts = {
ts: authorization.ts,
nonce: authorization.nonce,
host,
port,
hash: authorization.hash
};
// Calculate MAC
const mac = Crypto.calculateMac('message', credentials, artifacts);
if (!Cryptiles.fixedTimeComparison(mac, authorization.mac)) {
return callback(Utils.unauthorized('Bad mac'), credentials);
}
// Check payload hash
const hash = Crypto.calculatePayloadHash(message, credentials.algorithm);
if (!Cryptiles.fixedTimeComparison(hash, authorization.hash)) {
return callback(Utils.unauthorized('Bad message hash'), credentials);
}
// Check nonce
options.nonceFunc(credentials.key, authorization.nonce, authorization.ts, (err) => {
if (err) {
return callback(Utils.unauthorized('Invalid nonce'), credentials);
}
// Check timestamp staleness
if (Math.abs((authorization.ts * 1000) - now) > (options.timestampSkewSec * 1000)) {
return callback(Utils.unauthorized('Stale timestamp'), credentials);
}
// Successful authentication
return callback(null, credentials);
});
});
};
internals.nonceFunc = function (key, nonce, ts, nonceCallback) {
return nonceCallback(); // No validation
};

186
app/node_modules/hawk/lib/utils.js generated vendored Normal file
View File

@@ -0,0 +1,186 @@
'use strict';
// Load modules
const Sntp = require('sntp');
const Boom = require('boom');
// Declare internals
const internals = {};
exports.version = function () {
return require('../package.json').version;
};
exports.limits = {
maxMatchLength: 4096 // Limit the length of uris and headers to avoid a DoS attack on string matching
};
// Extract host and port from request
// $1 $2
internals.hostHeaderRegex = /^(?:(?:\r\n)?\s)*((?:[^:]+)|(?:\[[^\]]+\]))(?::(\d+))?(?:(?:\r\n)?\s)*$/; // (IPv4, hostname)|(IPv6)
exports.parseHost = function (req, hostHeaderName) {
hostHeaderName = (hostHeaderName ? hostHeaderName.toLowerCase() : 'host');
const hostHeader = req.headers[hostHeaderName];
if (!hostHeader) {
return null;
}
if (hostHeader.length > exports.limits.maxMatchLength) {
return null;
}
const hostParts = hostHeader.match(internals.hostHeaderRegex);
if (!hostParts) {
return null;
}
return {
name: hostParts[1],
port: (hostParts[2] ? hostParts[2] : (req.connection && req.connection.encrypted ? 443 : 80))
};
};
// Parse Content-Type header content
exports.parseContentType = function (header) {
if (!header) {
return '';
}
return header.split(';')[0].trim().toLowerCase();
};
// Convert node's to request configuration object
exports.parseRequest = function (req, options) {
if (!req.headers) {
return req;
}
// Obtain host and port information
let host;
if (!options.host ||
!options.port) {
host = exports.parseHost(req, options.hostHeaderName);
if (!host) {
return new Error('Invalid Host header');
}
}
const request = {
method: req.method,
url: req.url,
host: options.host || host.name,
port: options.port || host.port,
authorization: req.headers.authorization,
contentType: req.headers['content-type'] || ''
};
return request;
};
exports.now = function (localtimeOffsetMsec) {
return Sntp.now() + (localtimeOffsetMsec || 0);
};
exports.nowSecs = function (localtimeOffsetMsec) {
return Math.floor(exports.now(localtimeOffsetMsec) / 1000);
};
internals.authHeaderRegex = /^(\w+)(?:\s+(.*))?$/; // Header: scheme[ something]
internals.attributeRegex = /^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/; // !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9
// Parse Hawk HTTP Authorization header
exports.parseAuthorizationHeader = function (header, keys) {
keys = keys || ['id', 'ts', 'nonce', 'hash', 'ext', 'mac', 'app', 'dlg'];
if (!header) {
return Boom.unauthorized(null, 'Hawk');
}
if (header.length > exports.limits.maxMatchLength) {
return Boom.badRequest('Header length too long');
}
const headerParts = header.match(internals.authHeaderRegex);
if (!headerParts) {
return Boom.badRequest('Invalid header syntax');
}
const scheme = headerParts[1];
if (scheme.toLowerCase() !== 'hawk') {
return Boom.unauthorized(null, 'Hawk');
}
const attributesString = headerParts[2];
if (!attributesString) {
return Boom.badRequest('Invalid header syntax');
}
const attributes = {};
let errorMessage = '';
const verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, ($0, $1, $2) => {
// Check valid attribute names
if (keys.indexOf($1) === -1) {
errorMessage = 'Unknown attribute: ' + $1;
return;
}
// Allowed attribute value characters
if ($2.match(internals.attributeRegex) === null) {
errorMessage = 'Bad attribute value: ' + $1;
return;
}
// Check for duplicates
if (attributes.hasOwnProperty($1)) {
errorMessage = 'Duplicate attribute: ' + $1;
return;
}
attributes[$1] = $2;
return '';
});
if (verify !== '') {
return Boom.badRequest(errorMessage || 'Bad header format');
}
return attributes;
};
exports.unauthorized = function (message, attributes) {
return Boom.unauthorized(message || null, 'Hawk', attributes);
};