An open API service indexing awesome lists of open source software.

https://github.com/bryopsida/key-store

A TypeScript Node.JS library that provides a basic key store for data encryption keys with 0 dependencies outside of dev dependencies
https://github.com/bryopsida/key-store

data-encryption-key esm keystore nodejs typescript

Last synced: 10 months ago
JSON representation

A TypeScript Node.JS library that provides a basic key store for data encryption keys with 0 dependencies outside of dev dependencies

Awesome Lists containing this project

README

          

# Key-Store

[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=bryopsida_key-store&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=bryopsida_key-store) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=bryopsida_key-store&metric=coverage)](https://sonarcloud.io/summary/new_code?id=bryopsida_key-store) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=bryopsida_key-store&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=bryopsida_key-store) [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=bryopsida_key-store&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=bryopsida_key-store) [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=bryopsida_key-store&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=bryopsida_key-store) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=bryopsida_key-store&metric=bugs)](https://sonarcloud.io/summary/new_code?id=bryopsida_key-store)

## What is this?

This is a typescript library that can be used to create a key store for managing root and data encryption keys. A basic file store key store is included but it can also be extended to persist to a shared store such as redis.
What problem does this solve? This was intiailly created as part of a data munging project in which I needed to be able to dynamically encrypt credentials and share them across a distributed system using redis.

### How do I use it?

To create your own store extend from the BaseKeyStore and implement the required key slot functions. For example:

```typescript
import { writeFile, mkdir, access, readFile, unlink, rm } from 'fs/promises'
import { resolveHome } from './resolve.js'
import {
BaseKeyStore,
IKeyStoreContextProvider,
IKeyStoreValueProvider,
} from './baseKeyStore'

/**
* A implementation of BaseKeyStore that stores the sealed
* keys into a desiginated spot in the file system, consumers
* of the class supply providers that give the password, salt
* and context to use for AAED
*/
export class FileKeyStore extends BaseKeyStore {
private readonly keyStorePath: string

/**
*
* @param {string} keyStorePath path to the folder where keys will be saved
* @param {IKeyStoreValueProvider} keyStorePasswordProvider provide the password used to seal keys
* @param {IKeyStoreValueProvider} keyStoreSaltProvider provide the salt used to seal keys
* @param {IKeyStoreContextProvider} keyStoreContextProvider provider that will give the appropriate context based on key id
*/
constructor(
keyStorePath: string,
keyStorePasswordProvider: IKeyStoreValueProvider,
keyStoreSaltProvider: IKeyStoreValueProvider,
keyStoreContextProvider: IKeyStoreContextProvider
) {
super(
keyStorePasswordProvider,
keyStoreSaltProvider,
keyStoreContextProvider
)
this.keyStorePath = resolveHome(keyStorePath)
}

/**
* @Inheritdoc
*/
protected hasKeyInSlot(keySlot: string): Promise {
return access(this.keyStorePath + '/' + keySlot)
.then(() => true)
.catch(() => false)
}

private async createKeyStoreDirIfNotExists(): Promise {
await access(this.keyStorePath).catch(async () => {
await mkdir(this.keyStorePath, { recursive: true })
})
}

/**
* @Inheritdoc
*/
protected async putKeyInSlot(keySlot: string, key: Buffer): Promise {
await this.createKeyStoreDirIfNotExists()
await writeFile(this.keyStorePath + '/' + keySlot, key)
}

/**
* @Inheritdoc
*/
protected async getKeyInSlot(keySlot: string): Promise {
return readFile(this.keyStorePath + '/' + keySlot)
}

/**
* @Inheritdoc
*/
protected async deleteKeySlot(keySlot: string): Promise {
await unlink(this.keyStorePath + '/' + keySlot)
}

/**
* @Inheritdoc
*/
protected async clearKeySlots(): Promise {
await rm(this.keyStorePath, { recursive: true, force: true, maxRetries: 3 })
}

/**
* @Inheritdoc
*/
async close(): Promise {
// nothing to do
}
}
```

You can then use the store like this (snippet from a test):

```typescript
const storeDir = tmpdir()
const key = randomBytes(32)
const salt = randomBytes(16)
const context = randomBytes(32)

// create a keystore
const keystore = new FileKeyStore(
storeDir + '/keystore',
() => Promise.resolve(key),
() => Promise.resolve(salt),
() => Promise.resolve(context)
)

// create random data to act as key store
const dek = randomBytes(32)
const id = randomUUID()

// save it
await keystore.saveSealedDataEncKey(id, dek)

// ask for it back
const fetchedDek = await keystore.fetchSealedDataEncKey(id)

// should be the same
expect(fetchedDek).toEqual(dek)
```