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 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 }); }); }