/* * Copyright (c) 2022. * Author Peter Placzek (tada5hi) * For the full copyright and license information, * view the LICENSE file that was distributed with this source code. */ import {Context} from "@nuxt/types"; import { createPublicEncryptionKeyPair, createSymmetricEncryptionKey, decryptMessageSymmetric, decryptPublicEncryptionMessage, encryptMessageSymmetric, encryptPublicEncryptionMessage } from "~/modules/security/encryption/utils"; import * as localforage from "localforage"; import * as yup from "yup"; import {mutations, getters} from './store'; export type SecurityKeyPair = { publicKey: string, privateKey: string } type ContactKeysSchema = { selfKeyPairs: { id: string, keyPair: SecurityKeyPair }[], relatedKeys: { id: string, keyPair: SecurityKeyPair }[] } export class SecurityModule { protected ctx: Context; private contactSelfKeys : LocalForage; private contactRelatedKeys : LocalForage; private roomKeys: LocalForage; constructor(ctx: Context) { this.ctx = ctx; this.contactSelfKeys = localforage.createInstance({driver: localforage.INDEXEDDB, name: 'chatApp', storeName: 'contactSefKeys'}); this.contactRelatedKeys = localforage.createInstance({driver: localforage.INDEXEDDB, name: 'chatApp', storeName: 'contactRelatedKeys'}); this.roomKeys = localforage.createInstance({driver: localforage.INDEXEDDB, name: 'chatApp', storeName: 'roomKeys'}); if(!process.server) { this.init().then(r => r); } } async init() { await this.initContactSelfKeys(); await this.intContactedRelatedKeys(); await this.initRoomKeys(); } // -------------------------------------------------------------------- public async initContactSelfKeys() { const keys : string[] = await this.contactSelfKeys.keys(); for(let i=0; i await this.contactSelfKeys.getItem(key); mutations.setContactSelfKeyPair(key, keyPair); } } public async intContactedRelatedKeys() { const keys : string[] = await this.contactRelatedKeys.keys(); for(let i=0; i await this.contactRelatedKeys.getItem(key); mutations.setContactRelatedKey(key, keyPair.publicKey); } } public async initRoomKeys() { const keys : string[] = await this.roomKeys.keys(); for(let i=0; i await this.roomKeys.getItem(key); mutations.setChatRoomKey(key, keyPair.publicKey); } } // -------------------------------------------------------------------- // Contact - Keys ( self + related ) // -------------------------------------------------------------------- /** * Export Keys for and of contacts. */ public async exportContactKeys() : Promise { let schema : ContactKeysSchema = { selfKeyPairs: [], relatedKeys: [] } let keys : string[] = []; keys = await this.contactSelfKeys.keys(); for(let i=0; i await this.contactSelfKeys.getItem(keys[i]) }) } keys = await this.contactRelatedKeys.keys(); for(let i=0; i await this.contactRelatedKeys.getItem(keys[i]) }) } return schema; } /** * Import keys for and of contacts. * * * @param contactKeys */ public async importContactKeys(contactKeys: ContactKeysSchema | string) { let data : ContactKeysSchema | undefined; if(typeof contactKeys === 'string') { try { data = JSON.parse(contactKeys); } catch (e) { throw new Error('Die Zeichenkette ist nicht gültig.'); } } else { data = contactKeys; } let validationData = await useContactKeysImportValidator().validate(data); if(typeof validationData === 'undefined') { return; } let selfKeys = validationData.selfKeyPairs; if(typeof selfKeys !== 'undefined') { for (let i = 0; i < selfKeys.length; i++) { await this.contactSelfKeys.setItem(selfKeys[i].id, selfKeys[i].keyPair); mutations.setContactSelfKeyPair(selfKeys[i].id, selfKeys[i].keyPair); } } let relatedKeys = validationData.relatedKeys; if(typeof relatedKeys !== 'undefined') { for (let i = 0; i < relatedKeys.length; i++) { await this.contactRelatedKeys.setItem(relatedKeys[i].id, relatedKeys[i].keyPair); mutations.setContactRelatedKey(relatedKeys[i].id, (relatedKeys[i].keyPair).publicKey); } } } // -------------------------------------------------------------------- public async saveContactSelfKeyPair(contactId: string, keys: SecurityKeyPair) : Promise { await this.contactSelfKeys.setItem(contactId, keys); mutations.setContactSelfKeyPair(contactId, keys); } public async createAndSaveContactSelfKeyPair(contactId: string) { const keyPair = this.createKeyPair(); await this.saveContactSelfKeyPair(contactId, keyPair); return keyPair; } // -------------------------------------------------------------------- public async getContactRelatedKey(contactId: string) : Promise { const keyPair = await this.contactRelatedKeys.getItem(contactId); if(!keyPair) { throw new Error('Key not found'); } return keyPair.publicKey; } public async dropContactRelatedKey(contactId: string) : Promise { await this.contactRelatedKeys.removeItem(contactId) } public async saveContactRelatedKey(contactId: string, key: string) : Promise { await this.contactRelatedKeys.setItem(contactId, {publicKey: key}); mutations.setContactRelatedKey(contactId, key); } // -------------------------------------------------------------------- public async getChatRoomKey(roomId: string) : Promise { const keyPair = await this.roomKeys.getItem(roomId); if(!keyPair) { throw new Error('Key not found'); } return keyPair; } public async dropChatRoomKey(roomId: string) : Promise { await this.roomKeys.removeItem(roomId) } public async saveChatRoomKey(roomId: string, key: string) : Promise { await this.roomKeys.setItem(roomId, key); } // -------------------------------------------------------------------- createKeyPair() : SecurityKeyPair { return createPublicEncryptionKeyPair(); } createSymmetricKey() : string { return createSymmetricEncryptionKey(); } // -------------------------------------------------------------------- encodeMessageWithContact(message: string, contactId: string) { const selfKeyPair : SecurityKeyPair | undefined = getters.contactSelfKeyPair(contactId); const relatedKey = getters.contactRelatedKey(contactId); if(typeof selfKeyPair === 'undefined' || typeof relatedKey === 'undefined') { throw new Error('Die benötigten Schlüssel sind nicht verfügbar..'); } return encryptPublicEncryptionMessage(message, relatedKey, selfKeyPair.privateKey); } decodeMessageWithContact(message: string, contactId: string) { const selfKeyPair : SecurityKeyPair | undefined = getters.contactSelfKeyPair(contactId); const relatedKey = getters.contactRelatedKey(contactId); if(typeof selfKeyPair === 'undefined' || typeof relatedKey === 'undefined') { throw new Error('Die benötigten Schlüssel sind nicht verfügbar..'); } return decryptPublicEncryptionMessage(message, relatedKey, selfKeyPair.privateKey) } // -------------------------------------------------------------------- encodeMessageWithChatRoom(message: string, roomId: string) { const symmetricKey : string | undefined = getters.roomKey(roomId); if(typeof symmetricKey === 'undefined') { throw new Error('Die benötigte symmetrische Schlüssel existiert nicht...'); } return encryptMessageSymmetric(message, symmetricKey); } decodeMessageWithChatRoom(message: string, roomId: string) { const symmetricKey : string | undefined = getters.roomKey(roomId); if(typeof symmetricKey === 'undefined') { throw new Error('Die benötigte symmetrische Schlüssel existiert nicht...'); } return decryptMessageSymmetric(message, symmetricKey); } } export function useContactKeysImportValidator() { return yup.object().shape({ selfKeyPairs: yup.array().of(yup.object({ id: yup.string().required(), keyPair: yup.object({ privateKey: yup.string().required(), publicKey: yup.string().required() }).noUnknown(true) }).required()).default([]), relatedKeys: yup.array().of(yup.object({ id: yup.string().required(), keyPair: yup.object({ publicKey: yup.string().required() }).noUnknown(true) }).required()).default([]) }).noUnknown().required(); }