import {EncodedKeyPackage, EncodedValue, KeyValue} from './types' import {KeyPackage} from './KeyPackage' import {Payload} from './Payload' /** Interface for a JSON-serializable shared value. */ export interface SerializedSharedValue { value: EncodedValue, envelopes: EncodedKeyPackage, } /** * A value that is shared and accessible by multiple encryption keys. */ export class SharedValue { /** The package of keys allowed to access the encoded value. */ public readonly package: KeyPackage /** * Create a new shared value. * @param name - the name of the initial sharer's key * @param initialSharerKey - an initial key, used to give access to the encoded value * @param value - the value to be encoded and shared */ public static async create(name: string, initialSharerKey: KeyValue, value: T): Promise> { const keyPackage = await KeyPackage.fromInitialSharer(name, initialSharerKey) const cryptor = await keyPackage.getCryptor(initialSharerKey) const payload = new Payload(value) const serialized = { value: await cryptor.encode(payload.encode()), ...(keyPackage.toJSON()), } return new SharedValue(serialized) } constructor( /** The serialized data for this shared value. */ private serialized: SerializedSharedValue, public readonly validator: (w: unknown) => w is T = (w: unknown): w is T => true, ) { this.package = KeyPackage.fromJSON(serialized) } /** * Get a Payload instance of the decoded value. * @param key - valid key to decode the payload */ async payload(key: KeyValue): Promise> { const cryptor = await this.package.getCryptor(key) const decodedValue = await cryptor.decode(this.serialized.value) return Payload.from(decodedValue) } /** * Get the value of this secret. * @param key - valid key to decode the value */ async get(key: KeyValue): Promise { const payload = await this.payload(key) const value = payload.value if ( !this.validator(value) ) { throw new TypeError('Invalid encoded value!') } return value } /** * Set a new value for this secret. * @param key - valid key to encode the value * @param value - new value to encode */ async set(key: KeyValue, value: T): Promise { const cryptor = await this.package.getCryptor(key) const payload = new Payload(value) this.serialized.value = await cryptor.encode(payload.encode()) } /** * Add a new key to the key package for this shared value. This allows * the `shareeKey` to access this shared value. * @param sharerKey - a currently valid key * @param shareeName - the name of the new key * @param shareeKey - the new key to add */ async addKey(sharerKey: KeyValue, shareeName: string, shareeKey?: KeyValue): Promise { return this.package.addCryptor(sharerKey, shareeName, shareeKey) } /** * Remove a given key from the key package. * @param sharerKey * @param shareeKey */ async removeKey(sharerKey: KeyValue, shareeName: string): Promise { await this.package.removeCryptor(sharerKey, shareeName) } /** Serialize the shared value securely. */ toJSON(): SerializedSharedValue { return { value: this.serialized.value, ...(this.package.toJSON()), } } }