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
- Host: GitHub
- URL: https://github.com/bryopsida/key-store
- Owner: bryopsida
- License: unlicense
- Created: 2023-02-05T18:57:30.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2024-10-23T11:44:53.000Z (over 1 year ago)
- Last Synced: 2024-10-23T12:27:13.123Z (over 1 year ago)
- Topics: data-encryption-key, esm, keystore, nodejs, typescript
- Language: TypeScript
- Homepage:
- Size: 1.2 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Key-Store
[](https://sonarcloud.io/summary/new_code?id=bryopsida_key-store) [](https://sonarcloud.io/summary/new_code?id=bryopsida_key-store) [](https://sonarcloud.io/summary/new_code?id=bryopsida_key-store) [](https://sonarcloud.io/summary/new_code?id=bryopsida_key-store) [](https://sonarcloud.io/summary/new_code?id=bryopsida_key-store) [](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)
```