@wireapp/cryptobox
Version:
High-level API with persistent storage for Proteus.
1,494 lines (1,345 loc) • 160 kB
JavaScript
/*! @wireapp/cryptobox v8.3.8 */
var cryptobox =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 24);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
module.exports = Proteus;
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [0, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var proteus_1 = __webpack_require__(0);
var proteus_2 = __webpack_require__(0);
var proteus_3 = __webpack_require__(0);
var root_1 = __webpack_require__(4);
var CryptoboxSession_1 = __webpack_require__(5);
var DecryptionError_1 = __webpack_require__(2);
var InvalidPreKeyFormatError_1 = __webpack_require__(6);
var root_2 = __webpack_require__(3);
var lru_cache_1 = __webpack_require__(9);
var priority_queue_1 = __webpack_require__(20);
var EventEmitter = __webpack_require__(21);
var logdown = __webpack_require__(22);
var VERSION = __webpack_require__(23).version;
var Cryptobox = (function (_super) {
__extends(Cryptobox, _super);
function Cryptobox(engine, minimumAmountOfPreKeys) {
if (minimumAmountOfPreKeys === void 0) { minimumAmountOfPreKeys = 1; }
var _this = _super.call(this) || this;
_this.logger = logdown('@wireapp/cryptobox/Cryptobox', {
logger: console,
markdown: false,
});
_this.queues = new lru_cache_1.default(1000);
if (!engine) {
throw new Error("You cannot initialize Cryptobox without a storage component.");
}
if (minimumAmountOfPreKeys > proteus_1.keys.PreKey.MAX_PREKEY_ID) {
minimumAmountOfPreKeys = proteus_1.keys.PreKey.MAX_PREKEY_ID;
}
_this.cachedSessions = new lru_cache_1.default(1000);
_this.minimumAmountOfPreKeys = minimumAmountOfPreKeys;
_this.store = new root_2.CryptoboxCRUDStore(engine);
var storageEngine = engine.constructor.name;
_this.logger.log("Constructed Cryptobox. Minimum amount of PreKeys is \"" + minimumAmountOfPreKeys + "\". Storage engine is \"" + storageEngine + "\".");
return _this;
}
Cryptobox.prototype.get_session_queue = function (session_id) {
var queue = this.queues.get(session_id);
if (!queue) {
queue = new priority_queue_1.PriorityQueue({ maxRetries: 0 });
this.queues.set(session_id, queue);
}
return queue;
};
Cryptobox.prototype.save_session_in_cache = function (session) {
this.logger.log("Saving Session with ID \"" + session.id + "\" in cache...");
this.cachedSessions.set(session.id, session);
return session;
};
Cryptobox.prototype.load_session_from_cache = function (session_id) {
this.logger.log("Trying to load Session with ID \"" + session_id + "\" from cache...");
return this.cachedSessions.get(session_id);
};
Cryptobox.prototype.remove_session_from_cache = function (session_id) {
this.logger.log("Removing Session with ID \"" + session_id + "\" from cache...");
this.cachedSessions.delete(session_id);
};
Cryptobox.prototype.create = function () {
var _this = this;
this.logger.log("Initializing Cryptobox. Creating local identity...");
return this.create_new_identity()
.then(function (identity) {
_this.identity = identity;
_this.logger.log("Initialized Cryptobox with new local identity. Fingerprint is \"" + identity.public_key.fingerprint() + "\".");
return _this.create_last_resort_prekey();
})
.then(function (lastResortPreKey) {
_this.logger.log("Created Last Resort PreKey with ID \"" + lastResortPreKey.key_id + "\".");
return _this.init(false);
});
};
Cryptobox.prototype.load = function () {
var _this = this;
this.logger.log("Initializing Cryptobox. Loading local identity...");
return this.store
.load_identity()
.then(function (identity) {
if (identity) {
_this.identity = identity;
_this.logger.log('Initialized Cryptobox with existing local identity.');
_this.logger.log("Identity fingerprint is \"" + identity.public_key.fingerprint() + "\".");
_this.logger.log("Loading PreKeys...");
return _this.store.load_prekeys();
}
throw new root_1.CryptoboxError('Failed to load local identity');
})
.then(function (preKeysFromStorage) {
var lastResortPreKey = preKeysFromStorage.find(function (preKey) { return preKey.key_id === proteus_1.keys.PreKey.MAX_PREKEY_ID; });
if (lastResortPreKey) {
_this.logger.log("Loaded Last Resort PreKey with ID \"" + lastResortPreKey.key_id + "\".");
_this.lastResortPreKey = lastResortPreKey;
_this.logger.log("Loaded \"" + (_this.minimumAmountOfPreKeys - 1) + "\" standard PreKeys...");
return _this.init(true);
}
throw new root_1.CryptoboxError('Failed to load last resort PreKey');
});
};
Cryptobox.prototype.init = function (publishPrekeys) {
var _this = this;
return this.refill_prekeys(publishPrekeys)
.then(function () { return _this.store.load_prekeys(); })
.then(function (prekeys) { return prekeys.sort(function (a, b) { return a.key_id - b.key_id; }); });
};
Cryptobox.prototype.get_serialized_last_resort_prekey = function () {
if (this.lastResortPreKey) {
return Promise.resolve(this.serialize_prekey(this.lastResortPreKey));
}
return Promise.reject(new root_1.CryptoboxError('No last resort PreKey available.'));
};
Cryptobox.prototype.get_prekey = function (prekey_id) {
if (prekey_id === void 0) { prekey_id = proteus_1.keys.PreKey.MAX_PREKEY_ID; }
return this.store.load_prekey(prekey_id);
};
Cryptobox.prototype.get_serialized_standard_prekeys = function () {
var _this = this;
return this.store.load_prekeys().then(function (prekeys) {
return prekeys
.filter(function (preKey) {
var isLastResortPreKey = preKey.key_id === proteus_1.keys.PreKey.MAX_PREKEY_ID;
return !isLastResortPreKey;
})
.map(function (preKey) { return _this.serialize_prekey(preKey); });
});
};
Cryptobox.prototype.publish_event = function (topic, event) {
this.emit(topic, event);
this.logger.log("Published event \"" + topic + "\".", event);
};
Cryptobox.prototype.publish_prekeys = function (newPreKeys) {
if (newPreKeys.length > 0) {
this.publish_event(Cryptobox.TOPIC.NEW_PREKEYS, newPreKeys);
}
};
Cryptobox.prototype.publish_session_id = function (session) {
this.publish_event(Cryptobox.TOPIC.NEW_SESSION, session.id);
};
Cryptobox.prototype.refill_prekeys = function (publishPrekeys) {
var _this = this;
if (publishPrekeys === void 0) { publishPrekeys = true; }
return this.store
.load_prekeys()
.then(function (prekeys) {
var missingAmount = Math.max(0, _this.minimumAmountOfPreKeys - prekeys.length);
if (missingAmount > 0) {
var startId = prekeys.reduce(function (currentHighestValue, currentPreKey) {
var isLastResortPreKey = currentPreKey.key_id === proteus_1.keys.PreKey.MAX_PREKEY_ID;
return isLastResortPreKey ? currentHighestValue : Math.max(currentPreKey.key_id + 1, currentHighestValue);
}, 0);
_this.logger.warn("There are not enough PreKeys in the storage. Generating \"" + missingAmount + "\" new PreKey(s), starting from ID \"" + startId + "\"...");
return _this.new_prekeys(startId, missingAmount);
}
return [];
})
.then(function (newPreKeys) {
if (newPreKeys.length > 0) {
_this.logger.log("Generated PreKeys from ID \"" + newPreKeys[0].key_id + "\" to ID \"" + newPreKeys[newPreKeys.length - 1].key_id + "\".");
if (publishPrekeys) {
_this.publish_prekeys(newPreKeys);
}
}
return newPreKeys;
});
};
Cryptobox.prototype.create_new_identity = function () {
var _this = this;
return Promise.resolve()
.then(function () { return _this.store.delete_all(); })
.then(function () {
return proteus_1.keys.IdentityKeyPair.new();
})
.then(function (identity) {
_this.logger.warn("Cleaned cryptographic items prior to saving a new local identity.");
return _this.store.save_identity(identity);
});
};
Cryptobox.prototype.session_from_prekey = function (session_id, pre_key_bundle) {
var _this = this;
return this.session_load(session_id).catch(function (sessionLoadError) {
_this.logger.warn("Creating new session because session with ID \"" + session_id + "\" could not be loaded: " + sessionLoadError.message);
var bundle;
try {
bundle = proteus_1.keys.PreKeyBundle.deserialise(pre_key_bundle);
}
catch (error) {
throw new InvalidPreKeyFormatError_1.default("PreKey bundle for session \"" + session_id + "\" has an unsupported format: " + error.message);
}
if (_this.identity) {
return proteus_3.session.Session.init_from_prekey(_this.identity, bundle).then(function (session) {
var cryptobox_session = new CryptoboxSession_1.default(session_id, _this.store, session);
return _this.session_save(cryptobox_session);
});
}
return Promise.reject(new root_1.CryptoboxError('No local identity available.'));
});
};
Cryptobox.prototype.session_from_message = function (session_id, envelope) {
var _this = this;
var env = proteus_2.message.Envelope.deserialise(envelope);
if (this.identity) {
return proteus_3.session.Session.init_from_message(this.identity, this.store, env).then(function (tuple) {
var session = tuple[0], decrypted = tuple[1];
var cryptoBoxSession = new CryptoboxSession_1.default(session_id, _this.store, session);
return [cryptoBoxSession, decrypted];
});
}
return Promise.reject(new root_1.CryptoboxError('No local identity available.'));
};
Cryptobox.prototype.session_load = function (session_id) {
var _this = this;
this.logger.log("Trying to load Session with ID \"" + session_id + "\"...");
var cachedSession = this.load_session_from_cache(session_id);
if (cachedSession) {
return Promise.resolve(cachedSession);
}
if (this.identity) {
return this.store.read_session(this.identity, session_id).then(function (session) {
var cryptobox_session = new CryptoboxSession_1.default(session_id, _this.store, session);
return _this.save_session_in_cache(cryptobox_session);
});
}
throw new root_1.CryptoboxError('No local identity available.');
};
Cryptobox.prototype.session_save = function (session) {
var _this = this;
return this.store.create_session(session.id, session.session).then(function () { return _this.save_session_in_cache(session); });
};
Cryptobox.prototype.session_update = function (session) {
var _this = this;
return this.store.update_session(session.id, session.session).then(function () { return _this.save_session_in_cache(session); });
};
Cryptobox.prototype.session_delete = function (session_id) {
this.remove_session_from_cache(session_id);
return this.store.delete_session(session_id);
};
Cryptobox.prototype.create_last_resort_prekey = function () {
var _this = this;
return Promise.resolve()
.then(function () { return __awaiter(_this, void 0, void 0, function () {
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
this.logger.log("Creating Last Resort PreKey with ID \"" + proteus_1.keys.PreKey.MAX_PREKEY_ID + "\"...");
_a = this;
return [4, proteus_1.keys.PreKey.last_resort()];
case 1:
_a.lastResortPreKey = _b.sent();
return [2, this.store.save_prekeys([this.lastResortPreKey])];
}
});
}); })
.then(function (preKeys) { return preKeys[0]; });
};
Cryptobox.prototype.serialize_prekey = function (prekey) {
if (this.identity) {
return proteus_1.keys.PreKeyBundle.new(this.identity.public_key, prekey).serialised_json();
}
throw new root_1.CryptoboxError('No local identity available.');
};
Cryptobox.prototype.new_prekeys = function (start, size) {
var _this = this;
if (size === void 0) { size = 0; }
if (size === 0) {
return Promise.resolve([]);
}
return Promise.resolve()
.then(function () { return proteus_1.keys.PreKey.generate_prekeys(start, size); })
.then(function (newPreKeys) { return _this.store.save_prekeys(newPreKeys); });
};
Cryptobox.prototype.encrypt = function (session_id, payload, pre_key_bundle) {
var _this = this;
var encryptedBuffer;
var loadedSession;
return this.get_session_queue(session_id).add(function () {
return Promise.resolve()
.then(function () {
if (pre_key_bundle) {
return _this.session_from_prekey(session_id, pre_key_bundle);
}
return _this.session_load(session_id);
})
.then(function (session) {
loadedSession = session;
return loadedSession.encrypt(payload);
})
.then(function (encrypted) {
encryptedBuffer = encrypted;
return _this.session_update(loadedSession);
})
.then(function () { return encryptedBuffer; });
});
};
Cryptobox.prototype.decrypt = function (session_id, ciphertext) {
var _this = this;
var is_new_session = false;
var message;
var session;
if (ciphertext.byteLength === 0) {
return Promise.reject(new DecryptionError_1.default('Cannot decrypt an empty ArrayBuffer.'));
}
return this.get_session_queue(session_id).add(function () {
return (_this.session_load(session_id)
.catch(function () { return _this.session_from_message(session_id, ciphertext); })
.then(function (value) {
var decrypted_message;
if (value[0] !== undefined) {
session = value[0], decrypted_message = value[1];
_this.publish_session_id(session);
is_new_session = true;
return decrypted_message;
}
session = value;
return session.decrypt(ciphertext);
})
.then(function (decrypted_message) {
message = decrypted_message;
if (is_new_session) {
return _this.session_save(session);
}
return _this.session_update(session);
})
.then(function () { return _this.refill_prekeys(true); })
.then(function () { return message; }));
});
};
Cryptobox.TOPIC = {
NEW_PREKEYS: 'new-prekeys',
NEW_SESSION: 'new-session',
};
Cryptobox.VERSION = VERSION;
return Cryptobox;
}(EventEmitter));
exports.default = Cryptobox;
//# sourceMappingURL=Cryptobox.js.map
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var DecryptionError = (function (_super) {
__extends(DecryptionError, _super);
function DecryptionError(message) {
var _this = _super.call(this, message) || this;
_this.message = message;
Object.setPrototypeOf(_this, DecryptionError.prototype);
_this.message = message;
_this.name = _this.constructor.name;
_this.stack = new Error().stack;
return _this;
}
return DecryptionError;
}(Error));
exports.default = DecryptionError;
//# sourceMappingURL=DecryptionError.js.map
/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(__webpack_require__(12));
__export(__webpack_require__(18));
__export(__webpack_require__(19));
//# sourceMappingURL=root.js.map
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var CryptoboxError_1 = __webpack_require__(11);
exports.CryptoboxError = CryptoboxError_1.default;
//# sourceMappingURL=root.js.map
/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var DecryptionError_1 = __webpack_require__(2);
var proteus_1 = __webpack_require__(0);
var CryptoboxSession = (function () {
function CryptoboxSession(id, pk_store, session) {
this.id = id;
this.pk_store = pk_store;
this.session = session;
Object.freeze(this);
}
CryptoboxSession.prototype.decrypt = function (ciphertext) {
if (ciphertext.byteLength === 0) {
return Promise.reject(new DecryptionError_1.default('Cannot decrypt an empty ArrayBuffer.'));
}
var envelope = proteus_1.message.Envelope.deserialise(ciphertext);
return this.session.decrypt(this.pk_store, envelope);
};
CryptoboxSession.prototype.encrypt = function (plaintext) {
return this.session.encrypt(plaintext).then(function (ciphertext) {
return ciphertext.serialise();
});
};
CryptoboxSession.prototype.fingerprint_local = function () {
return this.session.local_identity.public_key.fingerprint();
};
CryptoboxSession.prototype.fingerprint_remote = function () {
return this.session.remote_identity.fingerprint();
};
return CryptoboxSession;
}());
exports.default = CryptoboxSession;
//# sourceMappingURL=CryptoboxSession.js.map
/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var InvalidPreKeyFormatError = (function (_super) {
__extends(InvalidPreKeyFormatError, _super);
function InvalidPreKeyFormatError(message) {
var _this = _super.call(this, message) || this;
_this.message = message;
Object.setPrototypeOf(_this, InvalidPreKeyFormatError.prototype);
_this.name = _this.constructor.name;
_this.message = message;
_this.stack = new Error().stack;
return _this;
}
return InvalidPreKeyFormatError;
}(Error));
exports.default = InvalidPreKeyFormatError;
//# sourceMappingURL=InvalidPreKeyFormatError.js.map
/***/ }),
/* 7 */
/***/ (function(module, exports) {
module.exports = StoreEngine;
/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(global) {/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
* @license MIT
*/
/* eslint-disable no-proto */
var base64 = __webpack_require__(15)
var ieee754 = __webpack_require__(16)
var isArray = __webpack_require__(17)
exports.Buffer = Buffer
exports.SlowBuffer = SlowBuffer
exports.INSPECT_MAX_BYTES = 50
/**
* If `Buffer.TYPED_ARRAY_SUPPORT`:
* === true Use Uint8Array implementation (fastest)
* === false Use Object implementation (most compatible, even IE6)
*
* Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
* Opera 11.6+, iOS 4.2+.
*
* Due to various browser bugs, sometimes the Object implementation will be used even
* when the browser supports typed arrays.
*
* Note:
*
* - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,
* See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
*
* - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
*
* - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
* incorrect length in some situations.
* We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they
* get the Object implementation, which is slower but behaves correctly.
*/
Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined
? global.TYPED_ARRAY_SUPPORT
: typedArraySupport()
/*
* Export kMaxLength after typed array support is determined.
*/
exports.kMaxLength = kMaxLength()
function typedArraySupport () {
try {
var arr = new Uint8Array(1)
arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}
return arr.foo() === 42 && // typed array instances can be augmented
typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
} catch (e) {
return false
}
}
function kMaxLength () {
return Buffer.TYPED_ARRAY_SUPPORT
? 0x7fffffff
: 0x3fffffff
}
function createBuffer (that, length) {
if (kMaxLength() < length) {
throw new RangeError('Invalid typed array length')
}
if (Buffer.TYPED_ARRAY_SUPPORT) {
// Return an augmented `Uint8Array` instance, for best performance
that = new Uint8Array(length)
that.__proto__ = Buffer.prototype
} else {
// Fallback: Return an object instance of the Buffer class
if (that === null) {
that = new Buffer(length)
}
that.length = length
}
return that
}
/**
* The Buffer constructor returns instances of `Uint8Array` that have their
* prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
* `Uint8Array`, so the returned instances will have all the node `Buffer` methods
* and the `Uint8Array` methods. Square bracket notation works as expected -- it
* returns a single octet.
*
* The `Uint8Array` prototype remains unmodified.
*/
function Buffer (arg, encodingOrOffset, length) {
if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {
return new Buffer(arg, encodingOrOffset, length)
}
// Common case.
if (typeof arg === 'number') {
if (typeof encodingOrOffset === 'string') {
throw new Error(
'If encoding is specified then the first argument must be a string'
)
}
return allocUnsafe(this, arg)
}
return from(this, arg, encodingOrOffset, length)
}
Buffer.poolSize = 8192 // not used by this implementation
// TODO: Legacy, not needed anymore. Remove in next major version.
Buffer._augment = function (arr) {
arr.__proto__ = Buffer.prototype
return arr
}
function from (that, value, encodingOrOffset, length) {
if (typeof value === 'number') {
throw new TypeError('"value" argument must not be a number')
}
if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {
return fromArrayBuffer(that, value, encodingOrOffset, length)
}
if (typeof value === 'string') {
return fromString(that, value, encodingOrOffset)
}
return fromObject(that, value)
}
/**
* Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
* if value is a number.
* Buffer.from(str[, encoding])
* Buffer.from(array)
* Buffer.from(buffer)
* Buffer.from(arrayBuffer[, byteOffset[, length]])
**/
Buffer.from = function (value, encodingOrOffset, length) {
return from(null, value, encodingOrOffset, length)
}
if (Buffer.TYPED_ARRAY_SUPPORT) {
Buffer.prototype.__proto__ = Uint8Array.prototype
Buffer.__proto__ = Uint8Array
if (typeof Symbol !== 'undefined' && Symbol.species &&
Buffer[Symbol.species] === Buffer) {
// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
Object.defineProperty(Buffer, Symbol.species, {
value: null,
configurable: true
})
}
}
function assertSize (size) {
if (typeof size !== 'number') {
throw new TypeError('"size" argument must be a number')
} else if (size < 0) {
throw new RangeError('"size" argument must not be negative')
}
}
function alloc (that, size, fill, encoding) {
assertSize(size)
if (size <= 0) {
return createBuffer(that, size)
}
if (fill !== undefined) {
// Only pay attention to encoding if it's a string. This
// prevents accidentally sending in a number that would
// be interpretted as a start offset.
return typeof encoding === 'string'
? createBuffer(that, size).fill(fill, encoding)
: createBuffer(that, size).fill(fill)
}
return createBuffer(that, size)
}
/**
* Creates a new filled Buffer instance.
* alloc(size[, fill[, encoding]])
**/
Buffer.alloc = function (size, fill, encoding) {
return alloc(null, size, fill, encoding)
}
function allocUnsafe (that, size) {
assertSize(size)
that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)
if (!Buffer.TYPED_ARRAY_SUPPORT) {
for (var i = 0; i < size; ++i) {
that[i] = 0
}
}
return that
}
/**
* Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
* */
Buffer.allocUnsafe = function (size) {
return allocUnsafe(null, size)
}
/**
* Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
*/
Buffer.allocUnsafeSlow = function (size) {
return allocUnsafe(null, size)
}
function fromString (that, string, encoding) {
if (typeof encoding !== 'string' || encoding === '') {
encoding = 'utf8'
}
if (!Buffer.isEncoding(encoding)) {
throw new TypeError('"encoding" must be a valid string encoding')
}
var length = byteLength(string, encoding) | 0
that = createBuffer(that, length)
var actual = that.write(string, encoding)
if (actual !== length) {
// Writing a hex string, for example, that contains invalid characters will
// cause everything after the first invalid character to be ignored. (e.g.
// 'abxxcd' will be treated as 'ab')
that = that.slice(0, actual)
}
return that
}
function fromArrayLike (that, array) {
var length = array.length < 0 ? 0 : checked(array.length) | 0
that = createBuffer(that, length)
for (var i = 0; i < length; i += 1) {
that[i] = array[i] & 255
}
return that
}
function fromArrayBuffer (that, array, byteOffset, length) {
array.byteLength // this throws if `array` is not a valid ArrayBuffer
if (byteOffset < 0 || array.byteLength < byteOffset) {
throw new RangeError('\'offset\' is out of bounds')
}
if (array.byteLength < byteOffset + (length || 0)) {
throw new RangeError('\'length\' is out of bounds')
}
if (byteOffset === undefined && length === undefined) {
array = new Uint8Array(array)
} else if (length === undefined) {
array = new Uint8Array(array, byteOffset)
} else {
array = new Uint8Array(array, byteOffset, length)
}
if (Buffer.TYPED_ARRAY_SUPPORT) {
// Return an augmented `Uint8Array` instance, for best performance
that = array
that.__proto__ = Buffer.prototype
} else {
// Fallback: Return an object instance of the Buffer class
that = fromArrayLike(that, array)
}
return that
}
function fromObject (that, obj) {
if (Buffer.isBuffer(obj)) {
var len = checked(obj.length) | 0
that = createBuffer(that, len)
if (that.length === 0) {
return that
}
obj.copy(that, 0, 0, len)
return that
}
if (obj) {
if ((typeof ArrayBuffer !== 'undefined' &&
obj.buffer instanceof ArrayBuffer) || 'length' in obj) {
if (typeof obj.length !== 'number' || isnan(obj.length)) {
return createBuffer(that, 0)
}
return fromArrayLike(that, obj)
}
if (obj.type === 'Buffer' && isArray(obj.data)) {
return fromArrayLike(that, obj.data)
}
}
throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')
}
function checked (length) {
// Note: cannot use `length < kMaxLength()` here because that fails when
// length is NaN (which is otherwise coerced to zero.)
if (length >= kMaxLength()) {
throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
'size: 0x' + kMaxLength().toString(16) + ' bytes')
}
return length | 0
}
function SlowBuffer (length) {
if (+length != length) { // eslint-disable-line eqeqeq
length = 0
}
return Buffer.alloc(+length)
}
Buffer.isBuffer = function isBuffer (b) {
return !!(b != null && b._isBuffer)
}
Buffer.compare = function compare (a, b) {
if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
throw new TypeError('Arguments must be Buffers')
}
if (a === b) return 0
var x = a.length
var y = b.length
for (var i = 0, len = Math.min(x, y); i < len; ++i) {
if (a[i] !== b[i]) {
x = a[i]
y = b[i]
break
}
}
if (x < y) return -1
if (y < x) return 1
return 0
}
Buffer.isEncoding = function isEncoding (encoding) {
switch (String(encoding).toLowerCase()) {
case 'hex':
case 'utf8':
case 'utf-8':
case 'ascii':
case 'latin1':
case 'binary':
case 'base64':
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
return true
default:
return false
}
}
Buffer.concat = function concat (list, length) {
if (!isArray(list)) {
throw new TypeError('"list" argument must be an Array of Buffers')
}
if (list.length === 0) {
return Buffer.alloc(0)
}
var i
if (length === undefined) {
length = 0
for (i = 0; i < list.length; ++i) {
length += list[i].length
}
}
var buffer = Buffer.allocUnsafe(length)
var pos = 0
for (i = 0; i < list.length; ++i) {
var buf = list[i]
if (!Buffer.isBuffer(buf)) {
throw new TypeError('"list" argument must be an Array of Buffers')
}
buf.copy(buffer, pos)
pos += buf.length
}
return buffer
}
function byteLength (string, encoding) {
if (Buffer.isBuffer(string)) {
return string.length
}
if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&
(ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {
return string.byteLength
}
if (typeof string !== 'string') {
string = '' + string
}
var len = string.length
if (len === 0) return 0
// Use a for loop to avoid recursion
var loweredCase = false
for (;;) {
switch (encoding) {
case 'ascii':
case 'latin1':
case 'binary':
return len
case 'utf8':
case 'utf-8':
case undefined:
return utf8ToBytes(string).length
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
return len * 2
case 'hex':
return len >>> 1
case 'base64':
return base64ToBytes(string).length
default:
if (loweredCase) return utf8ToBytes(string).length // assume utf8
encoding = ('' + encoding).toLowerCase()
loweredCase = true
}
}
}
Buffer.byteLength = byteLength
function slowToString (encoding, start, end) {
var loweredCase = false
// No need to verify that "this.length <= MAX_UINT32" since it's a read-only
// property of a typed array.
// This behaves neither like String nor Uint8Array in that we set start/end
// to their upper/lower bounds if the value passed is out of range.
// undefined is handled specially as per ECMA-262 6th Edition,
// Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
if (start === undefined || start < 0) {
start = 0
}
// Return early if start > this.length. Done here to prevent potential uint32
// coercion fail below.
if (start > this.length) {
return ''
}
if (end === undefined || end > this.length) {
end = this.length
}
if (end <= 0) {
return ''
}
// Force coersion to uint32. This will also coerce falsey/NaN values to 0.
end >>>= 0
start >>>= 0
if (end <= start) {
return ''
}
if (!encoding) encoding = 'utf8'
while (true) {
switch (encoding) {
case 'hex':
return hexSlice(this, start, end)
case 'utf8':
case 'utf-8':
return utf8Slice(this, start, end)
case 'ascii':
return asciiSlice(this, start, end)
case 'latin1':
case 'binary':
return latin1Slice(this, start, end)
case 'base64':
return base64Slice(this, start, end)
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
return utf16leSlice(this, start, end)
default:
if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
encoding = (encoding + '').toLowerCase()
loweredCase = true
}
}
}
// The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect
// Buffer instances.
Buffer.prototype._isBuffer = true
function swap (b, n, m) {
var i = b[n]
b[n] = b[m]
b[m] = i
}
Buffer.prototype.swap16 = function swap16 () {
var len = this.length
if (len % 2 !== 0) {
throw new RangeError('Buffer size must be a multiple of 16-bits')
}
for (var i = 0; i < len; i += 2) {
swap(this, i, i + 1)
}
return this
}
Buffer.prototype.swap32 = function swap32 () {
var len = this.length
if (len % 4 !== 0) {
throw new RangeError('Buffer size must be a multiple of 32-bits')
}
for (var i = 0; i < len; i += 4) {
swap(this, i, i + 3)
swap(this, i + 1, i + 2)
}
return this
}
Buffer.prototype.swap64 = function swap64 () {
var len = this.length
if (len % 8 !== 0) {
throw new RangeError('Buffer size must be a multiple of 64-bits')
}
for (var i = 0; i < len; i += 8) {
swap(this, i, i + 7)
swap(this, i + 1, i + 6)
swap(this, i + 2, i + 5)
swap(this, i + 3, i + 4)
}
return this
}
Buffer.prototype.toString = function toString () {
var length = this.length | 0
if (length === 0) return ''
if (arguments.length === 0) return utf8Slice(this, 0, length)
return slowToString.apply(this, arguments)
}
Buffer.prototype.equals = function equals (b) {
if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
if (this === b) return true
return Buffer.compare(this, b) === 0
}
Buffer.prototype.inspect = function inspect () {
var str = ''
var max = exports.INSPECT_MAX_BYTES
if (this.length > 0) {
str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
if (this.length > max) str += ' ... '
}
return '<Buffer ' + str + '>'
}
Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
if (!Buffer.isBuffer(target)) {
throw new TypeError('Argument must be a Buffer')
}
if (start === undefined) {
start = 0
}
if (end === undefined) {
end = target ? target.length : 0
}
if (thisStart === undefined) {
thisStart = 0
}
if (thisEnd === undefined) {
thisEnd = this.length
}
if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
throw new RangeError('out of range index')
}
if (thisStart >= thisEnd && start >= end) {
return 0
}
if (thisStart >= thisEnd) {
return -1
}
if (start >= end) {
return 1
}
start >>>= 0
end >>>= 0
thisStart >>>= 0
thisEnd >>>= 0
if (this === target) return 0
var x = thisEnd - thisStart
var y = end - start
var len = Math.min(x, y)
var thisCopy = this.slice(thisStart, thisEnd)
var targetCopy = target.slice(start, end)
for (var i = 0; i < len; ++i) {
if (thisCopy[i] !== targetCopy[i]) {
x = thisCopy[i]
y = targetCopy[i]
break
}
}
if (x < y) return -1
if (y < x) return 1
return 0
}
// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
// OR the last index of `val` in `buffer` at offset <= `byteOffset`.
//
// Arguments:
// - buffer - a Buffer to search
// - val - a string, Buffer, or number
// - byteOffset - an index into `buffer`; will be clamped to an int32
// - encoding - an optional encoding, relevant is val is a string
// - dir - true for indexOf, false for lastIndexOf
function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
// Empty buffer means no match
if (buffer.length === 0) return -1
// Normalize byteOffset
if (typeof byteOffset === 'string') {
encoding = byteOffset
byteOffset = 0
} else if (byteOffset > 0x7fffffff) {
byteOffset = 0x7fffffff
} else if (byteOffset < -0x80000000) {
byteOffset = -0x80000000
}
byteOffset = +byteOffset // Coerce to Number.
if (isNaN(byteOffset)) {
// byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
byteOffset = dir ? 0 : (buffer.length - 1)
}
// Normalize byteOffset: negative offsets start from the end of the buffer
if (byteOffset < 0) byteOffset = buffer.length + byteOffset
if (byteOffset >= buffer.length) {
if (dir) return -1
else byteOffset = buffer.length - 1
} else if (byteOffset < 0) {
if (dir) byteOffset = 0
else return -1
}
// Normalize val
if (typeof val === 'string') {
val = Buffer.from(val, encoding)
}
// Finally, search either indexOf (if dir is true) or lastIndexOf
if (Buffer.isBuffer(val)) {
// Special case: looking for empty string/buffer always fails
if (val.length === 0) {
return -1
}
return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
} else if (typeof val === 'number') {
val = val & 0xFF // Search for a byte value [0-255]
if (Buffer.TYPED_ARRAY_SUPPORT &&
typeof Uint8Array.prototype.indexOf === 'function') {
if (dir) {
return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
} else {
return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
}
}
return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
}
throw new TypeError('val must be string, number or Buffer')
}
function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
var indexSize = 1
var arrLength = arr.length
var valLength = val.length
if (encoding !== undefined) {
encoding = String(encoding).toLowerCase()
if (encoding === 'ucs2' || encoding === 'ucs-2' ||
encoding === 'utf16le' || encoding === 'utf-16le') {
if (arr.length < 2 || val.length < 2) {
return -1
}
indexSize = 2
arrLength /= 2
valLength /= 2
byteOffset /= 2
}
}
function read (buf, i) {
if (indexSize === 1) {
return buf[i]
} else {
return buf.readUInt16BE(i * indexSize)
}
}
var i
if (dir) {
var foundIndex = -1
for (i = byteOffset; i < arrLength; i++) {
if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
if (foundIndex === -1) foundIndex = i
if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
} else {
if (foundIndex !== -1) i -= i - foundIndex
foundIndex = -1
}
}
} else {
if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
for (i = byteOffset; i >= 0; i--) {
var found = true
for (var j = 0; j < valLength; j++) {
if (read(arr, i + j) !== read(val, j)) {
found = false
break
}
}
if (found) return i
}
}
return -1
}
Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
return this.indexOf(val, byteOffset, encoding) !== -1
}
Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
}
Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
}
function hexWrite (buf, string, offset, length) {
offset = Number(offset) || 0
var remaining = buf.length - offset
if (!length) {
length = remaining
} else {
length = Number(length)
if (length > remaining) {
length = remaining
}
}
// must be an even number of digits
var strLen = string.length
if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')
if (length > strLen / 2) {
length = strLen / 2
}
for (var i = 0; i < length; ++i) {
var parsed = parseInt(string.substr(i * 2, 2), 16)
if (isNaN(parsed)) return i
buf[offset + i] = parsed
}
return i
}
function utf8Write (buf, string, offset, length) {
return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
}
function asciiWrite (buf, string, offset, length) {
return blitBuffer(asciiToBytes(string), buf, offset, length)
}
function latin1Write (buf, string, offset, length) {
return asciiWrite(buf, string, offset, length)
}
function base64Write (buf, string, offset, length) {
return blitBuffer(base64ToBytes(string), buf, offset, length)
}
function ucs2Write (buf, string, offset, length) {
return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
}
Buffer.prototype.write = function write (string, offset, length, encoding) {
// Buffer#write(string)
if (offset === undefined) {
encoding = 'utf8'
length = this.length
offset = 0
// Buffer#write(string, encoding)
} else if (length === undefined && typeof offset === 'string') {
encoding = offset
length = this.length
offset = 0
// Buffer#write(string, offset[, length][, encoding])
} else if (isFinite(offset)) {
offset = offset | 0
if (isFinite(length)) {
length = length | 0
if (encoding === undefined) encoding = 'utf8'
} else {
encoding = length
length = undefined
}
// legacy write(string, encoding, offset, length) - remove in v0.13
} else {
throw new Error(
'Buffer.write(string, encoding, offset[, length]) is no longer supported'
)
}
var remaining = this.length - offset
if (length === undefined || length > remaining) length = remaining
if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
throw new RangeError('Attempt to write outside buffer bounds')
}
if (!encoding) encoding = 'utf8'
var loweredCase = false
for (;;) {
switch (encoding) {
case 'hex':
return hexWrite(this, string, offset, length)
case 'utf8':
case 'utf-8':
return utf8Write(this, string, offset, length)
case 'ascii':
return asciiWrite(this, string, offset, length)
case 'latin1':
case 'binary':
return latin1Write(this, string, offset, length)
case 'base64':
// Warning: maxLength not taken into account in base64Write
return base64Write(this, string, offset, length)
case 'ucs2':
case 'ucs-2':
case 'utf16l