@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
JavaScript
;
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);
}
}