Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@ginger.io/jay-z

Package Overview
Dependencies
Maintainers
7
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ginger.io/jay-z - npm Package Compare versions

Comparing version 0.0.3 to 0.0.4

diagram.svg

5

dist/index.d.ts
export { DataKey, DataKeyProvider } from "./DataKeyProvider";
export { ItemWithEncryptedFields, JayZ } from "./JayZ";
export * from "./Encryptor";
export { JayZ, JayZConfig } from "./JayZ";
export { KMSDataKeyProvider } from "./KMSDataKeyProvider";
export { LibsodiumEncryptor } from "./LibsodiumEncryptor";
export { StubDataKeyProvider } from "./StubDataKeyProvider";
export * from "./types";
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });

@@ -7,4 +10,7 @@ var JayZ_1 = require("./JayZ");

exports.KMSDataKeyProvider = KMSDataKeyProvider_1.KMSDataKeyProvider;
var LibsodiumEncryptor_1 = require("./LibsodiumEncryptor");
exports.LibsodiumEncryptor = LibsodiumEncryptor_1.LibsodiumEncryptor;
var StubDataKeyProvider_1 = require("./StubDataKeyProvider");
exports.StubDataKeyProvider = StubDataKeyProvider_1.StubDataKeyProvider;
__export(require("./types"));
//# sourceMappingURL=index.js.map

34

dist/JayZ.d.ts
import { DataKeyProvider } from "./DataKeyProvider";
declare enum EncryptionVersion {
V_0 = 0
}
declare type EncryptedItemMetadata<T, K extends keyof T> = {
version: EncryptionVersion;
nonce: Uint8Array;
encryptedDataKey: Uint8Array;
encryptedFieldNames: K[];
import { Encryptor } from "./Encryptor";
import { EncryptedJayZItem } from "./types";
export declare type JayZConfig = {
keyProvider: DataKeyProvider;
encryptor?: Encryptor;
maxUsesPerDataKey?: number;
};
export declare type ItemWithEncryptedFields<T, K extends keyof T> = Omit<T, K> & {
[P in K]: Uint8Array;
} & {
__jayz__metadata: EncryptedItemMetadata<T, K>;
};
export declare class JayZ {
private keyProvider;
constructor(keyProvider: DataKeyProvider);
encryptItem<T, K extends keyof T>(item: T, fieldsToEncrypt: K[]): Promise<ItemWithEncryptedFields<T, K>>;
decryptItem<T, K extends keyof T>(encryptedItem: ItemWithEncryptedFields<T, K>): Promise<T>;
private toBuffer;
private generateEcryptionKey;
private deriveEncryptionKey;
private encryptor;
private maxUsesPerDataKey;
private timesDataKeyUsed;
private dataKey?;
constructor(config: JayZConfig);
encryptItem<T, K extends keyof T>(item: T, fieldsToEncrypt: K[]): Promise<EncryptedJayZItem<T, K>>;
decryptItem<T, K extends keyof T>(encryptedJayZItem: EncryptedJayZItem<T, K>): Promise<T>;
private getDataKey;
}
export {};

@@ -11,80 +11,68 @@ "use strict";

};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fast_json_stable_stringify_1 = __importDefault(require("fast-json-stable-stringify"));
const libsodium_wrappers_1 = require("libsodium-wrappers");
var KeyType;
(function (KeyType) {
KeyType[KeyType["ENCRYPTION"] = 1] = "ENCRYPTION";
KeyType[KeyType["SIGNING"] = 2] = "SIGNING";
})(KeyType || (KeyType = {}));
var EncryptionVersion;
(function (EncryptionVersion) {
EncryptionVersion[EncryptionVersion["V_0"] = 0] = "V_0"; // experimental
})(EncryptionVersion || (EncryptionVersion = {}));
const LibsodiumEncryptor_1 = require("./LibsodiumEncryptor");
class JayZ {
constructor(keyProvider) {
this.keyProvider = keyProvider;
constructor(config) {
this.encryptor = new LibsodiumEncryptor_1.LibsodiumEncryptor();
this.timesDataKeyUsed = 0;
this.keyProvider = config.keyProvider;
this.encryptor =
config.encryptor !== undefined
? config.encryptor
: new LibsodiumEncryptor_1.LibsodiumEncryptor();
this.maxUsesPerDataKey = config.maxUsesPerDataKey || 1;
}
encryptItem(item, fieldsToEncrypt) {
return __awaiter(this, void 0, void 0, function* () {
const { encryptedDataKey, derivedEncryptionKey, nonce } = yield this.generateEcryptionKey();
const encryptedFields = {};
fieldsToEncrypt.forEach(name => {
encryptedFields[name] = libsodium_wrappers_1.crypto_secretbox_easy(this.toBuffer(item[name]), nonce, derivedEncryptionKey);
const { dataKey, encryptedDataKey } = yield this.getDataKey();
const { encryptedItem, nonce } = yield this.encryptor.encrypt({
item,
fieldsToEncrypt,
dataKey
});
libsodium_wrappers_1.memzero(derivedEncryptionKey);
const __jayz__metadata = {
encryptedDataKey,
nonce,
version: EncryptionVersion.V_0,
scheme: this.encryptor.scheme,
encryptedFieldNames: fieldsToEncrypt
};
return Object.assign(Object.assign(Object.assign({}, item), encryptedFields), { __jayz__metadata });
return Object.assign(Object.assign({}, encryptedItem), { __jayz__metadata });
});
}
decryptItem(encryptedItem) {
decryptItem(encryptedJayZItem) {
return __awaiter(this, void 0, void 0, function* () {
const { nonce, encryptedDataKey, encryptedFieldNames } = encryptedItem.__jayz__metadata;
const decryptedDataKey = yield this.keyProvider.decryptDataKey(encryptedDataKey);
const decryptionKey = this.deriveEncryptionKey(decryptedDataKey, KeyType.ENCRYPTION);
const data = Object.assign({}, encryptedItem);
delete data.__jayz__metadata;
const decryptedItem = Object.assign({}, data);
encryptedFieldNames.forEach(name => {
const cipherText = encryptedItem[name];
const json = libsodium_wrappers_1.crypto_secretbox_open_easy(cipherText, nonce, decryptionKey);
decryptedItem[name] = JSON.parse(libsodium_wrappers_1.to_string(json));
const { nonce, encryptedDataKey, encryptedFieldNames } = encryptedJayZItem.__jayz__metadata;
const encryptedItem = Object.assign({}, encryptedJayZItem);
delete encryptedItem.__jayz__metadata;
const dataKey = yield this.keyProvider.decryptDataKey(encryptedDataKey);
const { decryptedItem } = yield this.encryptor.decrypt({
encryptedItem,
fieldsToDecrypt: encryptedFieldNames,
dataKey,
nonce
});
libsodium_wrappers_1.memzero(dataKey);
return decryptedItem;
});
}
toBuffer(value) {
const json = fast_json_stable_stringify_1.default(value);
return libsodium_wrappers_1.from_string(json);
}
generateEcryptionKey() {
getDataKey() {
return __awaiter(this, void 0, void 0, function* () {
yield libsodium_wrappers_1.ready;
const { dataKey, encryptedDataKey } = yield this.keyProvider.generateDataKey();
const nonce = libsodium_wrappers_1.randombytes_buf(libsodium_wrappers_1.crypto_secretbox_NONCEBYTES);
const derivedEncryptionKey = this.deriveEncryptionKey(dataKey, KeyType.ENCRYPTION);
// scrub root key from memory since we're done with it
libsodium_wrappers_1.memzero(dataKey);
return {
encryptedDataKey: encryptedDataKey,
derivedEncryptionKey,
nonce
};
if (this.dataKey === undefined) {
this.timesDataKeyUsed = 1;
this.dataKey = yield this.keyProvider.generateDataKey();
}
else if (this.timesDataKeyUsed >= this.maxUsesPerDataKey) {
libsodium_wrappers_1.memzero(this.dataKey.dataKey);
this.timesDataKeyUsed = 1;
this.dataKey = yield this.keyProvider.generateDataKey();
}
else {
this.timesDataKeyUsed += 1;
}
return this.dataKey;
});
}
deriveEncryptionKey(dataKey, keyType) {
const key = libsodium_wrappers_1.crypto_kdf_derive_from_key(libsodium_wrappers_1.crypto_secretbox_KEYBYTES, keyType, "__jayz__", // encryption context: must be 8 chars, per https://libsodium.gitbook.io/doc/key_derivation
dataKey);
return key;
}
}
exports.JayZ = JayZ;
//# sourceMappingURL=JayZ.js.map
{
"name": "@ginger.io/jay-z",
"version": "0.0.3",
"version": "0.0.4",
"description": "Youve got 99 problems, but application-layer encryption aint one",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

@@ -26,3 +26,3 @@ # Jay-Z

First install beyonce - `npm install @ginger.io/jay-z`
First install jay-z - `npm install @ginger.io/jay-z`

@@ -37,3 +37,3 @@ ### 2. Get yourself a JayZ

const keyProvider = new KMSDataKeyProvider(kmsKeyId, new KMS())
const jayZ = new JayZ(keyProvider)
const jayZ = new JayZ({ keyProvider })
```

@@ -80,4 +80,20 @@

## Reusing Data Keys
By default, JayZ will request a fresh data key from its `DataKeyProvider` on every encryption operation. If you'd like to trade security for speed and/or cost - you can configure this with the `maxUsesPerDataKey` setting:
```TypeScript
const jayZ = new JayZ({ keyProvider: ..., maxUsesPerDataKey: 100 })
```
This would use each data key for 100 `encrypt` operations, before requesting a fresh key from the configured `DataKeyProvider`.
## Design
### Diagram
![diagram.svg](diagram.svg)
### Additional Details
1. Every time you encrypt data, JayZ uses the passed `DataKeyProvider` to generate key material. For example, if you're using the `KMSDataKeyProvider` it will make an API call to [`generateDataKey`](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/KMS.html#generateDataKey-property).

@@ -84,0 +100,0 @@

export { DataKey, DataKeyProvider } from "./DataKeyProvider"
export { ItemWithEncryptedFields, JayZ } from "./JayZ"
export * from "./Encryptor"
export { JayZ, JayZConfig } from "./JayZ"
export { KMSDataKeyProvider } from "./KMSDataKeyProvider"
export { LibsodiumEncryptor } from "./LibsodiumEncryptor"
export { StubDataKeyProvider } from "./StubDataKeyProvider"
export * from "./types"

@@ -1,74 +0,45 @@

import stringify from "fast-json-stable-stringify"
import {
crypto_kdf_derive_from_key,
crypto_secretbox_easy,
crypto_secretbox_KEYBYTES,
crypto_secretbox_NONCEBYTES,
crypto_secretbox_open_easy,
from_string,
memzero,
randombytes_buf,
ready,
to_string
} from "libsodium-wrappers"
import { DataKeyProvider } from "./DataKeyProvider"
import { memzero } from "libsodium-wrappers"
import { DataKey, DataKeyProvider } from "./DataKeyProvider"
import { Encryptor } from "./Encryptor"
import { LibsodiumEncryptor } from "./LibsodiumEncryptor"
import { EncryptedItemMetadata, EncryptedJayZItem } from "./types"
enum KeyType {
ENCRYPTION = 1,
SIGNING = 2
export type JayZConfig = {
keyProvider: DataKeyProvider
encryptor?: Encryptor
maxUsesPerDataKey?: number
}
enum EncryptionVersion {
V_0 // experimental
}
export class JayZ {
private keyProvider: DataKeyProvider
private encryptor: Encryptor = new LibsodiumEncryptor()
type EncryptedItemMetadata<T, K extends keyof T> = {
version: EncryptionVersion
nonce: Uint8Array
encryptedDataKey: Uint8Array
encryptedFieldNames: K[]
}
private maxUsesPerDataKey: number
private timesDataKeyUsed: number = 0
private dataKey?: DataKey
type EncryptionMaterials = {
encryptedDataKey: Uint8Array
derivedEncryptionKey: Uint8Array
nonce: Uint8Array
}
export type ItemWithEncryptedFields<T, K extends keyof T> = Omit<T, K> &
{
[P in K]: Uint8Array
} & {
__jayz__metadata: EncryptedItemMetadata<T, K>
constructor(config: JayZConfig) {
this.keyProvider = config.keyProvider
this.encryptor =
config.encryptor !== undefined
? config.encryptor
: new LibsodiumEncryptor()
this.maxUsesPerDataKey = config.maxUsesPerDataKey || 1
}
export class JayZ {
constructor(private keyProvider: DataKeyProvider) {}
async encryptItem<T, K extends keyof T>(
item: T,
fieldsToEncrypt: K[]
): Promise<ItemWithEncryptedFields<T, K>> {
const {
encryptedDataKey,
derivedEncryptionKey,
nonce
} = await this.generateEcryptionKey()
const encryptedFields: { [P in K]: Uint8Array } = {} as any
fieldsToEncrypt.forEach(name => {
encryptedFields[name] = crypto_secretbox_easy(
this.toBuffer(item[name]),
nonce,
derivedEncryptionKey
)
): Promise<EncryptedJayZItem<T, K>> {
const { dataKey, encryptedDataKey } = await this.getDataKey()
const { encryptedItem, nonce } = await this.encryptor.encrypt({
item,
fieldsToEncrypt,
dataKey
})
memzero(derivedEncryptionKey)
const __jayz__metadata: EncryptedItemMetadata<T, K> = {
encryptedDataKey,
nonce,
version: EncryptionVersion.V_0,
scheme: this.encryptor.scheme,
encryptedFieldNames: fieldsToEncrypt

@@ -78,4 +49,3 @@ }

return {
...item,
...encryptedFields,
...encryptedItem,
__jayz__metadata

@@ -86,3 +56,3 @@ }

async decryptItem<T, K extends keyof T>(
encryptedItem: ItemWithEncryptedFields<T, K>
encryptedJayZItem: EncryptedJayZItem<T, K>
): Promise<T> {

@@ -93,71 +63,34 @@ const {

encryptedFieldNames
} = encryptedItem.__jayz__metadata
} = encryptedJayZItem.__jayz__metadata
const decryptedDataKey = await this.keyProvider.decryptDataKey(
encryptedDataKey
)
const encryptedItem = { ...encryptedJayZItem }
delete encryptedItem.__jayz__metadata
const decryptionKey = this.deriveEncryptionKey(
decryptedDataKey,
KeyType.ENCRYPTION
)
const dataKey = await this.keyProvider.decryptDataKey(encryptedDataKey)
const data = { ...encryptedItem }
delete data.__jayz__metadata
const decryptedItem: { [P in keyof T]: T[P] } = {
...data
} as any
encryptedFieldNames.forEach(name => {
const cipherText = encryptedItem[name]
const json = crypto_secretbox_open_easy(cipherText, nonce, decryptionKey)
decryptedItem[name] = JSON.parse(to_string(json))
const { decryptedItem } = await this.encryptor.decrypt<T, K>({
encryptedItem,
fieldsToDecrypt: encryptedFieldNames,
dataKey,
nonce
})
memzero(dataKey)
return decryptedItem
}
private toBuffer(value: any): Uint8Array {
const json = stringify(value)
return from_string(json)
}
private async generateEcryptionKey(): Promise<EncryptionMaterials> {
await ready
const {
dataKey,
encryptedDataKey
} = await this.keyProvider.generateDataKey()
const nonce = randombytes_buf(crypto_secretbox_NONCEBYTES)
const derivedEncryptionKey = this.deriveEncryptionKey(
dataKey,
KeyType.ENCRYPTION
)
// scrub root key from memory since we're done with it
memzero(dataKey)
return {
encryptedDataKey: encryptedDataKey,
derivedEncryptionKey,
nonce
private async getDataKey(): Promise<DataKey> {
if (this.dataKey === undefined) {
this.timesDataKeyUsed = 1
this.dataKey = await this.keyProvider.generateDataKey()
} else if (this.timesDataKeyUsed >= this.maxUsesPerDataKey) {
memzero(this.dataKey.dataKey)
this.timesDataKeyUsed = 1
this.dataKey = await this.keyProvider.generateDataKey()
} else {
this.timesDataKeyUsed += 1
}
}
private deriveEncryptionKey(
dataKey: Uint8Array,
keyType: KeyType
): Uint8Array {
const key = crypto_kdf_derive_from_key(
crypto_secretbox_KEYBYTES,
keyType,
"__jayz__", // encryption context: must be 8 chars, per https://libsodium.gitbook.io/doc/key_derivation
dataKey
)
return key
return this.dataKey
}
}

@@ -7,27 +7,20 @@ import {

} from "libsodium-wrappers"
import { JayZ } from "../main/JayZ"
import { DataKey, DataKeyProvider } from "../main/DataKeyProvider"
import { JayZ, JayZConfig } from "../main/JayZ"
import { StubDataKeyProvider } from "../main/StubDataKeyProvider"
import { aBankAccount, BankAccount } from "./util"
interface BankAccount {
pk: string // "bank-account-123",
sk: string // "Flava Flav",
accountNumber: string
routingNumber: string
balance: number
notes: {
[key: string]: any
}
}
describe("JayZ", () => {
beforeAll(async () => await ready)
const fieldsToEncrypt: (keyof BankAccount)[] = [
"accountNumber",
"balance",
"routingNumber",
"notes"
]
it("should encrypt an item", async () => {
const { jayz, bankAccount } = setup()
const encryptedItem = await jayz.encryptItem(bankAccount, [
"accountNumber",
"balance",
"routingNumber",
"notes"
])
const encryptedItem = await jayz.encryptItem(bankAccount, fieldsToEncrypt)

@@ -46,34 +39,71 @@ expect(encryptedItem.pk).toEqual("account-123")

const { jayz, bankAccount } = setup()
const encryptedItem = await jayz.encryptItem(bankAccount, [
"accountNumber",
"balance",
"routingNumber",
"notes"
])
const encryptedItem = await jayz.encryptItem(bankAccount, fieldsToEncrypt)
const decryptedItem = await jayz.decryptItem(encryptedItem)
expect(decryptedItem).toEqual(bankAccount)
})
it("should not reuse data keys by default", async () => {
const keyProvider = new CountingKeyProvider()
const { jayz, bankAccount } = setup({
keyProvider
})
expect(keyProvider.keysIssued).toEqual(0)
await jayz.encryptItem(bankAccount, fieldsToEncrypt)
expect(keyProvider.keysIssued).toEqual(1)
await jayz.encryptItem(bankAccount, fieldsToEncrypt)
expect(keyProvider.keysIssued).toEqual(2)
})
it("should reuse data keys when configured to do so", async () => {
const keyProvider = new CountingKeyProvider()
const { jayz, bankAccount } = setup({
keyProvider,
maxUsesPerDataKey: 2
})
const item1 = await jayz.encryptItem(bankAccount, fieldsToEncrypt)
expect(keyProvider.keysIssued).toEqual(1)
const item2 = await jayz.encryptItem(bankAccount, fieldsToEncrypt)
expect(keyProvider.keysIssued).toEqual(1)
const item3 = await jayz.encryptItem(bankAccount, fieldsToEncrypt)
expect(keyProvider.keysIssued).toEqual(2)
expect(await jayz.decryptItem(item1)).toEqual(bankAccount)
expect(await jayz.decryptItem(item2)).toEqual(bankAccount)
expect(await jayz.decryptItem(item3)).toEqual(bankAccount)
})
})
function setup(): { bankAccount: BankAccount; jayz: JayZ } {
const key = randombytes_buf(crypto_kdf_KEYBYTES)
const provider = new StubDataKeyProvider(to_base64(key))
function setup(
config: JayZConfig = {
keyProvider: new StubDataKeyProvider(
to_base64(randombytes_buf(crypto_kdf_KEYBYTES))
)
}
): { bankAccount: BankAccount; jayz: JayZ } {
const bankAccount = aBankAccount()
const jayz = new JayZ(provider)
const jayz = new JayZ(config)
return { jayz, bankAccount }
}
function aBankAccount(): BankAccount {
return {
pk: "account-123",
sk: "Flava Flav",
accountNumber: "123",
routingNumber: "456",
balance: 100,
notes: {
previousBalances: [0, 50]
class CountingKeyProvider implements DataKeyProvider {
public keysIssued = 0
async generateDataKey(): Promise<DataKey> {
await ready
const key = randombytes_buf(crypto_kdf_KEYBYTES)
this.keysIssued += 1
return {
encryptedDataKey: key.slice(0),
dataKey: key.slice(0)
}
}
async decryptDataKey(encryptedDataKey: Uint8Array): Promise<Uint8Array> {
return encryptedDataKey.slice(0)
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc