UNPKG

@wireapp/cryptobox

Version:

High-level API with persistent storage for Proteus.

140 lines (119 loc) 5.58 kB
import * as ProteusKeys from '@wireapp/proteus/dist/keys/root'; import * as ProteusSession from '@wireapp/proteus/dist/session/root'; import {CRUDEngine} from '@wireapp/store-engine/dist/commonjs/engine/index'; import {CryptoboxStore, PersistedRecord, SerialisedRecord} from '../store/root'; import {StoreEngine} from '@wireapp/store-engine'; import {Decoder, Encoder} from 'bazinga64'; class CryptoboxCRUDStore implements CryptoboxStore { constructor(private engine: CRUDEngine) {} static get KEYS() { return { LOCAL_IDENTITY: 'local_identity', }; } static get STORES() { return { LOCAL_IDENTITY: 'keys', PRE_KEYS: 'prekeys', SESSIONS: 'sessions', }; } private from_store(record: PersistedRecord): ArrayBuffer { return typeof record.serialised === 'string' ? Decoder.fromBase64(record.serialised).asBytes.buffer : record.serialised; } private to_store(serialised: ArrayBuffer | string): string { return Encoder.toBase64(serialised).asString; } public delete_all(): Promise<boolean> { return Promise.resolve() .then(() => this.engine.deleteAll(CryptoboxCRUDStore.STORES.LOCAL_IDENTITY)) .then(() => this.engine.deleteAll(CryptoboxCRUDStore.STORES.PRE_KEYS)) .then(() => this.engine.deleteAll(CryptoboxCRUDStore.STORES.SESSIONS)) .then(() => true); } public delete_prekey(prekey_id: number): Promise<number> { return this.engine.delete(CryptoboxCRUDStore.STORES.PRE_KEYS, prekey_id.toString()).then(() => prekey_id); } public load_identity(): Promise<ProteusKeys.IdentityKeyPair | undefined> { return this.engine .read<PersistedRecord>(CryptoboxCRUDStore.STORES.LOCAL_IDENTITY, CryptoboxCRUDStore.KEYS.LOCAL_IDENTITY) .then((record: PersistedRecord) => { const payload = this.from_store(record); const identity: ProteusKeys.IdentityKeyPair = ProteusKeys.IdentityKeyPair.deserialise(payload); return identity; }) .catch((error: Error) => { if (error instanceof StoreEngine.error.RecordNotFoundError) { return undefined; } throw error; }); } public load_prekey(prekey_id: number): Promise<ProteusKeys.PreKey | undefined> { return this.engine .read<PersistedRecord>(CryptoboxCRUDStore.STORES.PRE_KEYS, prekey_id.toString()) .then((record: PersistedRecord) => { const payload = this.from_store(record); return ProteusKeys.PreKey.deserialise(payload); }) .catch((error: Error) => { if (error instanceof StoreEngine.error.RecordNotFoundError) { return undefined; } throw error; }); } public load_prekeys(): Promise<Array<ProteusKeys.PreKey>> { return this.engine.readAll(CryptoboxCRUDStore.STORES.PRE_KEYS).then((records: Array<any>) => { const preKeys: Array<ProteusKeys.PreKey> = []; records.forEach((record: PersistedRecord) => { const payload = this.from_store(record); let preKey: ProteusKeys.PreKey = ProteusKeys.PreKey.deserialise(payload); preKeys.push(preKey); }); return preKeys; }); } public save_identity(identity: ProteusKeys.IdentityKeyPair): Promise<ProteusKeys.IdentityKeyPair> { const serialised = this.to_store(identity.serialise()); const payload: SerialisedRecord = new SerialisedRecord(serialised, CryptoboxCRUDStore.KEYS.LOCAL_IDENTITY); return this.engine.create(CryptoboxCRUDStore.STORES.LOCAL_IDENTITY, payload.id, payload).then(() => identity); } public save_prekey(pre_key: ProteusKeys.PreKey): Promise<ProteusKeys.PreKey> { const serialised = this.to_store(pre_key.serialise()); const payload: SerialisedRecord = new SerialisedRecord(serialised, pre_key.key_id.toString()); return this.engine.create(CryptoboxCRUDStore.STORES.PRE_KEYS, payload.id, payload).then(() => pre_key); } public save_prekeys(pre_keys: ProteusKeys.PreKey[]): Promise<ProteusKeys.PreKey[]> { const promises: Array<Promise<ProteusKeys.PreKey>> = pre_keys.map(pre_key => this.save_prekey(pre_key)); return Promise.all(promises).then(() => pre_keys); } public create_session(session_id: string, session: ProteusSession.Session): Promise<ProteusSession.Session> { const serialised = this.to_store(session.serialise()); const payload: SerialisedRecord = new SerialisedRecord(serialised, session_id); return this.engine.create(CryptoboxCRUDStore.STORES.SESSIONS, payload.id, payload).then(() => session); } public read_session(identity: ProteusKeys.IdentityKeyPair, session_id: string): Promise<ProteusSession.Session> { return this.engine .read<PersistedRecord>(CryptoboxCRUDStore.STORES.SESSIONS, session_id) .then((record: PersistedRecord) => { const payload = this.from_store(record); return ProteusSession.Session.deserialise(identity, payload); }); } public update_session(session_id: string, session: ProteusSession.Session): Promise<ProteusSession.Session> { const serialised = this.to_store(session.serialise()); const payload: SerialisedRecord = new SerialisedRecord(serialised, session_id); return this.engine .update(CryptoboxCRUDStore.STORES.SESSIONS, payload.id, {serialised: payload.serialised}) .then(() => session); } public delete_session(session_id: string): Promise<string> { return this.engine .delete(CryptoboxCRUDStore.STORES.SESSIONS, session_id) .then((primary_key: string) => primary_key); } } export default CryptoboxCRUDStore;