UNPKG

@signalapp/mock-server

Version:
211 lines (210 loc) 7.53 kB
"use strict"; // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Device = void 0; const node_crypto_1 = require("node:crypto"); const debug_1 = __importDefault(require("debug")); const libsignal_client_1 = require("@signalapp/libsignal-client"); const zkgroup_1 = require("@signalapp/libsignal-client/zkgroup"); const types_1 = require("../types"); const debug = (0, debug_1.default)('mock:device'); // Technically, it is infinite. const PRE_KEY_ITERATOR_COUNT = 100; class Device { aci; deviceId; address; // If `true` - the device was provisioned and should receive messages over // the websocket. isProvisioned; capabilities; backupLevel = zkgroup_1.BackupLevel.Paid; accessKey; profileKeyCommitment; profileName; keys = new Map(); privPni; privNumber; privPniAddress; registrationId; pniRegistrationId; constructor(options) { this.aci = options.aci; this.deviceId = options.deviceId; this.registrationId = options.registrationId; this.privPni = options.pni; this.privNumber = options.number; this.pniRegistrationId = options.pniRegistrationId; this.isProvisioned = options.isProvisioned; this.address = libsignal_client_1.ProtocolAddress.new(this.aci, this.deviceId); this.privPniAddress = libsignal_client_1.ProtocolAddress.new(this.pni, this.deviceId); this.capabilities = { deleteSync: true, versionedExpirationTimer: true, ssre2: true, usernameChangeSyncMessage: true, }; } get debugId() { return `${this.aci}.${this.deviceId}`; } getRegistrationId(serviceIdKind) { switch (serviceIdKind) { case types_1.ServiceIdKind.ACI: return this.registrationId; case types_1.ServiceIdKind.PNI: return this.pniRegistrationId; } } get aciBinary() { return libsignal_client_1.Aci.parseFromServiceIdString(this.aci).getServiceIdBinary(); } get pni() { return this.privPni; } get pniBinary() { return libsignal_client_1.Pni.parseFromServiceIdString(this.pni).getServiceIdBinary(); } get aciRawUuid() { return libsignal_client_1.Aci.parseFromServiceIdString(this.aci).getRawUuidBytes(); } get pniRawUuid() { return libsignal_client_1.Pni.parseFromServiceIdString(this.pni).getRawUuidBytes(); } get number() { return this.privNumber; } get pniAddress() { return this.privPniAddress; } async changeNumber({ number, pni, pniRegistrationId, }) { this.privNumber = number; this.privPni = pni; this.pniRegistrationId = pniRegistrationId; this.privPniAddress = libsignal_client_1.ProtocolAddress.new(this.pni, this.deviceId); } async setKeys(serviceIdKind, keys) { debug('setting %s keys for %s', serviceIdKind, this.debugId); const existingKeys = this.keys.get(serviceIdKind); const { signedPreKey = existingKeys?.signedPreKey, lastResortKey = existingKeys?.lastResortKey, } = keys; if (!signedPreKey) { throw new Error('setKeys: Missing signedPreKey'); } if (!lastResortKey) { throw new Error('setKeys: Missing lastResortKey'); } this.keys.set(serviceIdKind, { identityKey: keys.identityKey, signedPreKey, preKeys: keys.preKeys?.slice() ?? [], kyberPreKeys: keys.kyberPreKeys?.slice() ?? [], lastResortKey, preKeyIterator: keys.preKeyIterator, kyberPreKeyIterator: keys.kyberPreKeyIterator, }); } async getIdentityKey(serviceIdKind = types_1.ServiceIdKind.ACI) { const keys = this.keys.get(serviceIdKind); if (!keys) { throw new Error('No keys available for device'); } return keys.identityKey; } async popSingleUseKey(serviceIdKind = types_1.ServiceIdKind.ACI) { const keys = this.keys.get(serviceIdKind); if (!keys) { throw new Error('No keys available for device'); } debug('popping single use key for %s', this.debugId); let preKey; if (keys.preKeyIterator) { const { value } = await keys.preKeyIterator.next(); preKey = value; } preKey ??= keys.preKeys.shift(); let pqPreKey; if (keys.kyberPreKeyIterator) { const { value } = await keys.kyberPreKeyIterator.next(); pqPreKey = value; } pqPreKey ??= keys.kyberPreKeys.shift(); pqPreKey ??= keys.lastResortKey; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, @typescript-eslint/strict-boolean-expressions if (!pqPreKey) { throw new Error('popSingleUseKey: Missing pqPreKey; checked iterator/array/lastResort'); } return { identityKey: keys.identityKey, signedPreKey: keys.signedPreKey, preKey, pqPreKey, }; } async getPreKeyCount(serviceIdKind = types_1.ServiceIdKind.ACI) { const keys = this.keys.get(serviceIdKind); if (!keys) { throw new Error('No keys available for device'); } if (keys.preKeyIterator) { return PRE_KEY_ITERATOR_COUNT; } return keys.preKeys.length; } async getKyberPreKeyCount(serviceIdKind = types_1.ServiceIdKind.ACI) { const keys = this.keys.get(serviceIdKind); if (!keys) { throw new Error('No keys available for device'); } if (keys.kyberPreKeyIterator) { return PRE_KEY_ITERATOR_COUNT; } return keys.kyberPreKeys.length; } getServiceIdByKind(serviceIdKind) { switch (serviceIdKind) { case types_1.ServiceIdKind.ACI: return this.aci; case types_1.ServiceIdKind.PNI: return this.pni; } } getServiceIdBinaryByKind(serviceIdKind) { switch (serviceIdKind) { case types_1.ServiceIdKind.ACI: return this.aciBinary; case types_1.ServiceIdKind.PNI: return this.pniBinary; } } getServiceIdKind(serviceId) { if (serviceId === this.aci) { return types_1.ServiceIdKind.ACI; } if (serviceId === this.pni) { return types_1.ServiceIdKind.PNI; } throw new Error(`Unknown serviceId: ${serviceId}`); } getServiceIdBinaryKind(serviceIdBinary) { if ((0, node_crypto_1.timingSafeEqual)(serviceIdBinary, this.aciBinary)) { return types_1.ServiceIdKind.ACI; } if ((0, node_crypto_1.timingSafeEqual)(serviceIdBinary, this.pniBinary)) { return types_1.ServiceIdKind.PNI; } throw new Error('Unknown serviceId'); } getAddressByKind(serviceIdKind) { switch (serviceIdKind) { case types_1.ServiceIdKind.ACI: return this.address; case types_1.ServiceIdKind.PNI: return this.pniAddress; } } } exports.Device = Device;