summaryrefslogtreecommitdiff
path: root/web/js/browser/lib/hawk.js
diff options
context:
space:
mode:
Diffstat (limited to 'web/js/browser/lib/hawk.js')
-rw-r--r--web/js/browser/lib/hawk.js145
1 files changed, 145 insertions, 0 deletions
diff --git a/web/js/browser/lib/hawk.js b/web/js/browser/lib/hawk.js
new file mode 100644
index 0000000..69c7153
--- /dev/null
+++ b/web/js/browser/lib/hawk.js
@@ -0,0 +1,145 @@
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+import { hexToUint8, uint8ToBase64, uint8ToHex } from './utils';
+const encoder = () => new TextEncoder();
+const NAMESPACE = 'identity.mozilla.com/picl/v1/';
+export function deriveHawkCredentials(token, context) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const baseKey = yield crypto.subtle.importKey('raw', hexToUint8(token), 'HKDF', false, ['deriveBits']);
+ const keyMaterial = yield crypto.subtle.deriveBits({
+ name: 'HKDF',
+ salt: new Uint8Array(0),
+ // @ts-ignore
+ info: encoder().encode(`${NAMESPACE}${context}`),
+ hash: 'SHA-256',
+ }, baseKey, 32 * 3 * 8);
+ const id = new Uint8Array(keyMaterial.slice(0, 32));
+ const authKey = new Uint8Array(keyMaterial.slice(32, 64));
+ const bundleKey = new Uint8Array(keyMaterial.slice(64));
+ return {
+ id: uint8ToHex(id),
+ key: authKey,
+ bundleKey: uint8ToHex(bundleKey),
+ };
+ });
+}
+// The following is adapted from https://github.com/hapijs/hawk/blob/master/lib/browser.js
+/*
+ HTTP Hawk Authentication Scheme
+ Copyright (c) 2012-2013, Eran Hammer <eran@hueniverse.com>
+ MIT Licensed
+ */
+function parseUri(input) {
+ const parts = input.match(/^([^:]+)\:\/\/(?:[^@/]*@)?([^\/:]+)(?:\:(\d+))?([^#]*)(?:#.*)?$/);
+ 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;
+}
+function randomString(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('');
+}
+function generateNormalizedString(type, options) {
+ let normalized = 'hawk.1.' +
+ 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(/\\/g, '\\\\').replace(/\n/g, '\\n');
+ }
+ normalized += '\n';
+ if (options.app) {
+ normalized += options.app + '\n' + (options.dlg || '') + '\n';
+ }
+ return normalized;
+}
+function calculatePayloadHash(payload = '', contentType = '') {
+ return __awaiter(this, void 0, void 0, function* () {
+ const data = encoder().encode(`hawk.1.payload\n${contentType}\n${payload}\n`);
+ const hash = yield crypto.subtle.digest('SHA-256', data);
+ return uint8ToBase64(new Uint8Array(hash));
+ });
+}
+function calculateMac(type, credentials, options) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const normalized = generateNormalizedString(type, options);
+ const key = yield crypto.subtle.importKey('raw', credentials.key, {
+ name: 'HMAC',
+ hash: 'SHA-256',
+ length: 256,
+ }, true, ['sign']);
+ const hmac = yield crypto.subtle.sign('HMAC', key, encoder().encode(normalized));
+ return uint8ToBase64(new Uint8Array(hmac));
+ });
+}
+export function hawkHeader(method, uri, options) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const timestamp = options.timestamp ||
+ Math.floor((Date.now() + (options.localtimeOffsetMsec || 0)) / 1000);
+ const parsedUri = parseUri(uri);
+ const hash = yield calculatePayloadHash(options.payload, options.contentType);
+ const artifacts = {
+ ts: timestamp,
+ nonce: options.nonce || randomString(6),
+ method,
+ resource: parsedUri.resource,
+ host: parsedUri.host,
+ port: parsedUri.port,
+ hash,
+ };
+ const mac = yield calculateMac('header', options.credentials, artifacts);
+ const header = 'Hawk id="' +
+ options.credentials.id +
+ '", ts="' +
+ artifacts.ts +
+ '", nonce="' +
+ artifacts.nonce +
+ (artifacts.hash ? '", hash="' + artifacts.hash : '') +
+ '", mac="' +
+ mac +
+ '"';
+ return header;
+ });
+}
+export function header(method, uri, token, kind, options) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const credentials = yield deriveHawkCredentials(token, kind);
+ const authorization = yield hawkHeader(method, uri, Object.assign({ credentials }, options));
+ return new Headers({ authorization });
+ });
+}