UNPKG

@iotize/tap

Version:

IoTize Device client for Javascript

1,019 lines (1,007 loc) 85.4 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@iotize/tap'), require('@iotize/tap/service/impl/group'), require('@iotize/common/error'), require('@iotize/tap/client/api'), require('rxjs'), require('@iotize/tap/service/impl/interface'), require('@iotize/common/debug'), require('@iotize/common/byte-converter'), require('@iotize/tap/crypto'), require('@iotize/common/crypto'), require('@iotize/common/utility'), require('@iotize/tap/client/impl'), require('@iotize/tap/service/impl/scram'), require('rxjs/operators'), require('@iotize/common/byte-stream')) : typeof define === 'function' && define.amd ? define('@iotize/tap/auth', ['exports', '@iotize/tap', '@iotize/tap/service/impl/group', '@iotize/common/error', '@iotize/tap/client/api', 'rxjs', '@iotize/tap/service/impl/interface', '@iotize/common/debug', '@iotize/common/byte-converter', '@iotize/tap/crypto', '@iotize/common/crypto', '@iotize/common/utility', '@iotize/tap/client/impl', '@iotize/tap/service/impl/scram', 'rxjs/operators', '@iotize/common/byte-stream'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.iotize = global.iotize || {}, global.iotize.tap = global.iotize.tap || {}, global.iotize.tap.auth = {}), global.iotize.tap, global.iotize.tap.service.impl.group, global.error, global.iotize.tap.client.api, global.rxjs, global.iotize.tap.service.impl["interface"], global.debug$1, global.byteConverter, global.iotize.tap.crypto, global.crypto$1, global.utility, global.iotize.tap.client.impl, global.iotize.tap.service.impl.scram, global.rxjs.operators, global.byteStream)); })(this, (function (exports, tap, group, error, api, rxjs, _interface, debug$1, byteConverter, crypto, crypto$1, utility, impl, scram, operators, byteStream) { 'use strict'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function () { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function __param(paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); }; } function __metadata(metadataKey, metadataValue) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); } function __awaiter$5(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(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 = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, 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 }; } } var __createBinding = Object.create ? (function (o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function () { return m[k]; } }); }) : (function (o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; }); function __exportStar(m, o) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p); } function __values(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } /** @deprecated */ function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } /** @deprecated */ function __spreadArrays() { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; } function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } function __await(v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } function __asyncGenerator(thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } } function __asyncDelegator(o) { var i, p; return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } } function __asyncValues(o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function (v) { resolve({ value: v, done: d }); }, reject); } } function __makeTemplateObject(cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; } ; var __setModuleDefault = Object.create ? (function (o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function (o, v) { o["default"] = v; }; function __importStar(mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; } function __importDefault(mod) { return (mod && mod.__esModule) ? mod : { default: mod }; } function __classPrivateFieldGet(receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); } function __classPrivateFieldSet(receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; } var prefix = '@iotize/tap/auth'; var debug = debug$1.createDebugger(prefix); var INITIAL_SESSION_STATE = { groupId: 0, lifeTime: -1, name: 'anonymous', startTime: undefined, profileId: 0, profileName: 'anonymous', }; var PASSWORD_LENGTH = 16; function hashLoginPassword(password) { return byteConverter.hexStringToBuffer(crypto.passwordHasher.hash(password).substring(0, PASSWORD_LENGTH * 2)); } function XOR(value1, value2) { if (value1.length !== value2.length) { throw new Error('Length does not match between the two array. Cannot compute XOR'); } var result = new Uint8Array(value1.length); for (var i = 0; i < result.length; i++) { result[i] = value1[i] ^ value2[i]; } return result; } var __awaiter$4 = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var TAG$3 = 'BasicAuth'; var BasicAuth = /** @class */ (function () { function BasicAuth(tap, options) { if (options === void 0) { options = { hashPassword: false, }; } this.tap = tap; this.options = options; } Object.defineProperty(BasicAuth.prototype, "hashPassword", { get: function () { var _a; return ((_a = this.options) === null || _a === void 0 ? void 0 : _a.hashPassword) || false; }, enumerable: false, configurable: true }); /** * Tap login * */ BasicAuth.prototype.login = function (params) { return __awaiter$4(this, void 0, void 0, function () { var username, password, response; return __generator(this, function (_b) { switch (_b.label) { case 0: username = params.username, password = params.password; if (!this.hashPassword) return [3 /*break*/, 2]; debug(TAG$3, "Using hash password login"); return [4 /*yield*/, this.tap.service.interface.loginWithHash({ username: username, password: hashLoginPassword(password), })]; case 1: response = _b.sent(); return [3 /*break*/, 4]; case 2: debug(TAG$3, "Using regular login"); return [4 /*yield*/, this.tap.service.interface.login({ username: username, password: password, })]; case 3: response = _b.sent(); _b.label = 4; case 4: response.successful(); return [2 /*return*/, {}]; } }); }); }; BasicAuth.prototype.logout = function () { return __awaiter$4(this, void 0, void 0, function () { return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, this.tap.service.interface.logout()]; case 1: (_b.sent()).successful(); return [2 /*return*/]; } }); }); }; /** * Change password for the groupId * @param newPassword the new password for the userId * @param userId the user id for which we want to change the password */ BasicAuth.prototype.changePassword = function (newPassword, userId) { return __awaiter$4(this, void 0, void 0, function () { var passwordBytes; return __generator(this, function (_b) { switch (_b.label) { case 0: if (!this.hashPassword) return [3 /*break*/, 2]; passwordBytes = hashLoginPassword(newPassword); return [4 /*yield*/, this.tap.service.group.changePasswordKey(userId, passwordBytes)]; case 1: (_b.sent()).successful(); return [3 /*break*/, 4]; case 2: return [4 /*yield*/, this.tap.service.group.changePassword(userId, newPassword)]; case 3: (_b.sent()).successful(); _b.label = 4; case 4: return [2 /*return*/]; } }); }); }; return BasicAuth; }()); var TapScramError = /** @class */ (function (_super) { __extends(TapScramError, _super); function TapScramError(code, message, request) { var _this = _super.call(this, code, message, undefined) || this; _this.request = request; return _this; } TapScramError.scramNotStartedYet = function (failedRequest) { return new TapScramError(tap.TapError.Code.ScramNotStartedYet, "SCRAM session has not been initialized yet. You cannot use secure communication.", failedRequest); }; TapScramError.invalidScramKey = function (failedRequest) { return new TapScramError(tap.TapError.Code.InvalidScramKey, "SCRAM session key is not valid.", failedRequest); }; return TapScramError; }(tap.TapError)); var __awaiter$3 = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var INITIALIZATION_VECTOR_LENGTH = 16; var DEFAULT_INITIALIZATION_VECTOR = '00000000000000000000000000000000'; var TAG$2 = 'ScramInterceptor'; function _hex(data) { return data ? byteConverter.bufferToHexString(data) : ''; } var ScramInterceptor = /** @class */ (function () { function ScramInterceptor(scramService) { var _this = this; this.scramService = scramService; this._ivFrameCounter = 0; this._ivSupported = true; this._options = { encryption: false, initializationVectorResetPeriod: 100, keys: {}, frameCounter: 0, }; this._encryptedFrameConverter = new impl.CryptedFrameConverter({ next: function () { return _this._options.frameCounter++; }, }); this.initializationVectorGenerator = function (length) { var data = new Array(length).fill(0).map(function (_) { return Math.floor(Math.random() * 0xff); }); return Uint8Array.from(data); }; } Object.defineProperty(ScramInterceptor.prototype, "encryptionAlgo", { get: function () { if (!this._encryptionAlgo) { throw impl.TapClientError.illegalStateError("Encryption algo has not been set yet"); } return this._encryptionAlgo; }, enumerable: false, configurable: true }); Object.defineProperty(ScramInterceptor.prototype, "ivSupported", { set: function (v) { this._ivSupported = v; }, enumerable: false, configurable: true }); Object.defineProperty(ScramInterceptor.prototype, "sessionKey", { get: function () { return this._options.keys.sessionKey; }, /** * Setter for the session key * @param key if null, it will stop encryption and remove session key. If true it will update session key used for encryption */ set: function (key) { this._options.keys.sessionKey = key; this._options.frameCounter = 0; if (this._options.keys.sessionKey) { this.refreshEncryptionAlgo(); } else { this.pauseEncryption(); } }, enumerable: false, configurable: true }); Object.defineProperty(ScramInterceptor.prototype, "options", { /** * Get a copy of encryption options */ get: function () { return utility.deepCopy(this._options); }, enumerable: false, configurable: true }); ScramInterceptor.prototype.setEncryptionKeys = function (options) { this._options.keys = options; this.refreshEncryptionAlgo(); }; ScramInterceptor.prototype.getEncryptionKeys = function () { return this._options.keys; }; ScramInterceptor.prototype.setFrameCounter = function (value) { this._options.frameCounter = value; }; ScramInterceptor.prototype.setInitializationVectorRefreshPeriod = function (period) { this._options.initializationVectorResetPeriod = period; }; ScramInterceptor.prototype.intercept = function (context, next) { var request = context.request; debug(TAG$2, "exec " + impl.TapRequestHelper.toString(request) + " (encryption:" + this._options.encryption + ", frameCounter: " + this._options.frameCounter + " - skip " + context.skipEncryption + ", key: " + _hex(this.sessionKey) + " iv decode: " + _hex(this._options.keys.ivDecode) + ", iv encode: " + _hex(this._options.keys.ivEncode) + ")"); var obs; if (this._options.encryption && !context.skipEncryption) { obs = this._sendWithEncryption(context, next).pipe(operators.map(function (response) { var data = response.rawBody(); return { status: response.status, data: data, }; })); } else { obs = next.handle(context); } return obs.pipe(operators.tap(function (response) { debug(TAG$2, "Response to " + impl.TapRequestHelper.toString(request) + " => " + response.status + " " + byteConverter.bufferToHexString(response.data)); })); }; ScramInterceptor.prototype._sendWithEncryption = function (context, next) { var _this = this; if (!this._encryptedFrameConverter) { return rxjs.throwError(impl.TapClientError.illegalStateError("Encrypted frame converter has not been specified yet")); } var callObs; var newClientIV; if (this._options.initializationVectorResetPeriod === 1) { newClientIV = this.initializationVectorGenerator(INITIALIZATION_VECTOR_LENGTH); this._setEncodeIV(newClientIV); debug(TAG$2, "Changing initialization vector for every requests. New client IV: " + _hex(newClientIV)); var cryptedFrame = this._buildEncryptedFrame(context); var call = this.scramService.sendWithIVCall({ request: cryptedFrame, iv: newClientIV, }); callObs = this._toCallObservable(Object.assign(Object.assign({}, context), { skipEncryption: true }), next, call).pipe(operators.map(function (response) { // if (response.isSuccessful() && response.rawBody().length < INITIALIZATION_VECTOR_LENGTH){ // // tap firmware version is too old apparentl. // // CCOM IV resource was already used to generate rand number before firmware version 1.83, thats why there is a success code result // throw TapError.initializationVectorNotSupported( // new Error(`Tap response is too short`) // ); // } var body = response.body(); debug(TAG$2, "Refreshing decoding initialization vector: " + _hex(body.iv)); _this._setDecodeIV(body.iv); return tap.TapResponse.create(response.status, body.response); })); } else { if (this._options.initializationVectorResetPeriod > 1 && this._ivFrameCounter >= this._options.initializationVectorResetPeriod && !isRefreshInitializationVectorRequest(context.request)) { debug(TAG$2, "_ivInterceptor refreshEncryptionInitializationVector is required " + this._ivFrameCounter + "/" + this._options.initializationVectorResetPeriod); if (!this._refreshingInitializationVectorObs) { this._refreshingInitializationVectorObs = rxjs.defer(function () { return __awaiter$3(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, , 2, 3]); return [4 /*yield*/, this.refreshInitializationVectors()]; case 1: return [2 /*return*/, _a.sent()]; case 2: this._refreshingInitializationVectorObs = undefined; return [7 /*endfinally*/]; case 3: return [2 /*return*/]; } }); }); }).pipe(operators.shareReplay({ bufferSize: 1, refCount: true })); } else { // debug(TAG, `Refreshing initialization vector has already been asked`); } callObs = this._refreshingInitializationVectorObs.pipe(operators.mergeMap(function (_) { var encryptedFrame = _this._buildEncryptedFrame(context); return _this._toCallObservable(context, next, _this.scramService.sendCall(encryptedFrame)); })); } else { var encryptedFrame = this._buildEncryptedFrame(context); var call = this.scramService.sendCall(encryptedFrame); callObs = this._toCallObservable(context, next, call); } } return callObs.pipe(operators.map(function (encryptedWarpperResponse) { _this._ivFrameCounter++; // debug(TAG, `Iotize client crypted frame: ${encryptedWarpperResponse}`); if (!encryptedWarpperResponse.isSuccessful()) { if (encryptedWarpperResponse.codeRet() === api.ResultCode.SERVICE_UNAVAILABLE) { throw TapScramError.scramNotStartedYet(context.request); } else if (encryptedWarpperResponse.codeRet() === api.ResultCode.BAD_REQUEST) { throw TapScramError.invalidScramKey(context.request); } encryptedWarpperResponse.successful(); } var cryptedFrameContent = _this.encryptionAlgo.decode(encryptedWarpperResponse.rawBody()); // debug(TAG, `Decrypted frame value: ${_hex(cryptedFrameContent)}`); var apduFrame = _this._encryptedFrameConverter.decode(cryptedFrameContent); // debug(TAG, `apduFrame frame value: ${_hex(apduFrame)}`); var tapResponse = context.client.responseDecoder.decode(apduFrame); var apiResponse = new tap.TapResponse(tapResponse, context.request); // let response: TapResponse<any> = this.lwm2mResponseConverter.decode(lwm2mResponseFrame.data); if (context.bodyDecoder) { apiResponse.setBodyDecoder(context.bodyDecoder); } // debug(TAG, `Iotize client decoded response: ${response}`) return apiResponse; })); }; ScramInterceptor.prototype._buildEncryptedFrame = function (context) { var iotizeFrame = impl.TapStreamWriter.create(10 + context.request.payload.length).writeTapRequestFrame(context.request).toBytes; var encryptedFrameModel = this._encryptedFrameConverter.encode(iotizeFrame); var encryptedFrame = this.encryptionAlgo.encode(encryptedFrameModel); return encryptedFrame; }; ScramInterceptor.prototype._setEncodeIV = function (iv) { this._options.keys.ivEncode = iv; this.refreshEncryptionAlgo(); }; ScramInterceptor.prototype._setDecodeIV = function (iv) { this._options.keys.ivDecode = iv; this.refreshEncryptionAlgo(); }; ScramInterceptor.prototype._toCallObservable = function (context, next, call) { var tapRequest = tap.ServiceCallRunner.toTapRequest(call); var bodyDecoderFct = tap.ServiceCallRunner.resolveResponseBodyDecoder(call); var bodyDecoder = bodyDecoderFct ? { decode: bodyDecoderFct, } : undefined; return next .handle(Object.assign(Object.assign({}, context), { request: tapRequest, bodyDecoder: bodyDecoder })) .pipe(operators.map(function (tapResponse) { return new tap.TapResponse(tapResponse, context.request, bodyDecoder); })); }; /** * Refresh initialization vectors */ ScramInterceptor.prototype.refreshInitializationVectors = function () { return __awaiter$3(this, void 0, void 0, function () { var clientIV, serverIV, err_1, tapResponseStatus; return __generator(this, function (_a) { switch (_a.label) { case 0: clientIV = this.initializationVectorGenerator(INITIALIZATION_VECTOR_LENGTH); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); debug(TAG$2, "Performing initialization vector refresh"); return [4 /*yield*/, this.scramService.setInitializationVector(clientIV)]; case 2: serverIV = (_a.sent()).body(); this._options.keys.ivEncode = clientIV; this._options.keys.ivDecode = serverIV; this._ivFrameCounter = 0; debug(TAG$2, "Initialization vector refreshed: server " + _hex(serverIV) + " client=" + _hex(clientIV) + ")"); this.refreshEncryptionAlgo(); return [3 /*break*/, 4]; case 3: err_1 = _a.sent(); debug(TAG$2, "Cannot refresh initialization vector: " + err_1.message); if (err_1 instanceof tap.TapResponseStatusError) { tapResponseStatus = err_1.response.status; if (tapResponseStatus === api.ResultCode.NOT_IMPLEMENTED || tapResponseStatus === api.ResultCode.NOT_FOUND) { debug(TAG$2, "Cannot initialize encryption vectors (firmware version does not support it). Error: " + err_1.message); this._ivSupported = false; throw tap.TapError.initializationVectorNotSupported(err_1); } } throw err_1; case 4: return [2 /*return*/]; } }); }); }; /** * Resume encryption session */ ScramInterceptor.prototype.resumeEncryption = function () { debug(TAG$2, "resume encryption"); this._options.encryption = true; }; /** * Pause encryption without destorying session (session keys will not be removed) */ ScramInterceptor.prototype.pauseEncryption = function () { debug(TAG$2, "pause encryption"); this._options.encryption = false; }; /** * Initialize a new encrypted sesssion * Session key will be changed. Initialization vector too (if firmware supports it) */ ScramInterceptor.prototype.newSession = function () { return __awaiter$3(this, void 0, void 0, function () { var response, newSessionKey, err_2, err_3; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 6, , 7]); debug(TAG$2, "Creating new encryption session"); this.clearSession(); return [4 /*yield*/, this.scramService.initialize()]; case 1: response = _a.sent(); response.successful(); newSessionKey = response.body(); this.sessionKey = newSessionKey; debug(TAG$2, "Session key will be " + _hex(newSessionKey) + " (length=" + newSessionKey.length + ")"); if (!(this._options.initializationVectorResetPeriod > 0)) return [3 /*break*/, 5]; _a.label = 2; case 2: _a.trys.push([2, 4, , 5]); return [4 /*yield*/, this.refreshInitializationVectors()]; case 3: _a.sent(); return [3 /*break*/, 5]; case 4: err_2 = _a.sent(); // Ignore initialization vector not supported error if (err_2.code !== tap.TapError.Code.InitializationVectorNotSupported) { throw err_2; } return [3 /*break*/, 5]; case 5: return [2 /*return*/, newSessionKey]; case 6: err_3 = _a.sent(); throw tap.TapError.cannotStartScram(err_3); case 7: return [2 /*return*/]; } }); }); }; /** * Clear encrypted session. * Destroys frame counter, session keys and initialization vectors * This will also stop encrypted communication if it was running */ ScramInterceptor.prototype.clearSession = function () { this.pauseEncryption(); this._options.keys.ivDecode = undefined; this._options.keys.ivEncode = undefined; this._options.keys.sessionKey = undefined; this._ivFrameCounter = 0; }; ScramInterceptor.prototype.refreshEncryptionAlgo = function () { if (this._options.keys.sessionKey) { var algo = new crypto$1.AesEcb128Converter({ key: _hex(this._options.keys.sessionKey), ivDecode: this._options.keys.ivDecode ? _hex(this._options.keys.ivDecode) : DEFAULT_INITIALIZATION_VECTOR, ivEncode: this._options.keys.ivEncode ? _hex(this._options.keys.ivEncode) : DEFAULT_INITIALIZATION_VECTOR, }); this._encryptionAlgo = algo; } }; return ScramInterceptor; }()); var SCRAM_CALL_FACTORY = new scram.ScramService(undefined); function isRefreshInitializationVectorRequest(request) { return (impl.TapRequestHelper.pathToString(request.header.path) === SCRAM_CALL_FACTORY.resources.setInitializationVector.path); } var __awaiter$2 = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var TapEncryption = /** @class */ (function () { // private _sessionState: BehaviorSubject<EncryptionKeys>; function TapEncryption(tap) { var _this = this; this.tap = tap; this.scramInterceptor = new ScramInterceptor(this.tap.service.scram); this.tap.client.addInterceptor(this.scramInterceptor); // this._sessionState = new BehaviorSubject<EncryptionKeys>(this._scramInterceptor.options.keys); this.tap.events.subscribe(function (event) { switch (event.type) { // Stop encryption on tap logout case 'tap-logout': _this.stop(); break; } }); } Object.defineProperty(TapEncryption.prototype, "isStarted", { get: function () { return this.scramInterceptor.options.encryption; }, enumerable: false, configurable: true }); TapEncryption.prototype.setEncryptionKeys = function (keys) { this.scramInterceptor.setEncryptionKeys(keys); }; TapEncryption.prototype.setEncryptedFrameCounter = function (frameCounter) { this.scramInterceptor.setFrameCounter(frameCounter); }; TapEncryption.prototype.getEncryptionOptions = function () { return this.scramInterceptor.options; }; TapEncryption.prototype.refreshEncryptionInitializationVector = function () { return this.scramInterceptor.refreshInitializationVectors(); }; TapEncryption.prototype.setInitializationVectorRefreshPeriod = function (period) { return this.scramInterceptor.setInitializationVectorRefreshPeriod(period); }; // /** // * Enable/Disable encrytion when communicating with a device // * @param enable true if requests must be encrypted // * @param resetSessionKey true if you want to reset the the session key. // * @param refreshInitializationVector true if you want to change initialization vectors // * // * @deprecated use {@link encryption()} instead // */ // public async enableEncryption( // enable: boolean, // resetSessionKey = false, // refreshInitializationVector = false // ): Promise<void> { // return this.encryption( // enable, // resetSessionKey, // refreshInitializationVector // ); // } // /** // * Enable/Disable encrytion when communicating with a device // * @param enable true if requests must be encrypted // * @param resetSessionKey true if you want to reset the the session key. // * @param refreshInitializationVector true if you want to change initialization vectors // * // */ // public async encryption( // enable: boolean, // resetSessionKey = false, // refreshInitializationVector = false // ): Promise<void> { // await this.scramInterceptor.encryption( // enable, // resetSessionKey, // refreshInitializationVector // ); // } /** * Enable encryption */ TapEncryption.prototype.start = function () { return __awaiter$2(this, void 0, void 0, function () { var keys; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.scramInterceptor.newSession()]; case 1: keys = _a.sent(); this.scramInterceptor.resumeEncryption(); return [2 /*return*/, keys]; } }); }); }; /** * Disable encryption */ TapEncryption.prototype.stop = function () { this.scramInterceptor.clearSession(); }; /** * Pause encryption */ TapEncryption.prototype.pause = function () { this.scramInterceptor.pauseEncryption(); }; /** * Pause encryption */ TapEncryption.prototype.resume = function () { this.scramInterceptor.resumeEncryption(); }; Object.defineProperty(TapEncryption.prototype, "sessionKey", { /** * Get current session key for encrypted communicatioin */ get: function () { return this.scramInterceptor.sessionKey; }, /** * Setter for the session key * @param key if null, it will stop encryption and remove session key. If true it will update session key used for encryption */ set: function (key) { this.scramInterceptor.sessionKey = key; }, enumerable: false, configurable: true }); return TapEncryption; }()); var ɵ0$1 = function (context) { return new TapEncryption(context.tap); }; var _TAP_EXTENSION_ENCRYPTION_ = tap.defineTapPropertyExtension('encryption', ɵ0$1); exports.TapAuthError = /** @class */ (function (_super) { _