UNPKG

@sentclose/sentc-nodejs

Version:

End-to-end encryption sdk

1,026 lines 50.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Group = exports.getGroup = exports.prepareKeys = void 0; const Sentc_1 = require("./Sentc"); const AbstractSymCrypto_1 = require("./crypto/AbstractSymCrypto"); const file_1 = require("./file"); const sentc_common_1 = require("@sentclose/sentc-common"); const sentc_node_js_1 = require("@sentclose/sentc_node_js"); const path = require("path"); function prepareKeys(keys, page = 0) { const offset = page * 50; const end = offset + 50; const key_slice = keys.slice(offset, end); let str = "["; for (let i = 0; i < key_slice.length; i++) { const key = keys[i].group_key; str += key + ","; } //remove the trailing comma str = str.slice(0, -1); str += "]"; //it must be this string: [{"Aes":{"key":"D29y+nli2g4wn1GawdVmeGyo+W8HKc1cllkzqdEA2bA=","key_id":"123"}}] return [str, end < keys.length - 1]; } exports.prepareKeys = prepareKeys; /** * Get a group, from the storage or the server * */ async function getGroup(group_id, base_url, app_token, user, parent = false, group_as_member, verify = 0, rek = false) { const storage = await Sentc_1.Sentc.getStore(); let user_id; if (!group_as_member || group_as_member === "") { user_id = user.user_data.user_id; } else { user_id = group_as_member; } const group_key = "group_data" /* USER_KEY_STORAGE_NAMES.groupData */ + "_user_" + user_id + "_id_" + group_id; const group = await storage.getItem(group_key); const jwt = await user.getJwt(); if (group) { if (group.last_check_time + 60000 * 5 < Date.now()) { //check this every 5 min const update = await (0, sentc_node_js_1.groupGetGroupUpdates)(base_url, app_token, jwt, group_as_member); group.rank = update.rank; group.key_update = update.keyUpdate; group.last_check_time = Date.now(); //update the group data in the storage await storage.set(group_key, group); } return new Group(group, base_url, app_token, user); } const out = await (0, sentc_node_js_1.groupGetGroupData)(base_url, app_token, jwt, group_id, group_as_member); //save the fetched keys but only decrypt them when creating the group obj const fetched_keys = []; for (let i = 0; i < out.keys.length; i++) { const k = out.keys[i]; fetched_keys.push({ key_data: k.keyData, private_key_id: k.privateKeyId, signed_by_user_id: k.signedByUserId, signed_by_user_sign_key_id: k.signedByUserSignKeyId }); } //check parent or group as member access if the groups are already fetched const access_by_parent_group = out.accessByParentGroup; const access_by_group_as_member = out.accessByGroupAsMember; const parent_group_id = out.parentGroupId; if (access_by_group_as_member && access_by_group_as_member !== "") { //only load the group once even for rek. calls. // otherwise, when access_by_parent_group is also set, this group will be checked again when loading the parent if (!rek) { //if a group as member set. load this group first to get the keys //no group as member flag await getGroup(access_by_group_as_member, base_url, app_token, user, false, undefined, verify); } } if (access_by_parent_group) { parent = true; //check if the parent group is fetched //rec here because the user might be in a parent of the parent group or so //check the tree until we found the group where the user accesses by user await getGroup(parent_group_id, base_url, app_token, user, false, group_as_member, verify, true); } let group_data = { group_id: out.groupId, parent_group_id, from_parent: parent, rank: out.rank, key_update: out.keyUpdate, create_time: out.createdTime, joined_time: out.joinedTime, keys: [], key_map: new Map(), newest_key_id: "", access_by_group_as_member, access_by_parent_group, is_connected_group: out.isConnectedGroup, hmac_keys: [], sortable_keys: [], last_check_time: Date.now() }; const group_obj = new Group(group_data, base_url, app_token, user); //update the group obj and the group data (which we saved in store) with the decrypted keys. //it is ok to use the private key with an empty array, // because we are using the keys of the parent group when this is a child group const keys = await group_obj.decryptKey(fetched_keys, verify); group_data.keys = keys; group_obj.groupKeys = keys; const key_map = new Map(); //get the newest key const newest_key_id = keys[0].group_key_id; //insert in the key map for (let i = 0; i < keys.length; i++) { key_map.set(keys[i].group_key_id, i); } group_data.key_map = key_map; group_data.newest_key_id = newest_key_id; group_obj.groupKeyMap = key_map; group_obj.data.newest_key_id = newest_key_id; if (keys.length >= 50) { //fetch the rest of the keys via pagination, get the updated data back group_data = await group_obj.fetchKeys(jwt, verify); } //now decrypt the hmac key for searchable encryption, the right key must be fetched before const hmac_keys = []; for (let i = 0; i < out.hmacKeys.length; i++) { const k = out.hmacKeys[i]; hmac_keys.push({ key_data: k.keyData, group_key_id: k.groupKeyId }); } const decrypted_hmac_keys = await group_obj.decryptHmacKeys(hmac_keys); group_obj.data.hmac_keys = decrypted_hmac_keys; group_data.hmac_keys = decrypted_hmac_keys; const sortable_keys = []; for (let i = 0; i < out.sortableKeys.length; i++) { const k = out.sortableKeys[i]; sortable_keys.push({ key_data: k.keyData, group_key_id: k.groupKeyId }); } const decrypted_sortable_keys = await group_obj.decryptSortableKeys(sortable_keys); group_obj.data.sortable_keys = decrypted_sortable_keys; group_data.sortable_keys = decrypted_sortable_keys; await Promise.all([ //store the group data storage.set(group_key, group_data), //save always the newest public key storage.set("group_public_key" /* USER_KEY_STORAGE_NAMES.groupPublicKey */ + "_id_" + group_id, { key: keys[0].exported_public_key, id: keys[0].group_key_id }) ]); return group_obj; } exports.getGroup = getGroup; class Group extends AbstractSymCrypto_1.AbstractSymCrypto { constructor(data, base_url, app_token, user) { super(base_url, app_token); this.data = data; this.user = user; } set groupKeys(keys) { this.data.keys = keys; } set groupKeyMap(key_map) { this.data.key_map = key_map; } //__________________________________________________________________________________________________________________ getChildGroup(group_id, verify = 0) { return getGroup(group_id, this.base_url, this.app_token, this.user, true, this.data.access_by_group_as_member, verify); } getConnectedGroup(group_id, verify = 0) { //access the connected group from this group return getGroup(group_id, this.base_url, this.app_token, this.user, false, this.data.group_id, verify); } async getChildren(last_fetched_item = null) { var _a, _b; const jwt = await this.user.getJwt(); const last_fetched_time = (_a = last_fetched_item === null || last_fetched_item === void 0 ? void 0 : last_fetched_item.time.toString()) !== null && _a !== void 0 ? _a : "0"; const last_id = (_b = last_fetched_item === null || last_fetched_item === void 0 ? void 0 : last_fetched_item.group_id) !== null && _b !== void 0 ? _b : "none"; const out = await (0, sentc_node_js_1.groupGetAllFirstLevelChildren)(this.base_url, this.app_token, jwt, this.data.group_id, last_fetched_time, last_id, this.data.access_by_group_as_member); const list = []; for (let i = 0; i < out.length; i++) { const k = out[i]; list.push({ group_id: k.groupId, time: +k.time, parent: k.parent }); } return list; } prepareCreateChildGroup(sign = false) { const latest_key = this.getNewestKey(); let sign_key; if (sign) { sign_key = this.user.getNewestSignKey(); } const group_input = (0, sentc_node_js_1.groupPrepareCreateGroup)(latest_key.public_group_key, sign_key, this.user.user_data.user_id); return [group_input, latest_key.group_key_id]; } async createChildGroup(sign = false) { const latest_key = this.getNewestKey().public_group_key; let sign_key; if (sign) { sign_key = this.user.getNewestSignKey(); } const jwt = await this.user.getJwt(); return (0, sentc_node_js_1.groupCreateChildGroup)(this.base_url, this.app_token, jwt, latest_key, this.data.group_id, this.data.rank, this.data.access_by_group_as_member, sign_key, this.user.user_data.user_id); } async createConnectedGroup(sign = false) { const latest_key = this.getNewestKey().public_group_key; let sign_key; if (sign) { sign_key = this.user.getNewestSignKey(); } const jwt = await this.user.getJwt(); return (0, sentc_node_js_1.groupCreateConnectedGroup)(this.base_url, this.app_token, jwt, this.data.group_id, this.data.rank, latest_key, this.data.access_by_group_as_member, sign_key, this.user.user_data.user_id); } async groupUpdateCheck() { const out = await (0, sentc_node_js_1.groupGetGroupUpdates)(this.base_url, this.app_token, await this.getJwt(), this.data.group_id, this.data.access_by_group_as_member); this.data.rank = out.rank; this.data.key_update = out.keyUpdate; this.data.last_check_time = Date.now(); } async getMember(last_fetched_item = null) { var _a, _b; const jwt = await this.user.getJwt(); const last_fetched_time = (_a = last_fetched_item === null || last_fetched_item === void 0 ? void 0 : last_fetched_item.joined_time.toString()) !== null && _a !== void 0 ? _a : "0"; const last_id = (_b = last_fetched_item === null || last_fetched_item === void 0 ? void 0 : last_fetched_item.user_id) !== null && _b !== void 0 ? _b : "none"; const out = await (0, sentc_node_js_1.groupGetMember)(this.base_url, this.app_token, jwt, this.data.group_id, last_fetched_time, last_id, this.data.access_by_group_as_member); const list = []; for (let i = 0; i < out.length; i++) { const k = out[i]; list.push({ user_id: k.userId, rank: k.rank, joined_time: +k.joinedTime, user_type: k.userType }); } return list; } async stopInvites() { const jwt = await this.user.getJwt(); return (0, sentc_node_js_1.groupStopGroupInvites)(this.base_url, this.app_token, jwt, this.data.group_id, this.data.rank, this.data.access_by_group_as_member); } async prepareKeysForNewMember(user_id, rank, page = 0, group = false) { const key_count = this.data.keys.length; let public_key; if (group) { const k = await Sentc_1.Sentc.getGroupPublicKeyData(this.base_url, this.app_token, user_id); public_key = k.key; } else { const k = await Sentc_1.Sentc.getUserPublicKeyData(this.base_url, this.app_token, user_id); public_key = k.public_key; } const [key_string] = this.prepareKeys(page); return (0, sentc_node_js_1.groupPrepareKeysForNewMember)(public_key, key_string, key_count, rank, this.data.rank); } async handleInviteSessionKeysForNewMember(session_id, user_id, auto = false, group = false) { if (session_id === "") { return; } const jwt = await this.user.getJwt(); let public_key; if (group) { const k = await Sentc_1.Sentc.getGroupPublicKeyData(this.base_url, this.app_token, user_id); public_key = k.key; } else { const k = await Sentc_1.Sentc.getUserPublicKeyData(this.base_url, this.app_token, user_id); public_key = k.public_key; } let next_page = true; let i = 1; const p = []; while (next_page) { const next_keys = this.prepareKeys(i); next_page = next_keys[1]; p.push((0, sentc_node_js_1.groupInviteUserSession)(this.base_url, this.app_token, jwt, this.data.group_id, auto, session_id, public_key, next_keys[0], this.data.access_by_group_as_member)); i++; } return Promise.allSettled(p); } invite(user_id, rank) { return this.inviteUserInternally(user_id, rank); } inviteAuto(user_id, rank) { return this.inviteUserInternally(user_id, rank, true); } inviteGroup(group_id, rank) { return this.inviteUserInternally(group_id, rank, false, true); } inviteGroupAuto(group_id, rank) { return this.inviteUserInternally(group_id, rank, true, true); } reInviteUser(user_id) { return this.inviteUserInternally(user_id, undefined, false, false, true); } reInviteGroup(group_id) { return this.inviteUserInternally(group_id, undefined, false, true, true); } async inviteUserInternally(user_id, rank, auto = false, group = false, re_invite = false) { let public_key; if (group) { const k = await Sentc_1.Sentc.getGroupPublicKeyData(this.base_url, this.app_token, user_id); public_key = k.key; } else { const k = await Sentc_1.Sentc.getUserPublicKeyData(this.base_url, this.app_token, user_id); public_key = k.public_key; } const key_count = this.data.keys.length; const [key_string] = this.prepareKeys(); const jwt = await this.user.getJwt(); const session_id = await (0, sentc_node_js_1.groupInviteUser)(this.base_url, this.app_token, jwt, this.data.group_id, user_id, key_count, rank, this.data.rank, auto, group, re_invite, public_key, key_string, this.data.access_by_group_as_member); if (session_id === "") { return; } //upload the rest of the keys via session let next_page = true; let i = 1; const p = []; while (next_page) { const next_keys = this.prepareKeys(i); next_page = next_keys[1]; p.push((0, sentc_node_js_1.groupInviteUserSession)(this.base_url, this.app_token, jwt, this.data.group_id, auto, session_id, public_key, next_keys[0], this.data.access_by_group_as_member)); i++; } return Promise.allSettled(p); } //__________________________________________________________________________________________________________________ //join req async getJoinRequests(last_fetched_item = null) { var _a, _b; if (this.data.rank > 2) { throw (0, sentc_common_1.create_error)("client_201", "No permission to fulfill this action"); } const jwt = await this.user.getJwt(); const last_fetched_time = (_a = last_fetched_item === null || last_fetched_item === void 0 ? void 0 : last_fetched_item.time.toString()) !== null && _a !== void 0 ? _a : "0"; const last_id = (_b = last_fetched_item === null || last_fetched_item === void 0 ? void 0 : last_fetched_item.user_id) !== null && _b !== void 0 ? _b : "none"; const out = await (0, sentc_node_js_1.groupGetJoinReqs)(this.base_url, this.app_token, jwt, this.data.group_id, this.data.rank, last_fetched_time, last_id, this.data.access_by_group_as_member); const list = []; for (let i = 0; i < out.length; i++) { const k = out[i]; list.push({ user_id: k.userId, time: +k.time, user_type: k.userType }); } return list; } async rejectJoinRequest(user_id) { const jwt = await this.user.getJwt(); return (0, sentc_node_js_1.groupRejectJoinReq)(this.base_url, this.app_token, jwt, this.data.group_id, this.data.rank, user_id, this.data.access_by_group_as_member); } async acceptJoinRequest(user_id, user_type = 0, rank) { const jwt = await this.user.getJwt(); const key_count = this.data.keys.length; const [key_string] = this.prepareKeys(); let public_key; if (user_type === 2) { const k = await Sentc_1.Sentc.getGroupPublicKeyData(this.base_url, this.app_token, user_id); public_key = k.key; } else { const k = await Sentc_1.Sentc.getUserPublicKeyData(this.base_url, this.app_token, user_id); public_key = k.public_key; } const session_id = await (0, sentc_node_js_1.groupAcceptJoinReq)(this.base_url, this.app_token, jwt, this.data.group_id, user_id, key_count, rank, this.data.rank, public_key, key_string, this.data.access_by_group_as_member); if (session_id === "") { return; } let next_page = true; let i = 1; const p = []; while (next_page) { const next_keys = this.prepareKeys(i); next_page = next_keys[1]; p.push((0, sentc_node_js_1.groupJoinUserSession)(this.base_url, this.app_token, jwt, this.data.group_id, session_id, public_key, next_keys[0], this.data.access_by_group_as_member)); i++; } return Promise.allSettled(p); } //__________________________________________________________________________________________________________________ async leave() { const jwt = await this.user.getJwt(); return (0, sentc_node_js_1.leaveGroup)(this.base_url, this.app_token, jwt, this.data.group_id, this.data.access_by_group_as_member); } //__________________________________________________________________________________________________________________ //key rotation /** * Get the actual used public key. * For the user, or if the user joined via a parent group, the parent group public key * * Returns only the public key format, not the exported public key! * * @private */ async getPublicKey() { var _a, _b; //normal user access if (!this.data.from_parent && !this.data.access_by_group_as_member) { return this.user.getNewestPublicKey(); } //access with parent group -> get the keys from the parent group //no need to change for a group as a member because we are using the keys of the parent if (this.data.from_parent) { //choose the right user id. // when the user is accessing the group over a parent which is also accessed by a connected group let user_id; if (!this.data.access_by_group_as_member) { user_id = this.user.user_data.user_id; } else { user_id = this.data.access_by_group_as_member; } //get parent group public key const storage = await Sentc_1.Sentc.getStore(); const parent_group_key = "group_data" /* USER_KEY_STORAGE_NAMES.groupData */ + "_user_" + user_id + "_id_" + this.data.parent_group_id; const parent_group = await storage.getItem(parent_group_key); if (!parent_group) { throw new Error("Parent group not found. This group was access from parent group but the parent group data is gone."); } const newest_key_id = parent_group.newest_key_id; const index = parent_group.key_map.get(newest_key_id); if (index === undefined) { throw new Error("Parent group not found. This group was access from parent group but the parent group data is gone."); } //use the latest key const public_key = (_a = parent_group.keys[index]) === null || _a === void 0 ? void 0 : _a.public_group_key; if (!public_key) { throw new Error("Parent group not found. This group was access from parent group but the parent group data is gone."); } return public_key; } //access not over parent but from group as member -> use the keys from the group as member const storage = await Sentc_1.Sentc.getStore(); const connected_group_key = "group_data" /* USER_KEY_STORAGE_NAMES.groupData */ + "_user_" + this.user.user_data.user_id + "_id_" + this.data.access_by_group_as_member; const connected_group = await storage.getItem(connected_group_key); if (!connected_group) { throw new Error("Connected group not found. This group was access from a connected group but the group data is gone."); } const newest_key_id = connected_group.newest_key_id; const index = connected_group.key_map.get(newest_key_id); if (index === undefined) { throw new Error("Connected group not found. This group was access from a connected group but the group data is gone."); } //use the latest key const public_key = (_b = connected_group.keys[index]) === null || _b === void 0 ? void 0 : _b.public_group_key; if (!public_key) { throw new Error("Connected group not found. This group was access from a connected group but the group data is gone."); } return public_key; } getNewestKey() { let index = this.data.key_map.get(this.data.newest_key_id); if (!index) { index = 0; } return this.data.keys[index]; } /** * Gets the right private key to the used public key * * If it is from a user -> get it from a user * * If not, then form the parent group * * @param private_key_id * @private */ async getPrivateKey(private_key_id) { if (!this.data.from_parent && !this.data.access_by_group_as_member) { return this.user.getPrivateKey(private_key_id); } if (this.data.from_parent) { let user_id; if (!this.data.access_by_group_as_member) { user_id = this.user.user_data.user_id; } else { user_id = this.data.access_by_group_as_member; } //get parent group private key const storage = await Sentc_1.Sentc.getStore(); const parent_group_key = "group_data" /* USER_KEY_STORAGE_NAMES.groupData */ + "_user_" + user_id + "_id_" + this.data.parent_group_id; const parent_group_data = await storage.getItem(parent_group_key); if (!parent_group_data) { throw new Error("Parent group not found. This group was access from parent group but the parent group data is gone."); } const parent_group = new Group(parent_group_data, this.base_url, this.app_token, this.user); //private key id got the same id as the group key const group_key = await parent_group.getGroupKey(private_key_id); //use the latest key return group_key.private_group_key; } //access over "group as member" const storage = await Sentc_1.Sentc.getStore(); const connected_group_key = "group_data" /* USER_KEY_STORAGE_NAMES.groupData */ + "_user_" + this.user.user_data.user_id + "_id_" + this.data.access_by_group_as_member; const connected_group_data = await storage.getItem(connected_group_key); if (!connected_group_data) { throw new Error("Connected group not found. This group was access from a connected group but the group data is gone."); } const connected_group = new Group(connected_group_data, this.base_url, this.app_token, this.user); const group_key = await connected_group.getGroupKey(private_key_id); return group_key.private_group_key; } /** * Prepares the key rotation to use it with own backend. * * The newest public key is used to encrypt the key for the starter. * If the starter joined via a parent group, then the parent group public key is used */ async prepareKeyRotation(sign = false) { //if this is a child group -> start the key rotation with the parent key! const public_key = await this.getPublicKey(); let sign_key; if (sign) { sign_key = await this.getSignKey(); } return (0, sentc_node_js_1.groupPrepareKeyRotation)(this.getNewestKey().group_key, public_key, sign_key, this.user.user_data.user_id); } async keyRotation(sign = false) { const jwt = await this.user.getJwt(); let sign_key; if (sign) { sign_key = await this.getSignKey(); } const key_id = await (0, sentc_node_js_1.groupKeyRotation)(this.base_url, this.app_token, jwt, this.data.group_id, await this.getPublicKey(), this.getNewestKey().group_key, sign_key, this.user.user_data.user_id, this.data.access_by_group_as_member); return this.getGroupKey(key_id, true); } async finishKeyRotation(verify = 0) { const jwt = await this.user.getJwt(); let keys = []; const out = await (0, sentc_node_js_1.groupPreDoneKeyRotation)(this.base_url, this.app_token, jwt, this.data.group_id, this.data.access_by_group_as_member); for (let i = 0; i < out.length; i++) { const k = out[i]; keys.push({ pre_group_key_id: k.preGroupKeyId, new_group_key_id: k.newGroupKeyId, encrypted_eph_key_key_id: k.encryptedEphKeyKeyId, server_output: k.serverOutput }); } if (keys.length === 0) { return; } let next_round = false; let rounds_left = 10; //use always the newest public key const public_key = await this.getPublicKey(); do { const left_keys = []; //should be always there because the group rotation keys are ordered by time for (let i = 0; i < keys.length; i++) { const key = keys[i]; let pre_key; try { // eslint-disable-next-line no-await-in-loop pre_key = await this.getGroupKey(key.pre_group_key_id, false, verify); // eslint-disable-next-line no-empty } catch (e) { //the key isn't found -> try the next round } if (pre_key === undefined) { left_keys.push(key); continue; } //get the right used private key for each key // eslint-disable-next-line no-await-in-loop const private_key = await this.getPrivateKey(key.encrypted_eph_key_key_id); //await must be in this loop because we need the keys // eslint-disable-next-line no-await-in-loop await (0, sentc_node_js_1.groupFinishKeyRotation)(this.base_url, this.app_token, jwt, this.data.group_id, key.server_output, pre_key.group_key, public_key, private_key, this.data.access_by_group_as_member); //now get the new key and safe it // eslint-disable-next-line no-await-in-loop await this.getGroupKey(key.new_group_key_id, true, verify); } //when it runs 10 times and there are still left -> breakup rounds_left--; if (left_keys.length > 0) { keys = []; //push the not found keys into the key array, maybe the pre-group keys are in the next round keys.push(...left_keys); next_round = true; } else { next_round = false; } } while (next_round && rounds_left > 0); let user_id; if (!this.data.access_by_group_as_member) { user_id = this.user.user_data.user_id; } else { user_id = this.data.access_by_group_as_member; } //after a key rotation -> save the new group data in the store const storage = await Sentc_1.Sentc.getStore(); const group_key = "group_data" /* USER_KEY_STORAGE_NAMES.groupData */ + "_user_" + user_id + "_id_" + this.data.group_id; return storage.set(group_key, this.data); } //__________________________________________________________________________________________________________________ prepareUpdateRank(user_id, new_rank) { return (0, sentc_node_js_1.groupPrepareUpdateRank)(user_id, new_rank, this.data.rank); } async updateRank(user_id, new_rank) { const jwt = await this.user.getJwt(); //check if the updated user is the actual user -> then update the group store await (0, sentc_node_js_1.groupUpdateRank)(this.base_url, this.app_token, jwt, this.data.group_id, user_id, new_rank, this.data.rank, this.data.access_by_group_as_member); let actual_user_id; if (!this.data.access_by_group_as_member) { actual_user_id = this.user.user_data.user_id; } else { actual_user_id = this.data.access_by_group_as_member; } if (actual_user_id === user_id) { const storage = await Sentc_1.Sentc.getStore(); const group_key = "group_data" /* USER_KEY_STORAGE_NAMES.groupData */ + "_user_" + actual_user_id + "_id_" + this.data.group_id; this.data.rank = new_rank; return storage.set(group_key, this.data); } } async kickUser(user_id) { const jwt = await this.user.getJwt(); return (0, sentc_node_js_1.groupKickUser)(this.base_url, this.app_token, jwt, this.data.group_id, user_id, this.data.rank, this.data.access_by_group_as_member); } //__________________________________________________________________________________________________________________ //group as member async getGroups(last_fetched_item = null) { var _a, _b; const jwt = await this.getJwt(); const last_fetched_time = (_a = last_fetched_item === null || last_fetched_item === void 0 ? void 0 : last_fetched_item.time.toString()) !== null && _a !== void 0 ? _a : "0"; const last_id = (_b = last_fetched_item === null || last_fetched_item === void 0 ? void 0 : last_fetched_item.group_id) !== null && _b !== void 0 ? _b : "none"; const out = await (0, sentc_node_js_1.groupGetGroupsForUser)(this.base_url, this.app_token, jwt, last_fetched_time, last_id, this.data.group_id); const list = []; for (let i = 0; i < out.length; i++) { const g = out[i]; list.push({ group_id: g.groupId, rank: g.rank, joined_time: +g.joinedTime, parent: g.parent, time: +g.time }); } return list; } //join req to another group to connect async getGroupInvites(last_fetched_item = null) { var _a, _b; const jwt = await this.getJwt(); const last_fetched_time = (_a = last_fetched_item === null || last_fetched_item === void 0 ? void 0 : last_fetched_item.time.toString()) !== null && _a !== void 0 ? _a : "0"; const last_id = (_b = last_fetched_item === null || last_fetched_item === void 0 ? void 0 : last_fetched_item.group_id) !== null && _b !== void 0 ? _b : "none"; const out = await (0, sentc_node_js_1.groupGetInvitesForUser)(this.base_url, this.app_token, jwt, last_fetched_time, last_id, this.data.group_id, this.data.access_by_group_as_member); const list = []; for (let i = 0; i < out.length; i++) { const l = out[i]; list.push({ group_id: l.groupId, time: +l.time }); } return list; } async acceptGroupInvite(group_id) { const jwt = await this.getJwt(); return (0, sentc_node_js_1.groupAcceptInvite)(this.base_url, this.app_token, jwt, group_id, this.data.group_id, this.data.access_by_group_as_member); } async rejectGroupInvite(group_id) { const jwt = await this.getJwt(); return (0, sentc_node_js_1.groupRejectInvite)(this.base_url, this.app_token, jwt, group_id, this.data.group_id, this.data.access_by_group_as_member); } //join req to another group async groupJoinRequest(group_id) { const jwt = await this.getJwt(); return (0, sentc_node_js_1.groupJoinReq)(this.base_url, this.app_token, jwt, group_id, this.data.group_id, this.data.access_by_group_as_member); } async sentJoinReq(last_fetched_item = null) { var _a, _b; const jwt = await this.user.getJwt(); const last_fetched_time = (_a = last_fetched_item === null || last_fetched_item === void 0 ? void 0 : last_fetched_item.time.toString()) !== null && _a !== void 0 ? _a : "0"; const last_id = (_b = last_fetched_item === null || last_fetched_item === void 0 ? void 0 : last_fetched_item.group_id) !== null && _b !== void 0 ? _b : "none"; const out = await (0, sentc_node_js_1.groupGetSentJoinReq)(this.base_url, this.app_token, jwt, this.data.group_id, this.data.rank, last_fetched_time, last_id, this.data.access_by_group_as_member); const list = []; for (let i = 0; i < out.length; i++) { const l = out[i]; list.push({ group_id: l.groupId, time: +l.time }); } return list; } async deleteJoinReq(id) { const jwt = await this.user.getJwt(); return (0, sentc_node_js_1.groupDeleteSentJoinReq)(this.base_url, this.app_token, jwt, this.data.group_id, this.data.rank, id, this.data.access_by_group_as_member); } //__________________________________________________________________________________________________________________ async deleteGroup() { if (this.data.rank > 1) { throw (0, sentc_common_1.create_error)("client_201", "No permission to fulfill this action"); } const jwt = await this.user.getJwt(); return (0, sentc_node_js_1.groupDeleteGroup)(this.base_url, this.app_token, jwt, this.data.group_id, this.data.rank, this.data.access_by_group_as_member); } //__________________________________________________________________________________________________________________ async fetchKeys(jwt, verify = 0) { let last_item = this.data.keys[this.data.keys.length - 1]; let next_fetch = true; const keys = []; while (next_fetch) { // eslint-disable-next-line no-await-in-loop const out = await (0, sentc_node_js_1.groupGetGroupKeys)(this.base_url, this.app_token, jwt, this.data.group_id, last_item.time, last_item.group_key_id); const fetchedKeys = []; for (let i = 0; i < out.length; i++) { const key = out[i]; fetchedKeys.push({ key_data: key.keyData, private_key_id: key.privateKeyId, signed_by_user_id: key.signedByUserId, signed_by_user_sign_key_id: key.signedByUserSignKeyId }); } // eslint-disable-next-line no-await-in-loop const decrypted_key = await this.decryptKey(fetchedKeys, verify); keys.push(...decrypted_key); next_fetch = fetchedKeys.length >= 50; last_item = decrypted_key[fetchedKeys.length - 1]; } const last_inserted_key_index = this.data.keys.length; //insert in the key map for (let i = 0; i < keys.length; i++) { this.data.key_map.set(keys[i].group_key_id, i + last_inserted_key_index); } this.data.keys.push(...keys); //return the updated data so it can be saved in the store return this.data; } /** * Decrypt the key with the right private key. * * get the right private key for each key * * @param fetchedKeys * @param verify */ async decryptKey(fetchedKeys, verify = 0) { const keys = []; for (let i = 0; i < fetchedKeys.length; i++) { const fetched_key = fetchedKeys[i]; // eslint-disable-next-line no-await-in-loop const private_key = await this.getPrivateKey(fetched_key.private_key_id); let verify_key; if (verify > 0 && fetched_key.signed_by_user_id && fetched_key.signed_by_user_sign_key_id) { try { // eslint-disable-next-line no-await-in-loop verify_key = await Sentc_1.Sentc.getUserVerifyKeyData(this.base_url, this.app_token, fetched_key.signed_by_user_id, fetched_key.signed_by_user_sign_key_id); } catch (e) { //for "verify" = 1 ignore the error and just decrypt the key if (verify === 2) { //check if code === 100 -> user not found. if so, ignore this error and use no verify-key const err = JSON.parse(e); if (err.status !== "server_100") { throw e; } } } } const decrypted_keys = (0, sentc_node_js_1.groupDecryptKey)(private_key, fetched_key.key_data, verify_key); keys.push({ group_key_id: decrypted_keys.groupKeyId, group_key: decrypted_keys.groupKey, private_group_key: decrypted_keys.privateGroupKey, time: decrypted_keys.time, public_group_key: decrypted_keys.publicGroupKey, exported_public_key: decrypted_keys.exportedPublicKey }); } return keys; } async decryptHmacKeys(fetchedKeys) { const keys = []; for (let i = 0; i < fetchedKeys.length; i++) { const fetched_key = fetchedKeys[i]; // eslint-disable-next-line no-await-in-loop const group_key = await this.getSymKeyById(fetched_key.group_key_id); const decrypted_hmac_key = (0, sentc_node_js_1.groupDecryptHmacKey)(group_key, fetched_key.key_data); keys.push(decrypted_hmac_key); } return keys; } async decryptSortableKeys(fetchedKeys) { const keys = []; for (let i = 0; i < fetchedKeys.length; i++) { const fetched_key = fetchedKeys[i]; // eslint-disable-next-line no-await-in-loop const group_key = await this.getSymKeyById(fetched_key.group_key_id); const decrypted_key = (0, sentc_node_js_1.groupDecryptSortableKey)(group_key, fetched_key.key_data); keys.push(decrypted_key); } return keys; } prepareKeys(page = 0) { return prepareKeys(this.data.keys, page); } async getGroupKey(key_id, new_keys = false, verify = 0) { let key_index = this.data.key_map.get(key_id); if (key_index === undefined) { const jwt = await this.user.getJwt(); const fetched_key = await (0, sentc_node_js_1.groupGetGroupKey)(this.base_url, this.app_token, jwt, this.data.group_id, key_id, this.data.access_by_group_as_member); const key = { key_data: fetched_key.keyData, private_key_id: fetched_key.privateKeyId, signed_by_user_id: fetched_key.signedByUserId, signed_by_user_sign_key_id: fetched_key.signedByUserSignKeyId }; const decrypted_key = await this.decryptKey([key], verify); const last_inserted_key_index = this.data.keys.length; this.data.keys.push(decrypted_key[0]); this.data.key_map.set(decrypted_key[0].group_key_id, last_inserted_key_index); const storage = await Sentc_1.Sentc.getStore(); if (new_keys) { this.data.newest_key_id = decrypted_key[0].group_key_id; //save also the newest key in the cache await storage.set("group_public_key" /* USER_KEY_STORAGE_NAMES.groupPublicKey */ + "_id_" + this.data.group_id, { key: decrypted_key[0].exported_public_key, id: decrypted_key[0].group_key_id }); } let actual_user_id; if (!this.data.access_by_group_as_member) { actual_user_id = this.user.user_data.user_id; } else { actual_user_id = this.data.access_by_group_as_member; } const group_key = "group_data" /* USER_KEY_STORAGE_NAMES.groupData */ + "_user_" + actual_user_id + "_id_" + this.data.group_id; await storage.set(group_key, this.data); key_index = this.data.key_map.get(key_id); if (!key_index) { //key not found throw new Error("Group key not found. Maybe done key rotation will help"); } } const key = this.data.keys[key_index]; if (!key) { //key not found throw new Error("Group key not found. Maybe done key rotation will help"); } return key; } getGroupKeySync(key_id) { const key_index = this.data.key_map.get(key_id); if (key_index === undefined) { throw new Error("Key not found"); } const key = this.data.keys[key_index]; if (!key) { //key not found throw new Error("Group key not found. Maybe done key rotation will help"); } return key; } //__________________________________________________________________________________________________________________ getSymKeyToEncrypt() { const latest_key = this.getNewestKey(); return Promise.resolve([latest_key.group_key, latest_key.group_key_id]); } getSymKeyToEncryptSync() { const latest_key = this.getNewestKey(); return [latest_key.group_key, latest_key.group_key_id]; } async getSymKeyById(key_id) { const key = await this.getGroupKey(key_id); return key.group_key; } getSymKeyByIdSync(key_id) { const key = this.getGroupKeySync(key_id); return key.group_key; } getJwt() { return this.user.getJwt(); } getSignKey() { //always use the users sign key return this.user.getSignKey(); } getSignKeySync() { return this.user.getSignKeySync(); } getNewestHmacKey() { return this.data.hmac_keys[0]; } getNewestSortableKey() { return this.data.sortable_keys[0]; } //__________________________________________________________________________________________________________________ /** * Prepare the register of a file. The server input could be passed to the sentc api from your backend * * encrypted_file_name, key and master_key_id are only for the frontend to encrypt more data if necessary * * @param file * @throws SentcError */ async prepareRegisterFile(file) { const [key, encrypted_key] = await this.generateNonRegisteredKey(); const uploader = new file_1.Uploader(this.base_url, this.app_token, this.user, this.data.group_id, this.data.access_by_group_as_member); const [server_input, encrypted_file_name] = uploader.prepareFileRegister(file, key.key, encrypted_key, key.master_key_id); return { server_input, encrypted_file_name, key, master_key_id: key.master_key_id }; } /** * Validates the sentc file register output * Returns the file id * * @param server_output */ doneFileRegister(server_output) { const uploader = new file_1.Uploader(this.base_url, this.app_token, this.user, this.data.group_id, this.data.access_by_group_as_member); return uploader.doneFileRegister(server_output); } uploadFile(fileHandle, fileSize, content_key, session_id, sign = false, upload_callback) { //call this after "file register" const uploader = new file_1.Uploader(this.base_url, this.app_token, this.user, this.data.group_id, undefined, upload_callback, this.data.access_by_group_as_member); return uploader.checkFileUpload(fileHandle, fileSize, content_key.key, session_id, sign); } async getFileMetaInfo(file_id, downloader, verify_key) { //in an extra function to use the downloader //1. get the file info const file_meta = await downloader.downloadFileMetaInformation(file_id); //2. get the content key which was used to encrypt the file const key = await this.getNonRegisteredKey(file_meta.master_key_id, file_meta.encrypted_key); //3. get the file name if any if (file_meta.encrypted_file_name && file_meta.encrypted_file_name !== "") { file_meta.file_name = key.decryptString(file_meta.encrypted_file_name, verify_key); } return [file_meta, key]; } downloadFileMetaInfo(file_id, verify_key) { const downloader = new file_1.Downloader(this.base_url, this.app_token, this.user, this.data.group_id, this.data.access_by_group_as_member); return this.getFileMetaInfo(file_id, downloader, verify_key); } downloadFileWithMetaInfo(fileHandle, key, file_meta, verify_key, updateProgressCb) { const downloader = new file_1.Downloader(this.base_url, this.app_token, this.user, this.data.group_id, this.data.access_by_group_as_member); return downloader.downloadFileParts(fileHandle, file_meta.part_list, key.key, updateProgressCb, verify_key); } async createFile(file, file_name, sign = false, upload_callback) { //1st register a new key for this file const [key, encrypted_key] = await this.generateNonRegisteredKey(); //2nd encrypt and upload the file, use the created key const uploader = new file_1.Uploader(this.base_url, this.app_token, this.user, this.data.group_id, undefined, upload_callback, this.data.access_by_group_as_member); const [file_id, encrypted_file_name] = await uploader.uploadFile(file, file_name, key.key, encrypted_key, key.master_key_id, sign); return { file_id, master_key_id: key.master_key_id, encrypted_file_name }; } async createFileWithPath(path, sign = false, upload_callback) { //1st register a new key for this file const [key, encrypted_key] = await this.generateNonRegisteredKey(); //2nd encrypt and upload the file, use the created key const uploader = new file_1.Uploader(this.base_url, this.app_token, this.user, this.data.group_id, undefined, upload_callback, this.data.access_by_group_as_member); const [file_id, encrypted_file_name] = await uploader.uploadFileWithPath(path, key.key, encrypted_key, key.master_key_id, sign); return { file_id, master_key_id: key.master_key_id, encrypted_file_name }; } async downloadFile(file_path, file_id, verify_key, updateProgressCb) { const downloader = new file_1.Downloader(this.base_url, this.app_token, this.user, this.data.group_id, this.data.access_by_group_as_member); const [file_meta, key] = await this.getFileMetaInfo(file_id, downloader, verify_key); const file_name = await (0, file_1.findAvailableFileName)(path.join(file_path, file_meta.file_name)); if (!file_name) { throw new Error("Could not find a file name"); } await downloader.downloadFilePartsWithPath(file_name, file_meta.part_list, key.key, updateProgressCb, verify_key); return [ file_meta, key ]; } /** * Delete a file at the backend. Only creator or group admin can delete files * * @param file_id */ async deleteFile(file_id) { const jwt = await this.getJwt(); return (0, sentc_node_js_1.fileDeleteFile)(this.base_url, this.app_token, jwt, file_id, this.data.group_id, this.data.access_by_group_as_member); } //__________________________________________________________________________________________________________________ //searchable encryption createSearchRaw(data, full, limit) { const key = this.getNewestHmacKey(); return (0, sentc_node_js_1.createSearchableRaw)(key, data, full === undefined ? false : full, limit); } createSearch(data, full, limit) { const key = this.getNewestHmacKey(); const out = (0, sentc_node_js_1.createSearchable)(key, data, full === undefined ? false : full, limit); return [out.hashes, out.alg, out.keyId]; } search(data) { const key = this.getNewestHmacKey(); return (0, sentc_node_js_1.search)(key, data); } //__________________________________________________________________________________________________________________ //sortable encryptSortableRawNumber(number) { const key = this.getNewestSortableKey(); return (0, sentc_node_js_1.sortableEncryptRawNumber)(key, number); } encr