import {EncodedValue, isEncodedPayload, KeyValue} from './types' import {Cryptor, InvalidKeyError} from './crypt/Cryptor' import {pkg} from './interface' import {Payload} from './Payload' /** * A collection of keys that are used to decode a base key. * * The base key is used to encode various values that can then be accessed * by all of the other keys in the package. */ export class KeyPackage { /** Create a new key package from an initial sharer's key. */ public static async fromInitialSharer(initialSharerKey: KeyValue): Promise { // Get the cryptor for the initial sharer const sharerCryptor = pkg.cryptor(initialSharerKey) // Encode a new global key using the initial sharer's key const envelope = new Payload(Cryptor.getNewKey()) const envelopeKey = await sharerCryptor.encode(envelope.encode()) // Return a new KeyPackage with the created envelope return new KeyPackage([envelopeKey]) } /** Instantiate a key package from its serialized JSON value. */ public static fromJSON(value: { envelopes: EncodedValue[] }): KeyPackage { return new KeyPackage(value.envelopes) } constructor( /** The encoded user keys. */ private readonly envelopes: EncodedValue[], ) {} /** * Get a Cryptor instance for master key given some user's key, if that key is valid. * @param key */ async getCryptor(key: KeyValue): Promise { const envelopeCryptor = pkg.cryptor(key) for ( const encodedKey of this.envelopes ) { // Attempt to decode one of the envelope keys using the input key const decodedKey = await envelopeCryptor.decode(encodedKey) if ( isEncodedPayload(decodedKey) ) { // We successfully decoded the envelope, which contains the global key return pkg.cryptor(Payload.from(decodedKey).value) } } throw new InvalidKeyError() } /** * Add a user's key to this package, granting them access to the master key. * @param sharerKey - a currently valid user's key * @param shareeKey - the key to add to the package */ async addCryptor(sharerKey: KeyValue, shareeKey?: KeyValue): Promise { // Validate the sharer's key and get the cryptor const sharerCryptor = await this.getCryptor(sharerKey) // Create a new cryptor for the sharee const determinedShareeKey = shareeKey || Cryptor.getNewKey() const shareeCryptor = pkg.cryptor(determinedShareeKey) // Encode the global key using the sharee's key const envelope = new Payload(sharerCryptor.getKey()) const envelopeKey = await shareeCryptor.encode(envelope.encode()) this.envelopes.push(envelopeKey) // Return the sharee's key that can be used to decode the global key return determinedShareeKey } /** Serialize the key package securely. */ toJSON() { return { envelopes: this.envelopes, } } }