3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 * The following bundle is from an external repository at github.com/mozilla/fxa-pairing-channel,
8 * it implements a shared library for two javascript environments to create an encrypted and authenticated
9 * communication channel by sharing a secret key and by relaying messages through a websocket server.
11 * It is used by the Firefox Accounts pairing flow, with one side of the channel being web
12 * content from https://accounts.firefox.com and the other side of the channel being chrome native code.
14 * This uses the event-target-shim node library published under the MIT license:
15 * https://github.com/mysticatea/event-target-shim/blob/master/LICENSE
17 * Bundle generated from https://github.com/mozilla/fxa-pairing-channel.git. Hash:c8ec3119920b4ffa833b, Chunkhash:378a5f51445e7aa7630e.
21 // This header provides a little bit of plumbing to use `FxAccountsPairingChannel`
22 // from Firefox browser code, hence the presence of these privileged browser APIs.
23 // If you're trying to use this from ordinary web content you're in for a bad time.
25 import { setTimeout } from "resource://gre/modules/Timer.sys.mjs";
27 // We cannot use WebSocket from chrome code without a window,
28 // see https://bugzilla.mozilla.org/show_bug.cgi?id=784686
29 const browser = Services.appShell.createWindowlessBrowser(true);
30 const {WebSocket} = browser.document.ownerGlobal;
32 export var FxAccountsPairingChannel =
33 /******/ (function(modules) { // webpackBootstrap
34 /******/ // The module cache
35 /******/ var installedModules = {};
37 /******/ // The require function
38 /******/ function __webpack_require__(moduleId) {
40 /******/ // Check if module is in cache
41 /******/ if(installedModules[moduleId]) {
42 /******/ return installedModules[moduleId].exports;
44 /******/ // Create a new module (and put it into the cache)
45 /******/ var module = installedModules[moduleId] = {
51 /******/ // Execute the module function
52 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
54 /******/ // Flag the module as loaded
55 /******/ module.l = true;
57 /******/ // Return the exports of the module
58 /******/ return module.exports;
62 /******/ // expose the modules object (__webpack_modules__)
63 /******/ __webpack_require__.m = modules;
65 /******/ // expose the module cache
66 /******/ __webpack_require__.c = installedModules;
68 /******/ // define getter function for harmony exports
69 /******/ __webpack_require__.d = function(exports, name, getter) {
70 /******/ if(!__webpack_require__.o(exports, name)) {
71 /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
75 /******/ // define __esModule on exports
76 /******/ __webpack_require__.r = function(exports) {
77 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
78 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
80 /******/ Object.defineProperty(exports, '__esModule', { value: true });
83 /******/ // create a fake namespace object
84 /******/ // mode & 1: value is a module id, require it
85 /******/ // mode & 2: merge all properties of value into the ns
86 /******/ // mode & 4: return value when already ns object
87 /******/ // mode & 8|1: behave like require
88 /******/ __webpack_require__.t = function(value, mode) {
89 /******/ if(mode & 1) value = __webpack_require__(value);
90 /******/ if(mode & 8) return value;
91 /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
92 /******/ var ns = Object.create(null);
93 /******/ __webpack_require__.r(ns);
94 /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
95 /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
99 /******/ // getDefaultExport function for compatibility with non-harmony modules
100 /******/ __webpack_require__.n = function(module) {
101 /******/ var getter = module && module.__esModule ?
102 /******/ function getDefault() { return module['default']; } :
103 /******/ function getModuleExports() { return module; };
104 /******/ __webpack_require__.d(getter, 'a', getter);
105 /******/ return getter;
108 /******/ // Object.prototype.hasOwnProperty.call
109 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
111 /******/ // __webpack_public_path__
112 /******/ __webpack_require__.p = "";
115 /******/ // Load entry module and return exports
116 /******/ return __webpack_require__(__webpack_require__.s = 0);
118 /************************************************************************/
121 /***/ (function(module, __webpack_exports__, __webpack_require__) {
125 __webpack_require__.r(__webpack_exports__);
128 __webpack_require__.d(__webpack_exports__, "PairingChannel", function() { return /* binding */ src_PairingChannel; });
129 __webpack_require__.d(__webpack_exports__, "base64urlToBytes", function() { return /* reexport */ base64urlToBytes; });
130 __webpack_require__.d(__webpack_exports__, "bytesToBase64url", function() { return /* reexport */ bytesToBase64url; });
131 __webpack_require__.d(__webpack_exports__, "bytesToHex", function() { return /* reexport */ bytesToHex; });
132 __webpack_require__.d(__webpack_exports__, "bytesToUtf8", function() { return /* reexport */ bytesToUtf8; });
133 __webpack_require__.d(__webpack_exports__, "hexToBytes", function() { return /* reexport */ hexToBytes; });
134 __webpack_require__.d(__webpack_exports__, "TLSCloseNotify", function() { return /* reexport */ TLSCloseNotify; });
135 __webpack_require__.d(__webpack_exports__, "TLSError", function() { return /* reexport */ TLSError; });
136 __webpack_require__.d(__webpack_exports__, "utf8ToBytes", function() { return /* reexport */ utf8ToBytes; });
137 __webpack_require__.d(__webpack_exports__, "_internals", function() { return /* binding */ _internals; });
139 // CONCATENATED MODULE: ./src/alerts.js
140 /* This Source Code Form is subject to the terms of the Mozilla Public
141 * License, v. 2.0. If a copy of the MPL was not distributed with this
142 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
144 /* eslint-disable sorting/sort-object-props */
145 const ALERT_LEVEL = {
150 const ALERT_DESCRIPTION = {
152 UNEXPECTED_MESSAGE: 10,
155 HANDSHAKE_FAILURE: 40,
156 ILLEGAL_PARAMETER: 47,
159 PROTOCOL_VERSION: 70,
161 MISSING_EXTENSION: 109,
162 UNSUPPORTED_EXTENSION: 110,
163 UNKNOWN_PSK_IDENTITY: 115,
164 NO_APPLICATION_PROTOCOL: 120,
166 /* eslint-enable sorting/sort-object-props */
168 function alertTypeToName(type) {
169 for (const name in ALERT_DESCRIPTION) {
170 if (ALERT_DESCRIPTION[name] === type) {
171 return `${name} (${type})`;
174 return `UNKNOWN (${type})`;
177 class TLSAlert extends Error {
178 constructor(description, level) {
179 super(`TLS Alert: ${alertTypeToName(description)}`);
180 this.description = description;
184 static fromBytes(bytes) {
185 if (bytes.byteLength !== 2) {
186 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
189 case ALERT_DESCRIPTION.CLOSE_NOTIFY:
190 if (bytes[0] !== ALERT_LEVEL.WARNING) {
191 // Close notifications should be fatal.
192 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
194 return new TLSCloseNotify();
196 return new TLSError(bytes[1]);
201 return new Uint8Array([this.level, this.description]);
205 class TLSCloseNotify extends TLSAlert {
207 super(ALERT_DESCRIPTION.CLOSE_NOTIFY, ALERT_LEVEL.WARNING);
211 class TLSError extends TLSAlert {
212 constructor(description = ALERT_DESCRIPTION.INTERNAL_ERROR) {
213 super(description, ALERT_LEVEL.FATAL);
217 // CONCATENATED MODULE: ./src/utils.js
218 /* This Source Code Form is subject to the terms of the Mozilla Public
219 * License, v. 2.0. If a copy of the MPL was not distributed with this
220 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
225 // Various low-level utility functions.
227 // These are mostly conveniences for working with Uint8Arrays as
228 // the primitive "bytes" type.
231 const UTF8_ENCODER = new TextEncoder();
232 const UTF8_DECODER = new TextDecoder();
236 function assert(cond, msg) {
238 throw new Error('assert failed: ' + msg);
242 function assertIsBytes(value, msg = 'value must be a Uint8Array') {
243 // Using `value instanceof Uint8Array` seems to fail in Firefox chrome code
244 // for inscrutable reasons, so we do a less direct check.
245 assert(ArrayBuffer.isView(value), msg);
246 assert(value.BYTES_PER_ELEMENT === 1, msg);
250 const EMPTY = new Uint8Array(0);
253 return new Uint8Array(n);
256 function arrayToBytes(value) {
257 return new Uint8Array(value);
260 function bytesToHex(bytes) {
261 return Array.prototype.map.call(bytes, byte => {
262 let s = byte.toString(16);
263 if (s.length === 1) {
270 function hexToBytes(hexstr) {
271 assert(hexstr.length % 2 === 0, 'hexstr.length must be even');
272 return new Uint8Array(Array.prototype.map.call(hexstr, (c, n) => {
274 return hexstr[n - 1] + c;
281 return parseInt(s, 16);
285 function bytesToUtf8(bytes) {
286 return UTF8_DECODER.decode(bytes);
289 function utf8ToBytes(str) {
290 return UTF8_ENCODER.encode(str);
293 function bytesToBase64url(bytes) {
294 // XXX TODO: try to use something constant-time, in case calling code
295 // uses it to encode secrets?
296 const charCodes = String.fromCharCode.apply(String, bytes);
297 return btoa(charCodes).replace(/\+/g, '-').replace(/\//g, '_');
300 function base64urlToBytes(str) {
301 // XXX TODO: try to use something constant-time, in case calling code
302 // uses it to decode secrets?
303 str = atob(str.replace(/-/g, '+').replace(/_/g, '/'));
304 const bytes = new Uint8Array(str.length);
305 for (let i = 0; i < str.length; i++) {
306 bytes[i] = str.charCodeAt(i);
311 function bytesAreEqual(v1, v2) {
314 if (v1.length !== v2.length) {
317 for (let i = 0; i < v1.length; i++) {
318 if (v1[i] !== v2[i]) {
325 // The `BufferReader` and `BufferWriter` classes are helpers for dealing with the
326 // binary struct format that's used for various TLS message. Think of them as a
327 // buffer with a pointer to the "current position" and a bunch of helper methods
328 // to read/write structured data and advance said pointer.
330 class utils_BufferWithPointer {
333 this._dataview = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
338 return this._buffer.byteLength;
347 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
349 if (pos > this.length()) {
350 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
356 this.seek(this._pos + offset);
360 // The `BufferReader` class helps you read structured data from a byte array.
361 // It offers methods for reading both primitive values, and the variable-length
362 // vector structures defined in https://tools.ietf.org/html/rfc8446#section-3.4.
364 // Such vectors are represented as a length followed by the concatenated
365 // bytes of each item, and the size of the length field is determined by
366 // the maximum allowed number of bytes in the vector. For example
367 // to read a vector that may contain up to 65535 bytes, use `readVector16`.
369 // To read a variable-length vector of between 1 and 100 uint16 values,
370 // defined in the RFC like this:
372 // uint16 items<2..200>;
374 // You would do something like this:
377 // buf.readVector8(buf => {
378 // items.push(buf.readUint16())
381 // The various `read` will throw `DECODE_ERROR` if you attempt to read path
382 // the end of the buffer, or past the end of a variable-length list.
384 class utils_BufferReader extends utils_BufferWithPointer {
387 return this.tell() < this.length();
391 // This avoids copies by returning a view onto the existing buffer.
392 const start = this._buffer.byteOffset + this.tell();
394 return new Uint8Array(this._buffer.buffer, start, length);
397 _rangeErrorToAlert(cb) {
401 if (err instanceof RangeError) {
402 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
409 return this._rangeErrorToAlert(() => {
410 const n = this._dataview.getUint8(this._pos);
417 return this._rangeErrorToAlert(() => {
418 const n = this._dataview.getUint16(this._pos);
425 return this._rangeErrorToAlert(() => {
426 let n = this._dataview.getUint16(this._pos);
427 n = (n << 8) | this._dataview.getUint8(this._pos + 2);
434 return this._rangeErrorToAlert(() => {
435 const n = this._dataview.getUint32(this._pos);
441 _readVector(length, cb) {
442 const contentsBuf = new utils_BufferReader(this.readBytes(length));
443 const expectedEnd = this.tell();
444 // Keep calling the callback until we've consumed the expected number of bytes.
446 while (contentsBuf.hasMoreBytes()) {
447 const prevPos = contentsBuf.tell();
449 // Check that the callback made forward progress, otherwise we'll infinite loop.
450 if (contentsBuf.tell() <= prevPos) {
451 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
455 // Check that the callback correctly consumed the vector's entire contents.
456 if (this.tell() !== expectedEnd) {
457 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
462 const length = this.readUint8();
463 return this._readVector(length, cb);
467 const length = this.readUint16();
468 return this._readVector(length, cb);
472 const length = this.readUint24();
473 return this._readVector(length, cb);
477 return this.readBytes(this.readUint8());
480 readVectorBytes16() {
481 return this.readBytes(this.readUint16());
484 readVectorBytes24() {
485 return this.readBytes(this.readUint24());
490 class utils_BufferWriter extends utils_BufferWithPointer {
491 constructor(size = 1024) {
492 super(new Uint8Array(size));
496 const curSize = this._buffer.byteLength;
497 const newPos = this._pos + n;
498 const shortfall = newPos - curSize;
500 // Classic grow-by-doubling, up to 4kB max increment.
501 // This formula was not arrived at by any particular science.
502 const incr = Math.min(curSize, 4 * 1024);
503 const newbuf = new Uint8Array(curSize + Math.ceil(shortfall / incr) * incr);
504 newbuf.set(this._buffer, 0);
505 this._buffer = newbuf;
506 this._dataview = new DataView(newbuf.buffer, newbuf.byteOffset, newbuf.byteLength);
510 slice(start = 0, end = this.tell()) {
512 end = this.tell() + end;
515 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
518 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
520 if (end > this.length()) {
521 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
523 return this._buffer.slice(start, end);
527 const slice = this.slice();
533 this._maybeGrow(data.byteLength);
534 this._buffer.set(data, this.tell());
535 this.incr(data.byteLength);
540 this._dataview.setUint8(this._pos, n);
546 this._dataview.setUint16(this._pos, n);
552 this._dataview.setUint16(this._pos, n >> 8);
553 this._dataview.setUint8(this._pos + 2, n & 0xFF);
559 this._dataview.setUint32(this._pos, n);
563 // These are helpers for writing the variable-length vector structure
564 // defined in https://tools.ietf.org/html/rfc8446#section-3.4.
566 // Such vectors are represented as a length followed by the concatenated
567 // bytes of each item, and the size of the length field is determined by
568 // the maximum allowed size of the vector. For example to write a vector
569 // that may contain up to 65535 bytes, use `writeVector16`.
571 // To write a variable-length vector of between 1 and 100 uint16 values,
572 // defined in the RFC like this:
574 // uint16 items<2..200>;
576 // You would do something like this:
578 // buf.writeVector8(buf => {
579 // for (let item of items) {
580 // buf.writeUint16(item)
584 // The helper will automatically take care of writing the appropriate
585 // length field once the callback completes.
587 _writeVector(maxLength, writeLength, cb) {
588 // Initially, write the length field as zero.
589 const lengthPos = this.tell();
591 // Call the callback to write the vector items.
592 const bodyPos = this.tell();
594 const length = this.tell() - bodyPos;
595 if (length >= maxLength) {
596 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
598 // Backfill the actual length field.
599 this.seek(lengthPos);
606 return this._writeVector(Math.pow(2, 8), len => this.writeUint8(len), cb);
610 return this._writeVector(Math.pow(2, 16), len => this.writeUint16(len), cb);
614 return this._writeVector(Math.pow(2, 24), len => this.writeUint24(len), cb);
617 writeVectorBytes8(bytes) {
618 return this.writeVector8(buf => {
619 buf.writeBytes(bytes);
623 writeVectorBytes16(bytes) {
624 return this.writeVector16(buf => {
625 buf.writeBytes(bytes);
629 writeVectorBytes24(bytes) {
630 return this.writeVector24(buf => {
631 buf.writeBytes(bytes);
636 // CONCATENATED MODULE: ./src/crypto.js
637 /* This Source Code Form is subject to the terms of the Mozilla Public
638 * License, v. 2.0. If a copy of the MPL was not distributed with this
639 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
642 // Low-level crypto primitives.
644 // This file implements the AEAD encrypt/decrypt and hashing routines
645 // for the TLS_AES_128_GCM_SHA256 ciphersuite. They are (thankfully)
646 // fairly light-weight wrappers around what's available via the WebCrypto
653 const AEAD_SIZE_INFLATION = 16;
654 const KEY_LENGTH = 16;
655 const IV_LENGTH = 12;
656 const HASH_LENGTH = 32;
658 async function prepareKey(key, mode) {
659 return crypto.subtle.importKey('raw', key, { name: 'AES-GCM' }, false, [mode]);
662 async function encrypt(key, iv, plaintext, additionalData) {
663 const ciphertext = await crypto.subtle.encrypt({
667 tagLength: AEAD_SIZE_INFLATION * 8
669 return new Uint8Array(ciphertext);
672 async function decrypt(key, iv, ciphertext, additionalData) {
674 const plaintext = await crypto.subtle.decrypt({
678 tagLength: AEAD_SIZE_INFLATION * 8
680 return new Uint8Array(plaintext);
682 // Yes, we really do throw 'decrypt_error' when failing to verify a HMAC,
683 // and a 'bad_record_mac' error when failing to decrypt.
684 throw new TLSError(ALERT_DESCRIPTION.BAD_RECORD_MAC);
688 async function hash(message) {
689 return new Uint8Array(await crypto.subtle.digest({ name: 'SHA-256' }, message));
692 async function hmac(keyBytes, message) {
693 const key = await crypto.subtle.importKey('raw', keyBytes, {
694 hash: { name: 'SHA-256' },
697 const sig = await crypto.subtle.sign({ name: 'HMAC' }, key, message);
698 return new Uint8Array(sig);
701 async function verifyHmac(keyBytes, signature, message) {
702 const key = await crypto.subtle.importKey('raw', keyBytes, {
703 hash: { name: 'SHA-256' },
705 }, false, ['verify']);
706 if (! (await crypto.subtle.verify({ name: 'HMAC' }, key, signature, message))) {
707 // Yes, we really do throw 'decrypt_error' when failing to verify a HMAC,
708 // and a 'bad_record_mac' error when failing to decrypt.
709 throw new TLSError(ALERT_DESCRIPTION.DECRYPT_ERROR);
713 async function hkdfExtract(salt, ikm) {
714 // Ref https://tools.ietf.org/html/rfc5869#section-2.2
715 return await hmac(salt, ikm);
718 async function hkdfExpand(prk, info, length) {
719 // Ref https://tools.ietf.org/html/rfc5869#section-2.3
720 const N = Math.ceil(length / HASH_LENGTH);
722 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
725 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
727 const input = new utils_BufferWriter();
728 const output = new utils_BufferWriter();
729 let T = new Uint8Array(0);
730 for (let i = 1; i <= N; i++) {
732 input.writeBytes(info);
734 T = await hmac(prk, input.flush());
735 output.writeBytes(T);
737 return output.slice(0, length);
740 async function hkdfExpandLabel(secret, label, context, length) {
742 // uint16 length = Length;
743 // opaque label < 7..255 > = "tls13 " + Label;
744 // opaque context < 0..255 > = Context;
746 const hkdfLabel = new utils_BufferWriter();
747 hkdfLabel.writeUint16(length);
748 hkdfLabel.writeVectorBytes8(utf8ToBytes('tls13 ' + label));
749 hkdfLabel.writeVectorBytes8(context);
750 return hkdfExpand(secret, hkdfLabel.flush(), length);
753 async function getRandomBytes(size) {
754 const bytes = new Uint8Array(size);
755 crypto.getRandomValues(bytes);
759 // CONCATENATED MODULE: ./src/extensions.js
760 /* This Source Code Form is subject to the terms of the Mozilla Public
761 * License, v. 2.0. If a copy of the MPL was not distributed with this
762 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
765 // Extension parsing.
767 // This file contains some helpers for reading/writing the various kinds
768 // of Extension that might appear in a HandshakeMessage.
770 // "Extensions" are how TLS signals the presence of particular bits of optional
771 // functionality in the protocol. Lots of parts of TLS1.3 that don't seem like
772 // they're optional are implemented in terms of an extension, IIUC because that's
773 // what was needed for a clean deployment in amongst earlier versions of the protocol.
780 /* eslint-disable sorting/sort-object-props */
781 const EXTENSION_TYPE = {
783 SUPPORTED_VERSIONS: 43,
784 PSK_KEY_EXCHANGE_MODES: 45,
786 /* eslint-enable sorting/sort-object-props */
788 // Base class for generic reading/writing of extensions,
789 // which are all uniformly formatted as:
792 // ExtensionType extension_type;
793 // opaque extension_data<0..2^16-1>;
796 // Extensions always appear inside of a handshake message,
797 // and their internal structure may differ based on the
798 // type of that message.
800 class extensions_Extension {
803 throw new Error('not implemented');
806 static read(messageType, buf) {
807 const type = buf.readUint16();
811 buf.readVector16(buf => {
813 case EXTENSION_TYPE.PRE_SHARED_KEY:
814 ext = extensions_PreSharedKeyExtension._read(messageType, buf);
816 case EXTENSION_TYPE.SUPPORTED_VERSIONS:
817 ext = extensions_SupportedVersionsExtension._read(messageType, buf);
819 case EXTENSION_TYPE.PSK_KEY_EXCHANGE_MODES:
820 ext = extensions_PskKeyExchangeModesExtension._read(messageType, buf);
823 // Skip over unrecognised extensions.
824 buf.incr(buf.length());
826 if (buf.hasMoreBytes()) {
827 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
833 write(messageType, buf) {
834 buf.writeUint16(this.TYPE_TAG);
835 buf.writeVector16(buf => {
836 this._write(messageType, buf);
840 static _read(messageType, buf) {
841 throw new Error('not implemented');
844 static _write(messageType, buf) {
845 throw new Error('not implemented');
849 // The PreSharedKey extension:
852 // opaque identity<1..2^16-1>;
853 // uint32 obfuscated_ticket_age;
855 // opaque PskBinderEntry<32..255>;
857 // PskIdentity identities<7..2^16-1>;
858 // PskBinderEntry binders<33..2^16-1>;
861 // select(Handshake.msg_type) {
862 // case client_hello: OfferedPsks;
863 // case server_hello: uint16 selected_identity;
865 // } PreSharedKeyExtension;
867 class extensions_PreSharedKeyExtension extends extensions_Extension {
868 constructor(identities, binders, selectedIdentity) {
870 this.identities = identities;
871 this.binders = binders;
872 this.selectedIdentity = selectedIdentity;
876 return EXTENSION_TYPE.PRE_SHARED_KEY;
879 static _read(messageType, buf) {
880 let identities = null, binders = null, selectedIdentity = null;
881 switch (messageType) {
882 case HANDSHAKE_TYPE.CLIENT_HELLO:
883 identities = []; binders = [];
884 buf.readVector16(buf => {
885 const identity = buf.readVectorBytes16();
886 buf.readBytes(4); // Skip over the ticket age.
887 identities.push(identity);
889 buf.readVector16(buf => {
890 const binder = buf.readVectorBytes8();
891 if (binder.byteLength < HASH_LENGTH) {
892 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
894 binders.push(binder);
896 if (identities.length !== binders.length) {
897 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
900 case HANDSHAKE_TYPE.SERVER_HELLO:
901 selectedIdentity = buf.readUint16();
904 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
906 return new this(identities, binders, selectedIdentity);
909 _write(messageType, buf) {
910 switch (messageType) {
911 case HANDSHAKE_TYPE.CLIENT_HELLO:
912 buf.writeVector16(buf => {
913 this.identities.forEach(pskId => {
914 buf.writeVectorBytes16(pskId);
915 buf.writeUint32(0); // Zero for "tag age" field.
918 buf.writeVector16(buf => {
919 this.binders.forEach(pskBinder => {
920 buf.writeVectorBytes8(pskBinder);
924 case HANDSHAKE_TYPE.SERVER_HELLO:
925 buf.writeUint16(this.selectedIdentity);
928 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
934 // The SupportedVersions extension:
937 // select(Handshake.msg_type) {
938 // case client_hello:
939 // ProtocolVersion versions < 2..254 >;
940 // case server_hello:
941 // ProtocolVersion selected_version;
943 // } SupportedVersions;
945 class extensions_SupportedVersionsExtension extends extensions_Extension {
946 constructor(versions, selectedVersion) {
948 this.versions = versions;
949 this.selectedVersion = selectedVersion;
953 return EXTENSION_TYPE.SUPPORTED_VERSIONS;
956 static _read(messageType, buf) {
957 let versions = null, selectedVersion = null;
958 switch (messageType) {
959 case HANDSHAKE_TYPE.CLIENT_HELLO:
961 buf.readVector8(buf => {
962 versions.push(buf.readUint16());
965 case HANDSHAKE_TYPE.SERVER_HELLO:
966 selectedVersion = buf.readUint16();
969 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
971 return new this(versions, selectedVersion);
974 _write(messageType, buf) {
975 switch (messageType) {
976 case HANDSHAKE_TYPE.CLIENT_HELLO:
977 buf.writeVector8(buf => {
978 this.versions.forEach(version => {
979 buf.writeUint16(version);
983 case HANDSHAKE_TYPE.SERVER_HELLO:
984 buf.writeUint16(this.selectedVersion);
987 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
993 class extensions_PskKeyExchangeModesExtension extends extensions_Extension {
1000 return EXTENSION_TYPE.PSK_KEY_EXCHANGE_MODES;
1003 static _read(messageType, buf) {
1005 switch (messageType) {
1006 case HANDSHAKE_TYPE.CLIENT_HELLO:
1007 buf.readVector8(buf => {
1008 modes.push(buf.readUint8());
1012 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
1014 return new this(modes);
1017 _write(messageType, buf) {
1018 switch (messageType) {
1019 case HANDSHAKE_TYPE.CLIENT_HELLO:
1020 buf.writeVector8(buf => {
1021 this.modes.forEach(mode => {
1022 buf.writeUint8(mode);
1027 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
1032 // CONCATENATED MODULE: ./src/constants.js
1033 /* This Source Code Form is subject to the terms of the Mozilla Public
1034 * License, v. 2.0. If a copy of the MPL was not distributed with this
1035 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
1037 const VERSION_TLS_1_0 = 0x0301;
1038 const VERSION_TLS_1_2 = 0x0303;
1039 const VERSION_TLS_1_3 = 0x0304;
1040 const TLS_AES_128_GCM_SHA256 = 0x1301;
1041 const PSK_MODE_KE = 0;
1043 // CONCATENATED MODULE: ./src/messages.js
1044 /* This Source Code Form is subject to the terms of the Mozilla Public
1045 * License, v. 2.0. If a copy of the MPL was not distributed with this
1046 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
1051 // Herein we have code for reading and writing the various Handshake
1052 // messages involved in the TLS protocol.
1061 /* eslint-disable sorting/sort-object-props */
1062 const HANDSHAKE_TYPE = {
1065 NEW_SESSION_TICKET: 4,
1066 ENCRYPTED_EXTENSIONS: 8,
1069 /* eslint-enable sorting/sort-object-props */
1071 // Base class for generic reading/writing of handshake messages,
1072 // which are all uniformly formatted as:
1075 // HandshakeType msg_type; /* handshake type */
1076 // uint24 length; /* bytes in message */
1077 // select(Handshake.msg_type) {
1078 // ... type specific cases here ...
1082 class messages_HandshakeMessage {
1085 throw new Error('not implemented');
1088 static fromBytes(bytes) {
1089 // Each handshake message has a type and length prefix, per
1090 // https://tools.ietf.org/html/rfc8446#appendix-B.3
1091 const buf = new utils_BufferReader(bytes);
1092 const msg = this.read(buf);
1093 if (buf.hasMoreBytes()) {
1094 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
1100 const buf = new utils_BufferWriter();
1106 const type = buf.readUint8();
1108 buf.readVector24(buf => {
1110 case HANDSHAKE_TYPE.CLIENT_HELLO:
1111 msg = messages_ClientHello._read(buf);
1113 case HANDSHAKE_TYPE.SERVER_HELLO:
1114 msg = messages_ServerHello._read(buf);
1116 case HANDSHAKE_TYPE.NEW_SESSION_TICKET:
1117 msg = messages_NewSessionTicket._read(buf);
1119 case HANDSHAKE_TYPE.ENCRYPTED_EXTENSIONS:
1120 msg = EncryptedExtensions._read(buf);
1122 case HANDSHAKE_TYPE.FINISHED:
1123 msg = messages_Finished._read(buf);
1126 if (buf.hasMoreBytes()) {
1127 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
1131 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
1137 buf.writeUint8(this.TYPE_TAG);
1138 buf.writeVector24(buf => {
1144 throw new Error('not implemented');
1148 throw new Error('not implemented');
1151 // Some little helpers for reading a list of extensions,
1152 // which is uniformly represented as:
1154 // Extension extensions<8..2^16-1>;
1156 // Recognized extensions are returned as a Map from extension type
1157 // to extension data object, with a special `lastSeenExtension`
1158 // property to make it easy to check which one came last.
1160 static _readExtensions(messageType, buf) {
1161 const extensions = new Map();
1162 buf.readVector16(buf => {
1163 const ext = extensions_Extension.read(messageType, buf);
1164 if (extensions.has(ext.TYPE_TAG)) {
1165 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
1167 extensions.set(ext.TYPE_TAG, ext);
1168 extensions.lastSeenExtension = ext.TYPE_TAG;
1173 _writeExtensions(buf, extensions) {
1174 buf.writeVector16(buf => {
1175 extensions.forEach(ext => {
1176 ext.write(this.TYPE_TAG, buf);
1183 // The ClientHello message:
1186 // ProtocolVersion legacy_version = 0x0303;
1188 // opaque legacy_session_id<0..32>;
1189 // CipherSuite cipher_suites<2..2^16-2>;
1190 // opaque legacy_compression_methods<1..2^8-1>;
1191 // Extension extensions<8..2^16-1>;
1194 class messages_ClientHello extends messages_HandshakeMessage {
1196 constructor(random, sessionId, extensions) {
1198 this.random = random;
1199 this.sessionId = sessionId;
1200 this.extensions = extensions;
1204 return HANDSHAKE_TYPE.CLIENT_HELLO;
1208 // The legacy_version field may indicate an earlier version of TLS
1209 // for backwards compatibility, but must not predate TLS 1.0!
1210 if (buf.readUint16() < VERSION_TLS_1_0) {
1211 throw new TLSError(ALERT_DESCRIPTION.PROTOCOL_VERSION);
1213 // The random bytes provided by the peer.
1214 const random = buf.readBytes(32);
1215 // Read legacy_session_id, so the server can echo it.
1216 const sessionId = buf.readVectorBytes8();
1217 // We only support a single ciphersuite, but the peer may offer several.
1218 // Scan the list to confirm that the one we want is present.
1220 buf.readVector16(buf => {
1221 const cipherSuite = buf.readUint16();
1222 if (cipherSuite === TLS_AES_128_GCM_SHA256) {
1227 throw new TLSError(ALERT_DESCRIPTION.HANDSHAKE_FAILURE);
1229 // legacy_compression_methods must be a single zero byte for TLS1.3 ClientHellos.
1230 // It can be non-zero in previous versions of TLS, but we're not going to
1231 // make a successful handshake with such versions, so better to just bail out now.
1232 const legacyCompressionMethods = buf.readVectorBytes8();
1233 if (legacyCompressionMethods.byteLength !== 1) {
1234 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
1236 if (legacyCompressionMethods[0] !== 0x00) {
1237 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
1239 // Read and check the extensions.
1240 const extensions = this._readExtensions(HANDSHAKE_TYPE.CLIENT_HELLO, buf);
1241 if (! extensions.has(EXTENSION_TYPE.SUPPORTED_VERSIONS)) {
1242 throw new TLSError(ALERT_DESCRIPTION.MISSING_EXTENSION);
1244 if (extensions.get(EXTENSION_TYPE.SUPPORTED_VERSIONS).versions.indexOf(VERSION_TLS_1_3) === -1) {
1245 throw new TLSError(ALERT_DESCRIPTION.PROTOCOL_VERSION);
1247 // Was the PreSharedKey extension the last one?
1248 if (extensions.has(EXTENSION_TYPE.PRE_SHARED_KEY)) {
1249 if (extensions.lastSeenExtension !== EXTENSION_TYPE.PRE_SHARED_KEY) {
1250 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
1253 return new this(random, sessionId, extensions);
1257 buf.writeUint16(VERSION_TLS_1_2);
1258 buf.writeBytes(this.random);
1259 buf.writeVectorBytes8(this.sessionId);
1260 // Our single supported ciphersuite
1261 buf.writeVector16(buf => {
1262 buf.writeUint16(TLS_AES_128_GCM_SHA256);
1264 // A single zero byte for legacy_compression_methods
1265 buf.writeVectorBytes8(new Uint8Array(1));
1266 this._writeExtensions(buf, this.extensions);
1271 // The ServerHello message:
1274 // ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
1276 // opaque legacy_session_id_echo<0..32>;
1277 // CipherSuite cipher_suite;
1278 // uint8 legacy_compression_method = 0;
1279 // Extension extensions < 6..2 ^ 16 - 1 >;
1282 class messages_ServerHello extends messages_HandshakeMessage {
1284 constructor(random, sessionId, extensions) {
1286 this.random = random;
1287 this.sessionId = sessionId;
1288 this.extensions = extensions;
1292 return HANDSHAKE_TYPE.SERVER_HELLO;
1296 // Fixed value for legacy_version.
1297 if (buf.readUint16() !== VERSION_TLS_1_2) {
1298 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
1300 // Random bytes from the server.
1301 const random = buf.readBytes(32);
1302 // It should have echoed our vector for legacy_session_id.
1303 const sessionId = buf.readVectorBytes8();
1304 // It should have selected our single offered ciphersuite.
1305 if (buf.readUint16() !== TLS_AES_128_GCM_SHA256) {
1306 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
1308 // legacy_compression_method must be zero.
1309 if (buf.readUint8() !== 0) {
1310 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
1312 const extensions = this._readExtensions(HANDSHAKE_TYPE.SERVER_HELLO, buf);
1313 if (! extensions.has(EXTENSION_TYPE.SUPPORTED_VERSIONS)) {
1314 throw new TLSError(ALERT_DESCRIPTION.MISSING_EXTENSION);
1316 if (extensions.get(EXTENSION_TYPE.SUPPORTED_VERSIONS).selectedVersion !== VERSION_TLS_1_3) {
1317 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
1319 return new this(random, sessionId, extensions);
1323 buf.writeUint16(VERSION_TLS_1_2);
1324 buf.writeBytes(this.random);
1325 buf.writeVectorBytes8(this.sessionId);
1326 // Our single supported ciphersuite
1327 buf.writeUint16(TLS_AES_128_GCM_SHA256);
1328 // A single zero byte for legacy_compression_method
1330 this._writeExtensions(buf, this.extensions);
1335 // The EncryptedExtensions message:
1338 // Extension extensions < 0..2 ^ 16 - 1 >;
1339 // } EncryptedExtensions;
1341 // We don't actually send any EncryptedExtensions,
1342 // but still have to send an empty message.
1344 class EncryptedExtensions extends messages_HandshakeMessage {
1345 constructor(extensions) {
1347 this.extensions = extensions;
1351 return HANDSHAKE_TYPE.ENCRYPTED_EXTENSIONS;
1355 const extensions = this._readExtensions(HANDSHAKE_TYPE.ENCRYPTED_EXTENSIONS, buf);
1356 return new this(extensions);
1360 this._writeExtensions(buf, this.extensions);
1365 // The Finished message:
1368 // opaque verify_data[Hash.length];
1371 class messages_Finished extends messages_HandshakeMessage {
1373 constructor(verifyData) {
1375 this.verifyData = verifyData;
1379 return HANDSHAKE_TYPE.FINISHED;
1383 const verifyData = buf.readBytes(HASH_LENGTH);
1384 return new this(verifyData);
1388 buf.writeBytes(this.verifyData);
1393 // The NewSessionTicket message:
1396 // uint32 ticket_lifetime;
1397 // uint32 ticket_age_add;
1398 // opaque ticket_nonce < 0..255 >;
1399 // opaque ticket < 1..2 ^ 16 - 1 >;
1400 // Extension extensions < 0..2 ^ 16 - 2 >;
1401 // } NewSessionTicket;
1403 // We don't actually make use of these, but we need to be able
1404 // to accept them and do basic validation.
1406 class messages_NewSessionTicket extends messages_HandshakeMessage {
1407 constructor(ticketLifetime, ticketAgeAdd, ticketNonce, ticket, extensions) {
1409 this.ticketLifetime = ticketLifetime;
1410 this.ticketAgeAdd = ticketAgeAdd;
1411 this.ticketNonce = ticketNonce;
1412 this.ticket = ticket;
1413 this.extensions = extensions;
1417 return HANDSHAKE_TYPE.NEW_SESSION_TICKET;
1421 const ticketLifetime = buf.readUint32();
1422 const ticketAgeAdd = buf.readUint32();
1423 const ticketNonce = buf.readVectorBytes8();
1424 const ticket = buf.readVectorBytes16();
1425 if (ticket.byteLength < 1) {
1426 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
1428 const extensions = this._readExtensions(HANDSHAKE_TYPE.NEW_SESSION_TICKET, buf);
1429 return new this(ticketLifetime, ticketAgeAdd, ticketNonce, ticket, extensions);
1433 buf.writeUint32(this.ticketLifetime);
1434 buf.writeUint32(this.ticketAgeAdd);
1435 buf.writeVectorBytes8(this.ticketNonce);
1436 buf.writeVectorBytes16(this.ticket);
1437 this._writeExtensions(buf, this.extensions);
1441 // CONCATENATED MODULE: ./src/states.js
1442 /* This Source Code Form is subject to the terms of the Mozilla Public
1443 * License, v. 2.0. If a copy of the MPL was not distributed with this
1444 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
1454 // State-machine for TLS Handshake Management.
1456 // Internally, we manage the TLS connection by explicitly modelling the
1457 // client and server state-machines from RFC8446. You can think of
1458 // these `State` objects as little plugins for the `Connection` class
1459 // that provide different behaviours of `send` and `receive` depending
1460 // on the state of the connection.
1463 class states_State {
1469 async initialize() {
1470 // By default, nothing to do when entering the state.
1473 async sendApplicationData(bytes) {
1474 // By default, assume we're not ready to send yet and the caller
1475 // should be blocking on the connection promise before reaching here.
1476 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
1479 async recvApplicationData(bytes) {
1480 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
1483 async recvHandshakeMessage(msg) {
1484 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
1487 async recvAlertMessage(alert) {
1488 switch (alert.description) {
1489 case ALERT_DESCRIPTION.CLOSE_NOTIFY:
1490 this.conn._closeForRecv(alert);
1493 return await this.handleErrorAndRethrow(alert);
1497 async recvChangeCipherSpec(bytes) {
1498 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
1501 async handleErrorAndRethrow(err) {
1503 if (! (alert instanceof TLSAlert)) {
1504 alert = new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
1506 // Try to send error alert to the peer, but we may not
1507 // be able to if the outgoing connection was already closed.
1509 await this.conn._sendAlertMessage(alert);
1511 await this.conn._transition(ERROR, err);
1516 const alert = new TLSCloseNotify();
1517 await this.conn._sendAlertMessage(alert);
1518 this.conn._closeForSend(alert);
1523 // A special "guard" state to prevent us from using
1524 // an improperly-initialized Connection.
1526 class UNINITIALIZED extends states_State {
1527 async initialize() {
1528 throw new Error('uninitialized state');
1530 async sendApplicationData(bytes) {
1531 throw new Error('uninitialized state');
1533 async recvApplicationData(bytes) {
1534 throw new Error('uninitialized state');
1536 async recvHandshakeMessage(msg) {
1537 throw new Error('uninitialized state');
1539 async recvChangeCipherSpec(bytes) {
1540 throw new Error('uninitialized state');
1542 async handleErrorAndRethrow(err) {
1546 throw new Error('uninitialized state');
1550 // A special "error" state for when something goes wrong.
1551 // This state never transitions to another state, effectively
1552 // terminating the connection.
1554 class ERROR extends states_State {
1555 async initialize(err) {
1557 this.conn._setConnectionFailure(err);
1558 // Unceremoniously shut down the record layer on error.
1559 this.conn._recordlayer.setSendError(err);
1560 this.conn._recordlayer.setRecvError(err);
1562 async sendApplicationData(bytes) {
1565 async recvApplicationData(bytes) {
1568 async recvHandshakeMessage(msg) {
1571 async recvAlertMessage(err) {
1574 async recvChangeCipherSpec(bytes) {
1577 async handleErrorAndRethrow(err) {
1585 // The "connected" state, for when the handshake is complete
1586 // and we're ready to send application-level data.
1587 // The logic for this is largely symmetric between client and server.
1589 class states_CONNECTED extends states_State {
1590 async initialize() {
1591 this.conn._setConnectionSuccess();
1593 async sendApplicationData(bytes) {
1594 await this.conn._sendApplicationData(bytes);
1596 async recvApplicationData(bytes) {
1599 async recvChangeCipherSpec(bytes) {
1600 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
1604 // A base class for states that occur in the middle of the handshake
1605 // (that is, between ClientHello and Finished). These states may receive
1606 // CHANGE_CIPHER_SPEC records for b/w compat reasons, which must contain
1607 // exactly a single 0x01 byte and must otherwise be ignored.
1609 class states_MidHandshakeState extends states_State {
1610 async recvChangeCipherSpec(bytes) {
1611 if (this.conn._hasSeenChangeCipherSpec) {
1612 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
1614 if (bytes.byteLength !== 1 || bytes[0] !== 1) {
1615 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
1617 this.conn._hasSeenChangeCipherSpec = true;
1621 // These states implement (part of) the client state-machine from
1622 // https://tools.ietf.org/html/rfc8446#appendix-A.1
1624 // Since we're only implementing a small subset of TLS1.3,
1625 // we only need a small subset of the handshake. It basically goes:
1627 // * send ClientHello
1628 // * receive ServerHello
1629 // * receive EncryptedExtensions
1630 // * receive server Finished
1631 // * send client Finished
1633 // We include some unused states for completeness, so that it's easier
1634 // to check the implementation against the diagrams in the RFC.
1636 class states_CLIENT_START extends states_State {
1637 async initialize() {
1638 const keyschedule = this.conn._keyschedule;
1639 await keyschedule.addPSK(this.conn.psk);
1640 // Construct a ClientHello message with our single PSK.
1641 // We can't know the PSK binder value yet, so we initially write zeros.
1642 const clientHello = new messages_ClientHello(
1643 // Client random salt.
1644 await getRandomBytes(32),
1645 // Random legacy_session_id; we *could* send an empty string here,
1646 // but sending a random one makes it easier to be compatible with
1647 // the data emitted by tlslite-ng for test-case generation.
1648 await getRandomBytes(32),
1650 new extensions_SupportedVersionsExtension([VERSION_TLS_1_3]),
1651 new extensions_PskKeyExchangeModesExtension([PSK_MODE_KE]),
1652 new extensions_PreSharedKeyExtension([this.conn.pskId], [zeros(HASH_LENGTH)]),
1655 const buf = new utils_BufferWriter();
1656 clientHello.write(buf);
1657 // Now that we know what the ClientHello looks like,
1658 // go back and calculate the appropriate PSK binder value.
1659 // We only support a single PSK, so the length of the binders field is the
1660 // length of the hash plus one for rendering it as a variable-length byte array,
1661 // plus two for rendering the variable-length list of PSK binders.
1662 const PSK_BINDERS_SIZE = HASH_LENGTH + 1 + 2;
1663 const truncatedTranscript = buf.slice(0, buf.tell() - PSK_BINDERS_SIZE);
1664 const pskBinder = await keyschedule.calculateFinishedMAC(keyschedule.extBinderKey, truncatedTranscript);
1665 buf.incr(-HASH_LENGTH);
1666 buf.writeBytes(pskBinder);
1667 await this.conn._sendHandshakeMessageBytes(buf.flush());
1668 await this.conn._transition(states_CLIENT_WAIT_SH, clientHello.sessionId);
1672 class states_CLIENT_WAIT_SH extends states_State {
1673 async initialize(sessionId) {
1674 this._sessionId = sessionId;
1676 async recvHandshakeMessage(msg) {
1677 if (! (msg instanceof messages_ServerHello)) {
1678 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
1680 if (! bytesAreEqual(msg.sessionId, this._sessionId)) {
1681 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
1683 const pskExt = msg.extensions.get(EXTENSION_TYPE.PRE_SHARED_KEY);
1685 throw new TLSError(ALERT_DESCRIPTION.MISSING_EXTENSION);
1687 // We expect only the SUPPORTED_VERSIONS and PRE_SHARED_KEY extensions.
1688 if (msg.extensions.size !== 2) {
1689 throw new TLSError(ALERT_DESCRIPTION.UNSUPPORTED_EXTENSION);
1691 if (pskExt.selectedIdentity !== 0) {
1692 throw new TLSError(ALERT_DESCRIPTION.ILLEGAL_PARAMETER);
1694 await this.conn._keyschedule.addECDHE(null);
1695 await this.conn._setSendKey(this.conn._keyschedule.clientHandshakeTrafficSecret);
1696 await this.conn._setRecvKey(this.conn._keyschedule.serverHandshakeTrafficSecret);
1697 await this.conn._transition(states_CLIENT_WAIT_EE);
1701 class states_CLIENT_WAIT_EE extends states_MidHandshakeState {
1702 async recvHandshakeMessage(msg) {
1703 // We don't make use of any encrypted extensions, but we still
1704 // have to wait for the server to send the (empty) list of them.
1705 if (! (msg instanceof EncryptedExtensions)) {
1706 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
1708 // We do not support any EncryptedExtensions.
1709 if (msg.extensions.size !== 0) {
1710 throw new TLSError(ALERT_DESCRIPTION.UNSUPPORTED_EXTENSION);
1712 const keyschedule = this.conn._keyschedule;
1713 const serverFinishedTranscript = keyschedule.getTranscript();
1714 await this.conn._transition(states_CLIENT_WAIT_FINISHED, serverFinishedTranscript);
1718 class states_CLIENT_WAIT_FINISHED extends states_State {
1719 async initialize(serverFinishedTranscript) {
1720 this._serverFinishedTranscript = serverFinishedTranscript;
1722 async recvHandshakeMessage(msg) {
1723 if (! (msg instanceof messages_Finished)) {
1724 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
1726 // Verify server Finished MAC.
1727 const keyschedule = this.conn._keyschedule;
1728 await keyschedule.verifyFinishedMAC(keyschedule.serverHandshakeTrafficSecret, msg.verifyData, this._serverFinishedTranscript);
1729 // Send our own Finished message in return.
1730 // This must be encrypted with the handshake traffic key,
1731 // but must not appear in the transcript used to calculate the application keys.
1732 const clientFinishedMAC = await keyschedule.calculateFinishedMAC(keyschedule.clientHandshakeTrafficSecret);
1733 await keyschedule.finalize();
1734 await this.conn._sendHandshakeMessage(new messages_Finished(clientFinishedMAC));
1735 await this.conn._setSendKey(keyschedule.clientApplicationTrafficSecret);
1736 await this.conn._setRecvKey(keyschedule.serverApplicationTrafficSecret);
1737 await this.conn._transition(states_CLIENT_CONNECTED);
1741 class states_CLIENT_CONNECTED extends states_CONNECTED {
1742 async recvHandshakeMessage(msg) {
1743 // A connected client must be prepared to accept NewSessionTicket
1744 // messages. We never use them, but other server implementations
1746 if (! (msg instanceof messages_NewSessionTicket)) {
1747 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
1752 // These states implement (part of) the server state-machine from
1753 // https://tools.ietf.org/html/rfc8446#appendix-A.2
1755 // Since we're only implementing a small subset of TLS1.3,
1756 // we only need a small subset of the handshake. It basically goes:
1758 // * receive ClientHello
1759 // * send ServerHello
1760 // * send empty EncryptedExtensions
1761 // * send server Finished
1762 // * receive client Finished
1764 // We include some unused states for completeness, so that it's easier
1765 // to check the implementation against the diagrams in the RFC.
1767 class states_SERVER_START extends states_State {
1768 async recvHandshakeMessage(msg) {
1769 if (! (msg instanceof messages_ClientHello)) {
1770 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
1772 // In the spec, this is where we select connection parameters, and maybe
1773 // tell the client to try again if we can't find a compatible set.
1774 // Since we only support a fixed cipherset, the only thing to "negotiate"
1775 // is whether they provided an acceptable PSK.
1776 const pskExt = msg.extensions.get(EXTENSION_TYPE.PRE_SHARED_KEY);
1777 const pskModesExt = msg.extensions.get(EXTENSION_TYPE.PSK_KEY_EXCHANGE_MODES);
1778 if (! pskExt || ! pskModesExt) {
1779 throw new TLSError(ALERT_DESCRIPTION.MISSING_EXTENSION);
1781 if (pskModesExt.modes.indexOf(PSK_MODE_KE) === -1) {
1782 throw new TLSError(ALERT_DESCRIPTION.HANDSHAKE_FAILURE);
1784 const pskIndex = pskExt.identities.findIndex(pskId => bytesAreEqual(pskId, this.conn.pskId));
1785 if (pskIndex === -1) {
1786 throw new TLSError(ALERT_DESCRIPTION.UNKNOWN_PSK_IDENTITY);
1788 await this.conn._keyschedule.addPSK(this.conn.psk);
1789 // Validate the PSK binder.
1790 const keyschedule = this.conn._keyschedule;
1791 const transcript = keyschedule.getTranscript();
1792 // Calculate size occupied by the PSK binders.
1793 let pskBindersSize = 2; // Vector16 representation overhead.
1794 for (const binder of pskExt.binders) {
1795 pskBindersSize += binder.byteLength + 1; // Vector8 representation overhead.
1797 await keyschedule.verifyFinishedMAC(keyschedule.extBinderKey, pskExt.binders[pskIndex], transcript.slice(0, -pskBindersSize));
1798 await this.conn._transition(states_SERVER_NEGOTIATED, msg.sessionId, pskIndex);
1802 class states_SERVER_NEGOTIATED extends states_MidHandshakeState {
1803 async initialize(sessionId, pskIndex) {
1804 await this.conn._sendHandshakeMessage(new messages_ServerHello(
1806 await getRandomBytes(32),
1809 new extensions_SupportedVersionsExtension(null, VERSION_TLS_1_3),
1810 new extensions_PreSharedKeyExtension(null, null, pskIndex),
1813 // If the client sent a non-empty sessionId, the server *must* send a change-cipher-spec for b/w compat.
1814 if (sessionId.byteLength > 0) {
1815 await this.conn._sendChangeCipherSpec();
1817 // We can now transition to the encrypted part of the handshake.
1818 const keyschedule = this.conn._keyschedule;
1819 await keyschedule.addECDHE(null);
1820 await this.conn._setSendKey(keyschedule.serverHandshakeTrafficSecret);
1821 await this.conn._setRecvKey(keyschedule.clientHandshakeTrafficSecret);
1822 // Send an empty EncryptedExtensions message.
1823 await this.conn._sendHandshakeMessage(new EncryptedExtensions([]));
1824 // Send the Finished message.
1825 const serverFinishedMAC = await keyschedule.calculateFinishedMAC(keyschedule.serverHandshakeTrafficSecret);
1826 await this.conn._sendHandshakeMessage(new messages_Finished(serverFinishedMAC));
1827 // We can now *send* using the application traffic key,
1828 // but have to wait to receive the client Finished before receiving under that key.
1829 // We need to remember the handshake state from before the client Finished
1830 // in order to successfully verify the client Finished.
1831 const clientFinishedTranscript = await keyschedule.getTranscript();
1832 const clientHandshakeTrafficSecret = keyschedule.clientHandshakeTrafficSecret;
1833 await keyschedule.finalize();
1834 await this.conn._setSendKey(keyschedule.serverApplicationTrafficSecret);
1835 await this.conn._transition(states_SERVER_WAIT_FINISHED, clientHandshakeTrafficSecret, clientFinishedTranscript);
1839 class states_SERVER_WAIT_FINISHED extends states_MidHandshakeState {
1840 async initialize(clientHandshakeTrafficSecret, clientFinishedTranscript) {
1841 this._clientHandshakeTrafficSecret = clientHandshakeTrafficSecret;
1842 this._clientFinishedTranscript = clientFinishedTranscript;
1844 async recvHandshakeMessage(msg) {
1845 if (! (msg instanceof messages_Finished)) {
1846 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
1848 const keyschedule = this.conn._keyschedule;
1849 await keyschedule.verifyFinishedMAC(this._clientHandshakeTrafficSecret, msg.verifyData, this._clientFinishedTranscript);
1850 this._clientHandshakeTrafficSecret = this._clientFinishedTranscript = null;
1851 await this.conn._setRecvKey(keyschedule.clientApplicationTrafficSecret);
1852 await this.conn._transition(states_CONNECTED);
1856 // CONCATENATED MODULE: ./src/keyschedule.js
1857 /* This Source Code Form is subject to the terms of the Mozilla Public
1858 * License, v. 2.0. If a copy of the MPL was not distributed with this
1859 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
1861 // TLS1.3 Key Schedule.
1863 // In this file we implement the "key schedule" from
1864 // https://tools.ietf.org/html/rfc8446#section-7.1, which
1865 // defines how to calculate various keys as the handshake
1866 // state progresses.
1874 // The `KeySchedule` class progresses through three stages corresponding
1875 // to the three phases of the TLS1.3 key schedule:
1891 // It will error out if the calling code attempts to add key material
1892 // in the wrong order.
1894 const STAGE_UNINITIALIZED = 0;
1895 const STAGE_EARLY_SECRET = 1;
1896 const STAGE_HANDSHAKE_SECRET = 2;
1897 const STAGE_MASTER_SECRET = 3;
1899 class keyschedule_KeySchedule {
1901 this.stage = STAGE_UNINITIALIZED;
1902 // WebCrypto doesn't support a rolling hash construct, so we have to
1903 // keep the entire message transcript in memory.
1904 this.transcript = new utils_BufferWriter();
1905 // This tracks the main secret from with other keys are derived at each stage.
1907 // And these are all the various keys we'll derive as the handshake progresses.
1908 this.extBinderKey = null;
1909 this.clientHandshakeTrafficSecret = null;
1910 this.serverHandshakeTrafficSecret = null;
1911 this.clientApplicationTrafficSecret = null;
1912 this.serverApplicationTrafficSecret = null;
1916 // Use the selected PSK (if any) to calculate the "early secret".
1918 psk = zeros(HASH_LENGTH);
1920 if (this.stage !== STAGE_UNINITIALIZED) {
1921 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
1923 this.stage = STAGE_EARLY_SECRET;
1924 this.secret = await hkdfExtract(zeros(HASH_LENGTH), psk);
1925 this.extBinderKey = await this.deriveSecret('ext binder', EMPTY);
1926 this.secret = await this.deriveSecret('derived', EMPTY);
1929 async addECDHE(ecdhe) {
1930 // Mix in the ECDHE output (if any) to calculate the "handshake secret".
1931 if (ecdhe === null) {
1932 ecdhe = zeros(HASH_LENGTH);
1934 if (this.stage !== STAGE_EARLY_SECRET) {
1935 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
1937 this.stage = STAGE_HANDSHAKE_SECRET;
1938 this.extBinderKey = null;
1939 this.secret = await hkdfExtract(this.secret, ecdhe);
1940 this.clientHandshakeTrafficSecret = await this.deriveSecret('c hs traffic');
1941 this.serverHandshakeTrafficSecret = await this.deriveSecret('s hs traffic');
1942 this.secret = await this.deriveSecret('derived', EMPTY);
1946 if (this.stage !== STAGE_HANDSHAKE_SECRET) {
1947 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
1949 this.stage = STAGE_MASTER_SECRET;
1950 this.clientHandshakeTrafficSecret = null;
1951 this.serverHandshakeTrafficSecret = null;
1952 this.secret = await hkdfExtract(this.secret, zeros(HASH_LENGTH));
1953 this.clientApplicationTrafficSecret = await this.deriveSecret('c ap traffic');
1954 this.serverApplicationTrafficSecret = await this.deriveSecret('s ap traffic');
1958 addToTranscript(bytes) {
1959 this.transcript.writeBytes(bytes);
1963 return this.transcript.slice();
1966 async deriveSecret(label, transcript = undefined) {
1967 transcript = transcript || this.getTranscript();
1968 return await hkdfExpandLabel(this.secret, label, await hash(transcript), HASH_LENGTH);
1971 async calculateFinishedMAC(baseKey, transcript = undefined) {
1972 transcript = transcript || this.getTranscript();
1973 const finishedKey = await hkdfExpandLabel(baseKey, 'finished', EMPTY, HASH_LENGTH);
1974 return await hmac(finishedKey, await hash(transcript));
1977 async verifyFinishedMAC(baseKey, mac, transcript = undefined) {
1978 transcript = transcript || this.getTranscript();
1979 const finishedKey = await hkdfExpandLabel(baseKey, 'finished', EMPTY, HASH_LENGTH);
1980 await verifyHmac(finishedKey, mac, await hash(transcript));
1984 // CONCATENATED MODULE: ./src/recordlayer.js
1985 /* This Source Code Form is subject to the terms of the Mozilla Public
1986 * License, v. 2.0. If a copy of the MPL was not distributed with this
1987 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
1990 // This file implements the "record layer" for TLS1.3, as defined in
1991 // https://tools.ietf.org/html/rfc8446#section-5.
1993 // The record layer is responsible for encrypting/decrypting bytes to be
1994 // sent over the wire, including stateful management of sequence numbers
1995 // for the incoming and outgoing stream.
1997 // The main interface is the RecordLayer class, which takes a callback function
1998 // sending data and can be used like so:
2000 // rl = new RecordLayer(async function send_encrypted_data(data) {
2001 // // application-specific sending logic here.
2004 // // Records are sent and received in plaintext by default,
2005 // // until you specify the key to use.
2006 // await rl.setSendKey(key)
2008 // // Send some data by specifying the record type and the bytes.
2009 // // Where allowed by the record type, it will be buffered until
2010 // // explicitly flushed, and then sent by calling the callback.
2011 // await rl.send(RECORD_TYPE.HANDSHAKE, <bytes for a handshake message>)
2012 // await rl.send(RECORD_TYPE.HANDSHAKE, <bytes for another handshake message>)
2015 // // Separate keys are used for sending and receiving.
2016 // rl.setRecvKey(key);
2018 // // When data is received, push it into the RecordLayer
2019 // // and pass a callback that will be called with a [type, bytes]
2020 // // pair for each message parsed from the data.
2021 // rl.recv(dataReceivedFromPeer, async (type, bytes) => {
2023 // case RECORD_TYPE.APPLICATION_DATA:
2024 // // do something with application data
2025 // case RECORD_TYPE.HANDSHAKE:
2026 // // do something with a handshake message
2039 /* eslint-disable sorting/sort-object-props */
2040 const RECORD_TYPE = {
2041 CHANGE_CIPHER_SPEC: 20,
2044 APPLICATION_DATA: 23,
2046 /* eslint-enable sorting/sort-object-props */
2048 // Encrypting at most 2^24 records will force us to stay
2049 // below data limits on AES-GCM encryption key use, and also
2050 // means we can accurately represent the sequence number as
2051 // a javascript double.
2052 const MAX_SEQUENCE_NUMBER = Math.pow(2, 24);
2053 const MAX_RECORD_SIZE = Math.pow(2, 14);
2054 const MAX_ENCRYPTED_RECORD_SIZE = MAX_RECORD_SIZE + 256;
2055 const RECORD_HEADER_SIZE = 5;
2057 // These are some helper classes to manage the encryption/decryption state
2058 // for a particular key.
2060 class recordlayer_CipherState {
2061 constructor(key, iv) {
2067 static async create(baseKey, mode) {
2068 // Derive key and iv per https://tools.ietf.org/html/rfc8446#section-7.3
2069 const key = await prepareKey(await hkdfExpandLabel(baseKey, 'key', EMPTY, KEY_LENGTH), mode);
2070 const iv = await hkdfExpandLabel(baseKey, 'iv', EMPTY, IV_LENGTH);
2071 return new this(key, iv);
2075 // Ref https://tools.ietf.org/html/rfc8446#section-5.3:
2076 // * left-pad the sequence number with zeros to IV_LENGTH
2077 // * xor with the provided iv
2078 // Our sequence numbers are always less than 2^24, so fit in a Uint32
2079 // in the last 4 bytes of the nonce.
2080 const nonce = this.iv.slice();
2081 const dv = new DataView(nonce.buffer, nonce.byteLength - 4, 4);
2082 dv.setUint32(0, dv.getUint32(0) ^ this.seqnum);
2084 if (this.seqnum > MAX_SEQUENCE_NUMBER) {
2085 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
2091 class recordlayer_EncryptionState extends recordlayer_CipherState {
2092 static async create(key) {
2093 return super.create(key, 'encrypt');
2096 async encrypt(plaintext, additionalData) {
2097 return await encrypt(this.key, this.nonce(), plaintext, additionalData);
2101 class recordlayer_DecryptionState extends recordlayer_CipherState {
2102 static async create(key) {
2103 return super.create(key, 'decrypt');
2106 async decrypt(ciphertext, additionalData) {
2107 return await decrypt(this.key, this.nonce(), ciphertext, additionalData);
2111 // The main RecordLayer class.
2113 class recordlayer_RecordLayer {
2114 constructor(sendCallback) {
2115 this.sendCallback = sendCallback;
2116 this._sendEncryptState = null;
2117 this._sendError = null;
2118 this._recvDecryptState = null;
2119 this._recvError = null;
2120 this._pendingRecordType = 0;
2121 this._pendingRecordBuf = null;
2124 async setSendKey(key) {
2126 this._sendEncryptState = await recordlayer_EncryptionState.create(key);
2129 async setRecvKey(key) {
2130 this._recvDecryptState = await recordlayer_DecryptionState.create(key);
2133 async setSendError(err) {
2134 this._sendError = err;
2137 async setRecvError(err) {
2138 this._recvError = err;
2141 async send(type, data) {
2142 if (this._sendError !== null) {
2143 throw this._sendError;
2145 // Forbid sending data that doesn't fit into a single record.
2146 // We do not support fragmentation over multiple records.
2147 if (data.byteLength > MAX_RECORD_SIZE) {
2148 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
2150 // Flush if we're switching to a different record type.
2151 if (this._pendingRecordType && this._pendingRecordType !== type) {
2154 // Flush if we would overflow the max size of a record.
2155 if (this._pendingRecordBuf !== null) {
2156 if (this._pendingRecordBuf.tell() + data.byteLength > MAX_RECORD_SIZE) {
2160 // Start a new pending record if necessary.
2161 // We reserve space at the start of the buffer for the record header,
2162 // which is conveniently always a fixed size.
2163 if (this._pendingRecordBuf === null) {
2164 this._pendingRecordType = type;
2165 this._pendingRecordBuf = new utils_BufferWriter();
2166 this._pendingRecordBuf.incr(RECORD_HEADER_SIZE);
2168 this._pendingRecordBuf.writeBytes(data);
2172 // If there's nothing to flush, bail out early.
2173 // Don't throw `_sendError` if we're not sending anything, because `flush()`
2174 // can be called when we're trying to transition into an error state.
2175 const buf = this._pendingRecordBuf;
2176 let type = this._pendingRecordType;
2179 throw new TLSError(ALERT_DESCRIPTION.INTERNAL_ERROR);
2183 if (this._sendError !== null) {
2184 throw this._sendError;
2186 // If we're encrypting, turn the existing buffer contents into a `TLSInnerPlaintext` by
2187 // appending the type. We don't do any zero-padding, although the spec allows it.
2188 let inflation = 0, innerPlaintext = null;
2189 if (this._sendEncryptState !== null) {
2190 buf.writeUint8(type);
2191 innerPlaintext = buf.slice(RECORD_HEADER_SIZE);
2192 inflation = AEAD_SIZE_INFLATION;
2193 type = RECORD_TYPE.APPLICATION_DATA;
2195 // Write the common header for either `TLSPlaintext` or `TLSCiphertext` record.
2196 const length = buf.tell() - RECORD_HEADER_SIZE + inflation;
2198 buf.writeUint8(type);
2199 buf.writeUint16(VERSION_TLS_1_2);
2200 buf.writeUint16(length);
2201 // Followed by different payload depending on encryption status.
2202 if (this._sendEncryptState !== null) {
2203 const additionalData = buf.slice(0, RECORD_HEADER_SIZE);
2204 const ciphertext = await this._sendEncryptState.encrypt(innerPlaintext, additionalData);
2205 buf.writeBytes(ciphertext);
2209 this._pendingRecordBuf = null;
2210 this._pendingRecordType = 0;
2211 await this.sendCallback(buf.flush());
2215 if (this._recvError !== null) {
2216 throw this._recvError;
2218 // For simplicity, we assume that the given data contains exactly one record.
2219 // Peers using this library will send one record at a time over the websocket
2220 // connection, and we can assume that the server-side websocket bridge will split
2221 // up any traffic into individual records if we ever start interoperating with
2222 // peers using a different TLS implementation.
2223 // Similarly, we assume that handshake messages will not be fragmented across
2224 // multiple records. This should be trivially true for the PSK-only mode used
2225 // by this library, but we may want to relax it in future for interoperability
2226 // with e.g. large ClientHello messages that contain lots of different options.
2227 const buf = new utils_BufferReader(data);
2228 // The data to read is either a TLSPlaintext or TLSCiphertext struct,
2229 // depending on whether record protection has been enabled yet:
2232 // ContentType type;
2233 // ProtocolVersion legacy_record_version;
2235 // opaque fragment[TLSPlaintext.length];
2239 // ContentType opaque_type = application_data; /* 23 */
2240 // ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */
2242 // opaque encrypted_record[TLSCiphertext.length];
2245 let type = buf.readUint8();
2246 // The spec says legacy_record_version "MUST be ignored for all purposes",
2247 // but we know TLS1.3 implementations will only ever emit two possible values,
2248 // so it seems useful to bail out early if we receive anything else.
2249 const version = buf.readUint16();
2250 if (version !== VERSION_TLS_1_2) {
2251 // TLS1.0 is only acceptable on initial plaintext records.
2252 if (this._recvDecryptState !== null || version !== VERSION_TLS_1_0) {
2253 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
2256 const length = buf.readUint16();
2258 if (this._recvDecryptState === null || type === RECORD_TYPE.CHANGE_CIPHER_SPEC) {
2259 [type, plaintext] = await this._readPlaintextRecord(type, length, buf);
2261 [type, plaintext] = await this._readEncryptedRecord(type, length, buf);
2263 // Sanity-check that we received exactly one record.
2264 if (buf.hasMoreBytes()) {
2265 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
2267 return [type, plaintext];
2270 // Helper to read an unencrypted `TLSPlaintext` struct
2272 async _readPlaintextRecord(type, length, buf) {
2273 if (length > MAX_RECORD_SIZE) {
2274 throw new TLSError(ALERT_DESCRIPTION.RECORD_OVERFLOW);
2276 return [type, buf.readBytes(length)];
2279 // Helper to read an encrypted `TLSCiphertext` struct,
2280 // decrypting it into plaintext.
2282 async _readEncryptedRecord(type, length, buf) {
2283 if (length > MAX_ENCRYPTED_RECORD_SIZE) {
2284 throw new TLSError(ALERT_DESCRIPTION.RECORD_OVERFLOW);
2286 // The outer type for encrypted records is always APPLICATION_DATA.
2287 if (type !== RECORD_TYPE.APPLICATION_DATA) {
2288 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
2290 // Decrypt and decode the contained `TLSInnerPlaintext` struct:
2293 // opaque content[TLSPlaintext.length];
2294 // ContentType type;
2295 // uint8 zeros[length_of_padding];
2296 // } TLSInnerPlaintext;
2298 // The additional data for the decryption is the `TLSCiphertext` record
2299 // header, which is a fixed size and immediately prior to current buffer position.
2300 buf.incr(-RECORD_HEADER_SIZE);
2301 const additionalData = buf.readBytes(RECORD_HEADER_SIZE);
2302 const ciphertext = buf.readBytes(length);
2303 const paddedPlaintext = await this._recvDecryptState.decrypt(ciphertext, additionalData);
2304 // We have to scan backwards over the zero padding at the end of the struct
2305 // in order to find the non-zero `type` byte.
2307 for (i = paddedPlaintext.byteLength - 1; i >= 0; i--) {
2308 if (paddedPlaintext[i] !== 0) {
2313 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
2315 type = paddedPlaintext[i];
2316 // `change_cipher_spec` records must always be plaintext.
2317 if (type === RECORD_TYPE.CHANGE_CIPHER_SPEC) {
2318 throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
2320 return [type, paddedPlaintext.slice(0, i)];
2324 // CONCATENATED MODULE: ./src/tlsconnection.js
2325 /* This Source Code Form is subject to the terms of the Mozilla Public
2326 * License, v. 2.0. If a copy of the MPL was not distributed with this
2327 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
2329 // The top-level APIs offered by this module are `ClientConnection` and
2330 // `ServerConnection` classes, which provide authenticated and encrypted
2331 // communication via the "externally-provisioned PSK" mode of TLS1.3.
2332 // They each take a callback to be used for sending data to the remote peer,
2333 // and operate like this:
2335 // conn = await ClientConnection.create(psk, pskId, async function send_data_to_server(data) {
2336 // // application-specific sending logic here.
2339 // // Send data to the server by calling `send`,
2340 // // which will use the callback provided in the constructor.
2341 // // A single `send()` by the application may result in multiple
2342 // // invokations of the callback.
2344 // await conn.send('application-level data')
2346 // // When data is received from the server, push it into
2347 // // the connection and let it return any decrypted app-level data.
2348 // // There might not be any app-level data if it was a protocol control
2349 // // message, and the receipt of the data might trigger additional calls
2350 // // to the send callback for protocol control purposes.
2352 // serverSocket.on('data', async encrypted_data => {
2353 // const plaintext = await conn.recv(data)
2354 // if (plaintext !== null) {
2355 // do_something_with_app_level_data(plaintext)
2359 // // It's good practice to explicitly close the connection
2360 // // when finished. This will send a "closed" notification
2361 // // to the server.
2363 // await conn.close()
2365 // // When the peer sends a "closed" notification it will show up
2366 // // as a `TLSCloseNotify` exception from recv:
2369 // data = await conn.recv(data);
2371 // if (! (err instanceof TLSCloseNotify) { throw err }
2372 // do_something_to_cleanly_close_data_connection();
2375 // The `ServerConnection` API operates similarly; the distinction is mainly
2376 // in which side is expected to send vs receieve during the protocol handshake.
2387 class tlsconnection_Connection {
2388 constructor(psk, pskId, sendCallback) {
2389 this.psk = assertIsBytes(psk);
2390 this.pskId = assertIsBytes(pskId);
2391 this.connected = new Promise((resolve, reject) => {
2392 this._onConnectionSuccess = resolve;
2393 this._onConnectionFailure = reject;
2395 this._state = new UNINITIALIZED(this);
2396 this._handshakeRecvBuffer = null;
2397 this._hasSeenChangeCipherSpec = false;
2398 this._recordlayer = new recordlayer_RecordLayer(sendCallback);
2399 this._keyschedule = new keyschedule_KeySchedule();
2400 this._lastPromise = Promise.resolve();
2403 // Subclasses will override this with some async initialization logic.
2404 static async create(psk, pskId, sendCallback) {
2405 return new this(psk, pskId, sendCallback);
2408 // These are the three public API methods that consumers can use
2409 // to send and receive data encrypted with TLS1.3.
2412 assertIsBytes(data);
2413 await this.connected;
2414 await this._synchronized(async () => {
2415 await this._state.sendApplicationData(data);
2420 assertIsBytes(data);
2421 return await this._synchronized(async () => {
2422 // Decrypt the data using the record layer.
2423 // We expect to receive precisely one record at a time.
2424 const [type, bytes] = await this._recordlayer.recv(data);
2425 // Dispatch based on the type of the record.
2427 case RECORD_TYPE.CHANGE_CIPHER_SPEC:
2428 await this._state.recvChangeCipherSpec(bytes);
2430 case RECORD_TYPE.ALERT:
2431 await this._state.recvAlertMessage(TLSAlert.fromBytes(bytes));
2433 case RECORD_TYPE.APPLICATION_DATA:
2434 return await this._state.recvApplicationData(bytes);
2435 case RECORD_TYPE.HANDSHAKE:
2436 // Multiple handshake messages may be coalesced into a single record.
2437 // Store the in-progress record buffer on `this` so that we can guard
2438 // against handshake messages that span a change in keys.
2439 this._handshakeRecvBuffer = new utils_BufferReader(bytes);
2440 if (! this._handshakeRecvBuffer.hasMoreBytes()) {
2441 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
2444 // Each handshake messages has a type and length prefix, per
2445 // https://tools.ietf.org/html/rfc8446#appendix-B.3
2446 this._handshakeRecvBuffer.incr(1);
2447 const mlength = this._handshakeRecvBuffer.readUint24();
2448 this._handshakeRecvBuffer.incr(-4);
2449 const messageBytes = this._handshakeRecvBuffer.readBytes(mlength + 4);
2450 this._keyschedule.addToTranscript(messageBytes);
2451 await this._state.recvHandshakeMessage(messages_HandshakeMessage.fromBytes(messageBytes));
2452 } while (this._handshakeRecvBuffer.hasMoreBytes());
2453 this._handshakeRecvBuffer = null;
2456 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
2462 await this._synchronized(async () => {
2463 await this._state.close();
2467 // Ensure that async functions execute one at a time,
2468 // by waiting for the previous call to `_synchronized()` to complete
2469 // before starting a new one. This helps ensure that we complete
2470 // one state-machine transition before starting to do the next.
2471 // It's also a convenient place to catch and alert on errors.
2474 const nextPromise = this._lastPromise.then(() => {
2476 }).catch(async err => {
2477 if (err instanceof TLSCloseNotify) {
2480 await this._state.handleErrorAndRethrow(err);
2482 // We don't want to hold on to the return value or error,
2483 // just synchronize on the fact that it completed.
2484 this._lastPromise = nextPromise.then(noop, noop);
2488 // This drives internal transition of the state-machine,
2489 // ensuring that the new state is properly initialized.
2491 async _transition(State, ...args) {
2492 this._state = new State(this);
2493 await this._state.initialize(...args);
2494 await this._recordlayer.flush();
2497 // These are helpers to allow the State to manipulate the recordlayer
2498 // and send out various types of data.
2500 async _sendApplicationData(bytes) {
2501 await this._recordlayer.send(RECORD_TYPE.APPLICATION_DATA, bytes);
2502 await this._recordlayer.flush();
2505 async _sendHandshakeMessage(msg) {
2506 await this._sendHandshakeMessageBytes(msg.toBytes());
2509 async _sendHandshakeMessageBytes(bytes) {
2510 this._keyschedule.addToTranscript(bytes);
2511 await this._recordlayer.send(RECORD_TYPE.HANDSHAKE, bytes);
2512 // Don't flush after each handshake message, since we can probably
2513 // coalesce multiple messages into a single record.
2516 async _sendAlertMessage(err) {
2517 await this._recordlayer.send(RECORD_TYPE.ALERT, err.toBytes());
2518 await this._recordlayer.flush();
2521 async _sendChangeCipherSpec() {
2522 await this._recordlayer.send(RECORD_TYPE.CHANGE_CIPHER_SPEC, new Uint8Array([0x01]));
2523 await this._recordlayer.flush();
2526 async _setSendKey(key) {
2527 return await this._recordlayer.setSendKey(key);
2530 async _setRecvKey(key) {
2531 // Handshake messages that change keys must be on a record boundary.
2532 if (this._handshakeRecvBuffer && this._handshakeRecvBuffer.hasMoreBytes()) {
2533 throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
2535 return await this._recordlayer.setRecvKey(key);
2538 _setConnectionSuccess() {
2539 if (this._onConnectionSuccess !== null) {
2540 this._onConnectionSuccess();
2541 this._onConnectionSuccess = null;
2542 this._onConnectionFailure = null;
2546 _setConnectionFailure(err) {
2547 if (this._onConnectionFailure !== null) {
2548 this._onConnectionFailure(err);
2549 this._onConnectionSuccess = null;
2550 this._onConnectionFailure = null;
2554 _closeForSend(alert) {
2555 this._recordlayer.setSendError(alert);
2558 _closeForRecv(alert) {
2559 this._recordlayer.setRecvError(alert);
2563 class tlsconnection_ClientConnection extends tlsconnection_Connection {
2564 static async create(psk, pskId, sendCallback) {
2565 const instance = await super.create(psk, pskId, sendCallback);
2566 await instance._transition(states_CLIENT_START);
2571 class tlsconnection_ServerConnection extends tlsconnection_Connection {
2572 static async create(psk, pskId, sendCallback) {
2573 const instance = await super.create(psk, pskId, sendCallback);
2574 await instance._transition(states_SERVER_START);
2579 // CONCATENATED MODULE: ./node_modules/event-target-shim/dist/event-target-shim.mjs
2581 * @author Toru Nagashima <https://github.com/mysticatea>
2582 * @copyright 2015 Toru Nagashima. All rights reserved.
2583 * See LICENSE file in root directory for full license.
2586 * @typedef {object} PrivateData
2587 * @property {EventTarget} eventTarget The event target.
2588 * @property {{type:string}} event The original event object.
2589 * @property {number} eventPhase The current event phase.
2590 * @property {EventTarget|null} currentTarget The current event target.
2591 * @property {boolean} canceled The flag to prevent default.
2592 * @property {boolean} stopped The flag to stop propagation.
2593 * @property {boolean} immediateStopped The flag to stop propagation immediately.
2594 * @property {Function|null} passiveListener The listener if the current listener is passive. Otherwise this is null.
2595 * @property {number} timeStamp The unix time.
2600 * Private data for event wrappers.
2601 * @type {WeakMap<Event, PrivateData>}
2604 const privateData = new WeakMap();
2607 * Cache for wrapper classes.
2608 * @type {WeakMap<Object, Function>}
2611 const wrappers = new WeakMap();
2615 * @param {Event} event The event object to get private data.
2616 * @returns {PrivateData} The private data of the event.
2619 function pd(event) {
2620 const retv = privateData.get(event);
2623 "'this' is expected an Event object, but got",
2630 * https://dom.spec.whatwg.org/#set-the-canceled-flag
2631 * @param data {PrivateData} private data.
2633 function setCancelFlag(data) {
2634 if (data.passiveListener != null) {
2636 typeof console !== "undefined" &&
2637 typeof console.error === "function"
2640 "Unable to preventDefault inside passive event listener invocation.",
2641 data.passiveListener
2646 if (!data.event.cancelable) {
2650 data.canceled = true;
2651 if (typeof data.event.preventDefault === "function") {
2652 data.event.preventDefault();
2657 * @see https://dom.spec.whatwg.org/#interface-event
2661 * The event wrapper.
2663 * @param {EventTarget} eventTarget The event target of this dispatching.
2664 * @param {Event|{type:string}} event The original event to wrap.
2666 function Event(eventTarget, event) {
2667 privateData.set(this, {
2671 currentTarget: eventTarget,
2674 immediateStopped: false,
2675 passiveListener: null,
2676 timeStamp: event.timeStamp || Date.now(),
2679 // https://heycam.github.io/webidl/#Unforgeable
2680 Object.defineProperty(this, "isTrusted", { value: false, enumerable: true });
2683 const keys = Object.keys(event);
2684 for (let i = 0; i < keys.length; ++i) {
2685 const key = keys[i];
2686 if (!(key in this)) {
2687 Object.defineProperty(this, key, defineRedirectDescriptor(key));
2692 // Should be enumerable, but class methods are not enumerable.
2695 * The type of this event.
2699 return pd(this).event.type
2703 * The target of this event.
2704 * @type {EventTarget}
2707 return pd(this).eventTarget
2711 * The target of this event.
2712 * @type {EventTarget}
2714 get currentTarget() {
2715 return pd(this).currentTarget
2719 * @returns {EventTarget[]} The composed path of this event.
2722 const currentTarget = pd(this).currentTarget;
2723 if (currentTarget == null) {
2726 return [currentTarget]
2738 * Constant of CAPTURING_PHASE.
2741 get CAPTURING_PHASE() {
2746 * Constant of AT_TARGET.
2754 * Constant of BUBBLING_PHASE.
2757 get BUBBLING_PHASE() {
2762 * The target of this event.
2766 return pd(this).eventPhase
2770 * Stop event bubbling.
2774 const data = pd(this);
2776 data.stopped = true;
2777 if (typeof data.event.stopPropagation === "function") {
2778 data.event.stopPropagation();
2783 * Stop event bubbling.
2786 stopImmediatePropagation() {
2787 const data = pd(this);
2789 data.stopped = true;
2790 data.immediateStopped = true;
2791 if (typeof data.event.stopImmediatePropagation === "function") {
2792 data.event.stopImmediatePropagation();
2797 * The flag to be bubbling.
2801 return Boolean(pd(this).event.bubbles)
2805 * The flag to be cancelable.
2809 return Boolean(pd(this).event.cancelable)
2813 * Cancel this event.
2817 setCancelFlag(pd(this));
2821 * The flag to indicate cancellation state.
2824 get defaultPrevented() {
2825 return pd(this).canceled
2829 * The flag to be composed.
2833 return Boolean(pd(this).event.composed)
2837 * The unix time of this event.
2841 return pd(this).timeStamp
2845 * The target of this event.
2846 * @type {EventTarget}
2850 return pd(this).eventTarget
2854 * The flag to stop event bubbling.
2858 get cancelBubble() {
2859 return pd(this).stopped
2861 set cancelBubble(value) {
2865 const data = pd(this);
2867 data.stopped = true;
2868 if (typeof data.event.cancelBubble === "boolean") {
2869 data.event.cancelBubble = true;
2874 * The flag to indicate cancellation state.
2879 return !pd(this).canceled
2881 set returnValue(value) {
2883 setCancelFlag(pd(this));
2888 * Initialize this event object. But do nothing under event dispatching.
2889 * @param {string} type The event type.
2890 * @param {boolean} [bubbles=false] The flag to be possible to bubble up.
2891 * @param {boolean} [cancelable=false] The flag to be possible to cancel.
2899 // `constructor` is not enumerable.
2900 Object.defineProperty(Event.prototype, "constructor", {
2906 // Ensure `event instanceof window.Event` is `true`.
2907 if (typeof window !== "undefined" && typeof window.Event !== "undefined") {
2908 Object.setPrototypeOf(Event.prototype, window.Event.prototype);
2910 // Make association for wrappers.
2911 wrappers.set(window.Event.prototype, Event);
2915 * Get the property descriptor to redirect a given property.
2916 * @param {string} key Property name to define property descriptor.
2917 * @returns {PropertyDescriptor} The property descriptor to redirect the property.
2920 function defineRedirectDescriptor(key) {
2923 return pd(this).event[key]
2926 pd(this).event[key] = value;
2934 * Get the property descriptor to call a given method property.
2935 * @param {string} key Property name to define property descriptor.
2936 * @returns {PropertyDescriptor} The property descriptor to call the method property.
2939 function defineCallDescriptor(key) {
2942 const event = pd(this).event;
2943 return event[key].apply(event, arguments)
2951 * Define new wrapper class.
2952 * @param {Function} BaseEvent The base wrapper class.
2953 * @param {Object} proto The prototype of the original event.
2954 * @returns {Function} The defined wrapper class.
2957 function defineWrapper(BaseEvent, proto) {
2958 const keys = Object.keys(proto);
2959 if (keys.length === 0) {
2964 function CustomEvent(eventTarget, event) {
2965 BaseEvent.call(this, eventTarget, event);
2968 CustomEvent.prototype = Object.create(BaseEvent.prototype, {
2969 constructor: { value: CustomEvent, configurable: true, writable: true },
2972 // Define accessors.
2973 for (let i = 0; i < keys.length; ++i) {
2974 const key = keys[i];
2975 if (!(key in BaseEvent.prototype)) {
2976 const descriptor = Object.getOwnPropertyDescriptor(proto, key);
2977 const isFunc = typeof descriptor.value === "function";
2978 Object.defineProperty(
2979 CustomEvent.prototype,
2982 ? defineCallDescriptor(key)
2983 : defineRedirectDescriptor(key)
2992 * Get the wrapper class of a given prototype.
2993 * @param {Object} proto The prototype of the original event to get its wrapper.
2994 * @returns {Function} The wrapper class.
2997 function getWrapper(proto) {
2998 if (proto == null || proto === Object.prototype) {
3002 let wrapper = wrappers.get(proto);
3003 if (wrapper == null) {
3004 wrapper = defineWrapper(getWrapper(Object.getPrototypeOf(proto)), proto);
3005 wrappers.set(proto, wrapper);
3011 * Wrap a given event to management a dispatching.
3012 * @param {EventTarget} eventTarget The event target of this dispatching.
3013 * @param {Object} event The event to wrap.
3014 * @returns {Event} The wrapper instance.
3017 function wrapEvent(eventTarget, event) {
3018 const Wrapper = getWrapper(Object.getPrototypeOf(event));
3019 return new Wrapper(eventTarget, event)
3023 * Get the immediateStopped flag of a given event.
3024 * @param {Event} event The event to get.
3025 * @returns {boolean} The flag to stop propagation immediately.
3028 function isStopped(event) {
3029 return pd(event).immediateStopped
3033 * Set the current event phase of a given event.
3034 * @param {Event} event The event to set current target.
3035 * @param {number} eventPhase New event phase.
3039 function setEventPhase(event, eventPhase) {
3040 pd(event).eventPhase = eventPhase;
3044 * Set the current target of a given event.
3045 * @param {Event} event The event to set current target.
3046 * @param {EventTarget|null} currentTarget New current target.
3050 function setCurrentTarget(event, currentTarget) {
3051 pd(event).currentTarget = currentTarget;
3055 * Set a passive listener of a given event.
3056 * @param {Event} event The event to set current target.
3057 * @param {Function|null} passiveListener New passive listener.
3061 function setPassiveListener(event, passiveListener) {
3062 pd(event).passiveListener = passiveListener;
3066 * @typedef {object} ListenerNode
3067 * @property {Function} listener
3068 * @property {1|2|3} listenerType
3069 * @property {boolean} passive
3070 * @property {boolean} once
3071 * @property {ListenerNode|null} next
3076 * @type {WeakMap<object, Map<string, ListenerNode>>}
3079 const listenersMap = new WeakMap();
3084 const ATTRIBUTE = 3;
3087 * Check whether a given value is an object or not.
3088 * @param {any} x The value to check.
3089 * @returns {boolean} `true` if the value is an object.
3091 function isObject(x) {
3092 return x !== null && typeof x === "object" //eslint-disable-line no-restricted-syntax
3097 * @param {EventTarget} eventTarget The event target to get.
3098 * @returns {Map<string, ListenerNode>} The listeners.
3101 function getListeners(eventTarget) {
3102 const listeners = listenersMap.get(eventTarget);
3103 if (listeners == null) {
3104 throw new TypeError(
3105 "'this' is expected an EventTarget object, but got another value."
3112 * Get the property descriptor for the event attribute of a given event.
3113 * @param {string} eventName The event name to get property descriptor.
3114 * @returns {PropertyDescriptor} The property descriptor.
3117 function defineEventAttributeDescriptor(eventName) {
3120 const listeners = getListeners(this);
3121 let node = listeners.get(eventName);
3122 while (node != null) {
3123 if (node.listenerType === ATTRIBUTE) {
3124 return node.listener
3132 if (typeof listener !== "function" && !isObject(listener)) {
3133 listener = null; // eslint-disable-line no-param-reassign
3135 const listeners = getListeners(this);
3137 // Traverse to the tail while removing old value.
3139 let node = listeners.get(eventName);
3140 while (node != null) {
3141 if (node.listenerType === ATTRIBUTE) {
3142 // Remove old value.
3143 if (prev !== null) {
3144 prev.next = node.next;
3145 } else if (node.next !== null) {
3146 listeners.set(eventName, node.next);
3148 listeners.delete(eventName);
3158 if (listener !== null) {
3161 listenerType: ATTRIBUTE,
3166 if (prev === null) {
3167 listeners.set(eventName, newNode);
3169 prev.next = newNode;
3179 * Define an event attribute (e.g. `eventTarget.onclick`).
3180 * @param {Object} eventTargetPrototype The event target prototype to define an event attrbite.
3181 * @param {string} eventName The event name to define.
3184 function defineEventAttribute(eventTargetPrototype, eventName) {
3185 Object.defineProperty(
3186 eventTargetPrototype,
3188 defineEventAttributeDescriptor(eventName)
3193 * Define a custom EventTarget with event attributes.
3194 * @param {string[]} eventNames Event names for event attributes.
3195 * @returns {EventTarget} The custom EventTarget.
3198 function defineCustomEventTarget(eventNames) {
3199 /** CustomEventTarget */
3200 function CustomEventTarget() {
3201 EventTarget.call(this);
3204 CustomEventTarget.prototype = Object.create(EventTarget.prototype, {
3206 value: CustomEventTarget,
3212 for (let i = 0; i < eventNames.length; ++i) {
3213 defineEventAttribute(CustomEventTarget.prototype, eventNames[i]);
3216 return CustomEventTarget
3222 * - This is constructor if no arguments.
3223 * - This is a function which returns a CustomEventTarget constructor if there are arguments.
3227 * class A extends EventTarget {}
3228 * class B extends EventTarget("message") {}
3229 * class C extends EventTarget("message", "error") {}
3230 * class D extends EventTarget(["message", "error"]) {}
3232 function EventTarget() {
3233 /*eslint-disable consistent-return */
3234 if (this instanceof EventTarget) {
3235 listenersMap.set(this, new Map());
3238 if (arguments.length === 1 && Array.isArray(arguments[0])) {
3239 return defineCustomEventTarget(arguments[0])
3241 if (arguments.length > 0) {
3242 const types = new Array(arguments.length);
3243 for (let i = 0; i < arguments.length; ++i) {
3244 types[i] = arguments[i];
3246 return defineCustomEventTarget(types)
3248 throw new TypeError("Cannot call a class as a function")
3249 /*eslint-enable consistent-return */
3252 // Should be enumerable, but class methods are not enumerable.
3253 EventTarget.prototype = {
3255 * Add a given listener to this event target.
3256 * @param {string} eventName The event name to add.
3257 * @param {Function} listener The listener to add.
3258 * @param {boolean|{capture?:boolean,passive?:boolean,once?:boolean}} [options] The options for this listener.
3261 addEventListener(eventName, listener, options) {
3262 if (listener == null) {
3265 if (typeof listener !== "function" && !isObject(listener)) {
3266 throw new TypeError("'listener' should be a function or an object.")
3269 const listeners = getListeners(this);
3270 const optionsIsObj = isObject(options);
3271 const capture = optionsIsObj
3272 ? Boolean(options.capture)
3274 const listenerType = capture ? CAPTURE : BUBBLE;
3278 passive: optionsIsObj && Boolean(options.passive),
3279 once: optionsIsObj && Boolean(options.once),
3283 // Set it as the first node if the first node is null.
3284 let node = listeners.get(eventName);
3285 if (node === undefined) {
3286 listeners.set(eventName, newNode);
3290 // Traverse to the tail while checking duplication..
3292 while (node != null) {
3294 node.listener === listener &&
3295 node.listenerType === listenerType
3297 // Should ignore duplication.
3305 prev.next = newNode;
3309 * Remove a given listener from this event target.
3310 * @param {string} eventName The event name to remove.
3311 * @param {Function} listener The listener to remove.
3312 * @param {boolean|{capture?:boolean,passive?:boolean,once?:boolean}} [options] The options for this listener.
3315 removeEventListener(eventName, listener, options) {
3316 if (listener == null) {
3320 const listeners = getListeners(this);
3321 const capture = isObject(options)
3322 ? Boolean(options.capture)
3324 const listenerType = capture ? CAPTURE : BUBBLE;
3327 let node = listeners.get(eventName);
3328 while (node != null) {
3330 node.listener === listener &&
3331 node.listenerType === listenerType
3333 if (prev !== null) {
3334 prev.next = node.next;
3335 } else if (node.next !== null) {
3336 listeners.set(eventName, node.next);
3338 listeners.delete(eventName);
3349 * Dispatch a given event.
3350 * @param {Event|{type:string}} event The event to dispatch.
3351 * @returns {boolean} `false` if canceled.
3353 dispatchEvent(event) {
3354 if (event == null || typeof event.type !== "string") {
3355 throw new TypeError('"event.type" should be a string.')
3358 // If listeners aren't registered, terminate.
3359 const listeners = getListeners(this);
3360 const eventName = event.type;
3361 let node = listeners.get(eventName);
3366 // Since we cannot rewrite several properties, so wrap object.
3367 const wrappedEvent = wrapEvent(this, event);
3369 // This doesn't process capturing phase and bubbling phase.
3370 // This isn't participating in a tree.
3372 while (node != null) {
3373 // Remove this listener if it's once
3375 if (prev !== null) {
3376 prev.next = node.next;
3377 } else if (node.next !== null) {
3378 listeners.set(eventName, node.next);
3380 listeners.delete(eventName);
3386 // Call this listener
3389 node.passive ? node.listener : null
3391 if (typeof node.listener === "function") {
3393 node.listener.call(this, wrappedEvent);
3396 typeof console !== "undefined" &&
3397 typeof console.error === "function"
3403 node.listenerType !== ATTRIBUTE &&
3404 typeof node.listener.handleEvent === "function"
3406 node.listener.handleEvent(wrappedEvent);
3409 // Break if `event.stopImmediatePropagation` was called.
3410 if (isStopped(wrappedEvent)) {
3416 setPassiveListener(wrappedEvent, null);
3417 setEventPhase(wrappedEvent, 0);
3418 setCurrentTarget(wrappedEvent, null);
3420 return !wrappedEvent.defaultPrevented
3424 // `constructor` is not enumerable.
3425 Object.defineProperty(EventTarget.prototype, "constructor", {
3431 // Ensure `eventTarget instanceof window.EventTarget` is `true`.
3433 typeof window !== "undefined" &&
3434 typeof window.EventTarget !== "undefined"
3436 Object.setPrototypeOf(EventTarget.prototype, window.EventTarget.prototype);
3439 /* harmony default export */ var event_target_shim = (EventTarget);
3442 // CONCATENATED MODULE: ./src/index.js
3443 /* This Source Code Form is subject to the terms of the Mozilla Public
3444 * License, v. 2.0. If a copy of the MPL was not distributed with this
3445 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
3447 // A wrapper that combines a WebSocket to the channelserver
3448 // with some client-side encryption for securing the channel.
3450 // This code is responsible for the event handling and the consumer API.
3451 // All the details of encrypting the messages are delegated to`./tlsconnection.js`.
3459 const CLOSE_FLUSH_BUFFER_INTERVAL_MS = 200;
3460 const CLOSE_FLUSH_BUFFER_MAX_TRIES = 5;
3462 class src_PairingChannel extends EventTarget {
3463 constructor(channelId, channelKey, socket, connection) {
3465 this._channelId = channelId;
3466 this._channelKey = channelKey;
3467 this._socket = socket;
3468 this._connection = connection;
3469 this._selfClosed = false;
3470 this._peerClosed = false;
3471 this._setupListeners();
3475 * Create a new pairing channel.
3477 * This will open a channel on the channelserver, and generate a random client-side
3478 * encryption key. When the promise resolves, `this.channelId` and `this.channelKey`
3479 * can be transferred to another client to allow it to securely connect to the channel.
3481 * @returns Promise<PairingChannel>
3483 static create(channelServerURI) {
3484 const wsURI = new URL('/v1/ws/', channelServerURI).href;
3485 const channelKey = crypto.getRandomValues(new Uint8Array(32));
3486 // The one who creates the channel plays the role of 'server' in the underlying TLS exchange.
3487 return this._makePairingChannel(wsURI, tlsconnection_ServerConnection, channelKey);
3491 * Connect to an existing pairing channel.
3493 * This will connect to a channel on the channelserver previously established by
3494 * another client calling `create`. The `channelId` and `channelKey` must have been
3495 * obtained via some out-of-band mechanism (such as by scanning from a QR code).
3497 * @returns Promise<PairingChannel>
3499 static connect(channelServerURI, channelId, channelKey) {
3500 const wsURI = new URL(`/v1/ws/${channelId}`, channelServerURI).href;
3501 // The one who connects to an existing channel plays the role of 'client'
3502 // in the underlying TLS exchange.
3503 return this._makePairingChannel(wsURI, tlsconnection_ClientConnection, channelKey);
3506 static _makePairingChannel(wsUri, ConnectionClass, psk) {
3507 const socket = new WebSocket(wsUri);
3508 return new Promise((resolve, reject) => {
3509 // eslint-disable-next-line prefer-const
3511 const onConnectionError = async () => {
3513 reject(new Error('Error while creating the pairing channel'));
3515 const onFirstMessage = async event => {
3518 // The channelserver echos back the channel id, and we use it as an
3519 // additional input to the TLS handshake via the "psk id" field.
3520 const {channelid: channelId} = JSON.parse(event.data);
3521 const pskId = utf8ToBytes(channelId);
3522 const connection = await ConnectionClass.create(psk, pskId, data => {
3523 // Send data by forwarding it via the channelserver websocket.
3524 // The TLS connection gives us `data` as raw bytes, but channelserver
3525 // expects b64urlsafe strings, because it wraps them in a JSON object envelope.
3526 socket.send(bytesToBase64url(data));
3528 const instance = new this(channelId, psk, socket, connection);
3534 stopListening = () => {
3535 socket.removeEventListener('close', onConnectionError);
3536 socket.removeEventListener('error', onConnectionError);
3537 socket.removeEventListener('message', onFirstMessage);
3539 socket.addEventListener('close', onConnectionError);
3540 socket.addEventListener('error', onConnectionError);
3541 socket.addEventListener('message', onFirstMessage);
3546 this._socket.addEventListener('message', async event => {
3548 // When we receive data from the channelserver, pump it through the TLS connection
3549 // to decrypt it, then echo it back out to consumers as an event.
3550 const channelServerEnvelope = JSON.parse(event.data);
3551 const payload = await this._connection.recv(base64urlToBytes(channelServerEnvelope.message));
3552 if (payload !== null) {
3553 const data = JSON.parse(bytesToUtf8(payload));
3554 this.dispatchEvent(new CustomEvent('message', {
3557 sender: channelServerEnvelope.sender,
3563 // The underlying TLS connection will signal a clean shutdown of the channel
3564 // by throwing a special error, because it doesn't really have a better
3565 // signally mechanism available.
3566 if (error instanceof TLSCloseNotify) {
3567 this._peerClosed = true;
3568 if (this._selfClosed) {
3571 event = new CustomEvent('close');
3573 event = new CustomEvent('error', {
3579 this.dispatchEvent(event);
3582 // Relay the WebSocket events.
3583 this._socket.addEventListener('error', () => {
3585 // The dispatched event that we receive has no useful information.
3586 this.dispatchEvent(new CustomEvent('error', {
3588 error: new Error('WebSocket error.'),
3592 // In TLS, the peer has to explicitly send a close notification,
3593 // which we dispatch above. Unexpected socket close is an error.
3594 this._socket.addEventListener('close', () => {
3596 if (! this._peerClosed) {
3597 this.dispatchEvent(new CustomEvent('error', {
3599 error: new Error('WebSocket unexpectedly closed'),
3607 * @param {Object} data
3610 const payload = utf8ToBytes(JSON.stringify(data));
3611 await this._connection.send(payload);
3615 this._selfClosed = true;
3616 await this._connection.close();
3618 // Ensure all queued bytes have been sent before closing the connection.
3620 while (this._socket.bufferedAmount > 0) {
3621 if (++tries > CLOSE_FLUSH_BUFFER_MAX_TRIES) {
3622 throw new Error('Could not flush the outgoing buffer in time.');
3624 await new Promise(res => setTimeout(res, CLOSE_FLUSH_BUFFER_INTERVAL_MS));
3627 // If the peer hasn't closed, we might still receive some data.
3628 if (this._peerClosed) {
3636 this._socket.close();
3637 this._socket = null;
3638 this._connection = null;
3643 return (! this._socket) || (this._socket.readyState === 3);
3647 return this._channelId;
3651 return this._channelKey;
3655 // Re-export helpful utilities for calling code to use.
3658 // For running tests using the built bundle,
3659 // expose a bunch of implementation details.
3667 const _internals = {
3668 arrayToBytes: arrayToBytes,
3669 BufferReader: utils_BufferReader,
3670 BufferWriter: utils_BufferWriter,
3671 bytesAreEqual: bytesAreEqual,
3672 bytesToHex: bytesToHex,
3673 bytesToUtf8: bytesToUtf8,
3674 ClientConnection: tlsconnection_ClientConnection,
3675 Connection: tlsconnection_Connection,
3676 DecryptionState: recordlayer_DecryptionState,
3677 EncryptedExtensions: EncryptedExtensions,
3678 EncryptionState: recordlayer_EncryptionState,
3679 Finished: messages_Finished,
3680 HASH_LENGTH: HASH_LENGTH,
3681 hexToBytes: hexToBytes,
3682 hkdfExpand: hkdfExpand,
3683 KeySchedule: keyschedule_KeySchedule,
3684 NewSessionTicket: messages_NewSessionTicket,
3685 RecordLayer: recordlayer_RecordLayer,
3686 ServerConnection: tlsconnection_ServerConnection,
3687 utf8ToBytes: utf8ToBytes,
3693 /******/ ])["PairingChannel"];