UNPKG

@sebastianp265/safe-server-side-storage-client

Version:

Library for Confidential Server-Side Message Storage Using the Labyrinth Protocol

83 lines (82 loc) 4.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Labyrinth = void 0; const ThisDevice_1 = require("./device/ThisDevice"); const EpochStorage_1 = require("./EpochStorage"); const VirtualDevice_1 = require("./device/virtual-device/VirtualDevice"); const join_epoch_1 = require("./phases/join-epoch"); const authenticated_symmetric_encryption_1 = require("./crypto/authenticated-symmetric-encryption"); const utils_1 = require("./crypto/utils"); const key_derivation_1 = require("./crypto/key-derivation"); const open_new_epoch_based_on_current_1 = require("./phases/open-new-epoch-based-on-current"); class Labyrinth { thisDevice; epochStorage; static async checkIfLabyrinthIsInitialized(labyrinthServerClient) { return await labyrinthServerClient.checkIfLabyrinthIsInitialized(); } static async initialize(userId, labyrinthServerClient) { const { virtualDevice, virtualDeviceDecryptionKey, recoveryCode } = VirtualDevice_1.VirtualDevice.initialize(userId); const { firstEpoch, thisDevice } = await ThisDevice_1.ThisDevice.initialize(virtualDevice, virtualDeviceDecryptionKey, labyrinthServerClient); const epochStorage = EpochStorage_1.EpochStorage.createEmpty(); epochStorage.add(firstEpoch); const labyrinthInstance = new Labyrinth(thisDevice, epochStorage); return { labyrinthInstance, recoveryCode, }; } static async fromRecoveryCode(userId, recoveryCode, labyrinthServerClient) { const { virtualDevice, epoch } = await VirtualDevice_1.VirtualDevice.fromRecoveryCode(userId, recoveryCode, labyrinthServerClient); const epochStorage = EpochStorage_1.EpochStorage.createEmpty(); epochStorage.add(epoch); await (0, join_epoch_1.joinAllEpochs)(virtualDevice, epochStorage, labyrinthServerClient); const newestRecoveredEpoch = epochStorage.getNewestEpoch(); const thisDevice = await ThisDevice_1.ThisDevice.fromRecoveryCode(newestRecoveredEpoch, labyrinthServerClient); await labyrinthServerClient.notifyAboutDeviceActivity(thisDevice.id); await checkIfAnyDeviceExceededInactivityLimitAndOpenNewEpochIfNeeded(labyrinthServerClient, thisDevice, epochStorage); return new Labyrinth(thisDevice, epochStorage); } static async deserialize(labyrinthSerialized, labyrinthServerClient) { const { thisDevice: thisDeviceSerialized, epochStorage: epochStorageSerialized, } = labyrinthSerialized; const epochStorage = EpochStorage_1.EpochStorage.deserialize(epochStorageSerialized); const thisDevice = await ThisDevice_1.ThisDevice.deserialize(thisDeviceSerialized, epochStorage, labyrinthServerClient); await labyrinthServerClient.notifyAboutDeviceActivity(thisDevice.id); await checkIfAnyDeviceExceededInactivityLimitAndOpenNewEpochIfNeeded(labyrinthServerClient, thisDevice, epochStorage); return new Labyrinth(thisDevice, epochStorage); } serialize() { return { thisDevice: this.thisDevice.serialize(), epochStorage: this.epochStorage.serialize(), }; } constructor(thisDevice, epochStorage) { this.thisDevice = thisDevice; this.epochStorage = epochStorage; } encrypt(threadId, epochSequenceId, plaintext) { return (0, authenticated_symmetric_encryption_1.encryptWithRandomNonce)(deriveMessageKey(threadId, this.epochStorage.getEpoch(epochSequenceId)), (0, utils_1.asciiStringToBytes)(`message_thread_${threadId}`), plaintext); } decrypt(threadId, epochSequenceId, ciphertext) { return (0, authenticated_symmetric_encryption_1.decrypt)(deriveMessageKey(threadId, this.epochStorage.getEpoch(epochSequenceId)), (0, utils_1.asciiStringToBytes)(`message_thread_${threadId}`), ciphertext); } getNewestEpochSequenceId() { return this.epochStorage.getNewestEpoch().sequenceId; } getNewestEpochId() { return this.epochStorage.getNewestEpoch().id; } } exports.Labyrinth = Labyrinth; const CIPHER_VERSION = 1; function deriveMessageKey(threadId, epoch) { return (0, key_derivation_1.kdfOneKey)(epoch.rootKey, null, (0, utils_1.asciiStringToBytes)(`message_key_in_epoch_${epoch.sequenceId}_cipher_version_${CIPHER_VERSION}_${threadId}`)); } async function checkIfAnyDeviceExceededInactivityLimitAndOpenNewEpochIfNeeded(labyrinthServerClient, thisDevice, epochStorage) { const { didAnyDeviceExceedInactivityLimit } = await labyrinthServerClient.checkIfAnyDeviceExceedInactivityLimit(); if (didAnyDeviceExceedInactivityLimit) { const newCreatedEpoch = await (0, open_new_epoch_based_on_current_1.openNewEpochBasedOnCurrent)(epochStorage.getNewestEpoch(), labyrinthServerClient, thisDevice); epochStorage.add(newCreatedEpoch); } }