A library for multi-key reversible encryption
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

83 lines
3.0 KiB

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<KeyPackage> {
// 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)
/** 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<Cryptor> {
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<string>(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<KeyValue> {
// 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())
// 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,