@wireapp/cryptobox
Version:
High-level API with persistent storage for Proteus.
140 lines (119 loc) • 5.58 kB
text/typescript
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;