UNPKG

@bryopsida/key-store

Version:

A extendable TypeScript Node.JS library that provides a basic key store for data encryption keys

100 lines 4.02 kB
import { createCipheriv, createDecipheriv, createHash, randomBytes, scrypt, } from 'crypto'; export class BaseKeyStore { keyStorePasswordProvider; keyStoreSaltProvider; keyStoreContextProvider; constructor(keyStorePasswordProvider, keyStoreSaltProvider, keyStoreContextProvider) { this.keyStorePasswordProvider = keyStorePasswordProvider; this.keyStoreSaltProvider = keyStoreSaltProvider; this.keyStoreContextProvider = keyStoreContextProvider; } async hasSealedRootKey(rootKeyId) { const keySlot = await this.getKeySlot('dek', rootKeyId, await this.keyStoreSaltProvider()); return this.hasKeyInSlot(keySlot); } async saveSealedRootKey(rootKeyId, key) { await this.saveSealedKey('root', rootKeyId, key); } async saveSealedDataEncKey(keyId, key) { await this.saveSealedKey('dek', keyId, key); } async hasSealedDataEncKey(keyId) { const keySlot = await this.getKeySlot('dek', keyId, await this.keyStoreSaltProvider()); return this.hasKeyInSlot(keySlot); } fetchSealedRootKey(rootKeyId) { return this.fetchSealedKey('root', rootKeyId); } fetchSealedDataEncKey(keyId) { return this.fetchSealedKey('dek', keyId); } async destroySealedRootKey(rootKeyId) { await this.deleteSealedKey('root', rootKeyId); } async destroySealedDataEncKey(keyId) { await this.deleteSealedKey('dek', keyId); } async destroyAllKeys() { await this.clearKeySlots(); } async getScryptKey(password, salt, context) { return new Promise((resolve, reject) => { scrypt(password, salt, 32, (err, key) => { if (err) { return reject(err); } return resolve(key); }); }); } async getKeySlot(type, keyId, salt) { const hash = createHash('sha256'); hash.update(type); hash.update(keyId); hash.update(salt); return hash.digest('hex'); } async saveSealedKey(type, keyId, key) { const salt = await this.keyStoreSaltProvider(); const keySlot = await this.getKeySlot(type, keyId, salt); const context = await this.keyStoreContextProvider(keyId); const password = await this.keyStorePasswordProvider(); const scryptKey = await this.getScryptKey(password, salt, context); const iv = randomBytes(16); const cipher = createCipheriv('aes-256-gcm', scryptKey, iv, { authTagLength: 16, }).setAAD(context); const ciphertext = Buffer.concat([ iv, cipher.update(key), cipher.final(), cipher.getAuthTag(), ]); await this.putKeyInSlot(keySlot, ciphertext); } async fetchSealedKey(type, keyId) { const salt = await this.keyStoreSaltProvider(); const keySlot = await this.getKeySlot(type, keyId, salt); if (!(await this.hasKeyInSlot(keySlot))) { throw new Error('Key not found'); } const key = await this.getKeyInSlot(keySlot); const context = await this.keyStoreContextProvider(keyId); const password = await this.keyStorePasswordProvider(); const scryptKey = await this.getScryptKey(password, salt, context); const iv = key.slice(0, 16); const authTag = key.slice(key.length - 16); const keyCipherText = key.slice(16, key.length - 16); const decipher = createDecipheriv('aes-256-gcm', scryptKey, iv, { authTagLength: 16, }).setAAD(context); decipher.setAuthTag(authTag); return Buffer.concat([decipher.update(keyCipherText), decipher.final()]); } async deleteSealedKey(type, keyId) { const salt = await this.keyStoreSaltProvider(); const keySlot = await this.getKeySlot(type, keyId, salt); await this.deleteKeySlot(keySlot); } } //# sourceMappingURL=baseKeyStore.js.map