chamesu
Version:
This package contains all packages of the chat application.
302 lines (235 loc) • 10.2 kB
text/typescript
/*
* 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<keys.length; i++) {
const key : string = keys[i];
const keyPair = <SecurityKeyPair> await this.contactSelfKeys.getItem(key);
mutations.setContactSelfKeyPair(key, keyPair);
}
}
public async intContactedRelatedKeys() {
const keys : string[] = await this.contactRelatedKeys.keys();
for(let i=0; i<keys.length; i++) {
const key : string = keys[i];
const keyPair = <SecurityKeyPair> 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<keys.length; i++) {
const key : string = keys[i];
const keyPair = <SecurityKeyPair> await this.roomKeys.getItem(key);
mutations.setChatRoomKey(key, keyPair.publicKey);
}
}
// --------------------------------------------------------------------
// Contact - Keys ( self + related )
// --------------------------------------------------------------------
/**
* Export Keys for and of contacts.
*/
public async exportContactKeys() : Promise<ContactKeysSchema> {
let schema : ContactKeysSchema = {
selfKeyPairs: [],
relatedKeys: []
}
let keys : string[] = [];
keys = await this.contactSelfKeys.keys();
for(let i=0; i<keys.length; i++) {
schema.selfKeyPairs.push({
id: keys[i],
keyPair: <SecurityKeyPair> await this.contactSelfKeys.getItem<SecurityKeyPair>(keys[i])
})
}
keys = await this.contactRelatedKeys.keys();
for(let i=0; i<keys.length; i++) {
schema.relatedKeys.push({
id: keys[i],
keyPair: <SecurityKeyPair> await this.contactRelatedKeys.getItem<SecurityKeyPair>(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<SecurityKeyPair>(selfKeys[i].id, <SecurityKeyPair>selfKeys[i].keyPair);
mutations.setContactSelfKeyPair(selfKeys[i].id, <SecurityKeyPair> selfKeys[i].keyPair);
}
}
let relatedKeys = validationData.relatedKeys;
if(typeof relatedKeys !== 'undefined') {
for (let i = 0; i < relatedKeys.length; i++) {
await this.contactRelatedKeys.setItem<SecurityKeyPair>(relatedKeys[i].id, <SecurityKeyPair>relatedKeys[i].keyPair);
mutations.setContactRelatedKey(relatedKeys[i].id, (<SecurityKeyPair>relatedKeys[i].keyPair).publicKey);
}
}
}
// --------------------------------------------------------------------
public async saveContactSelfKeyPair(contactId: string, keys: SecurityKeyPair) : Promise<void> {
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<string> {
const keyPair = await this.contactRelatedKeys.getItem<SecurityKeyPair>(contactId);
if(!keyPair) {
throw new Error('Key not found');
}
return keyPair.publicKey;
}
public async dropContactRelatedKey(contactId: string) : Promise<void> {
await this.contactRelatedKeys.removeItem(contactId)
}
public async saveContactRelatedKey(contactId: string, key: string) : Promise<void> {
await this.contactRelatedKeys.setItem(contactId, {publicKey: key});
mutations.setContactRelatedKey(contactId, key);
}
// --------------------------------------------------------------------
public async getChatRoomKey(roomId: string) : Promise<string> {
const keyPair = await this.roomKeys.getItem<string>(roomId);
if(!keyPair) {
throw new Error('Key not found');
}
return keyPair;
}
public async dropChatRoomKey(roomId: string) : Promise<void> {
await this.roomKeys.removeItem(roomId)
}
public async saveChatRoomKey(roomId: string, key: string) : Promise<void> {
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();
}