@ginger.io/jay-z
Advanced tools
Comparing version 0.0.9 to 0.0.10
@@ -22,5 +22,5 @@ import { EncryptionScheme, ItemWithEncryptedFields } from "./types"; | ||
readonly scheme: EncryptionScheme; | ||
encrypt<T, K extends keyof T>(params: EncryptParams<T, K>): Promise<EncryptResult<T, K>>; | ||
decrypt<T, K extends keyof T>(params: DecryptParams<T, K>): Promise<DecryptResult<T>>; | ||
encrypt<T, K extends keyof T>(params: EncryptParams<T, K>): EncryptResult<T, K>; | ||
decrypt<T, K extends keyof T>(params: DecryptParams<T, K>): DecryptResult<T>; | ||
} | ||
//# sourceMappingURL=Encryptor.d.ts.map |
@@ -10,6 +10,5 @@ "use strict"; | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.FixedDataKeyProvider = exports.LibsodiumEncryptor = exports.KMSDataKeyProvider = exports.JayZ = void 0; | ||
__exportStar(require("./Encryptor"), exports); | ||
@@ -16,0 +15,0 @@ var JayZ_1 = require("./JayZ"); |
@@ -9,2 +9,6 @@ import { DataKeyProvider } from "./DataKeyProvider"; | ||
}; | ||
export declare type EncryptItemRequest<T, K extends keyof T> = { | ||
item: T; | ||
fieldsToEncrypt: K[]; | ||
}; | ||
export declare class JayZ { | ||
@@ -14,9 +18,9 @@ private keyProvider; | ||
private maxUsesPerDataKey; | ||
private timesDataKeyUsed; | ||
private dataKey?; | ||
private currentDataKey?; | ||
private currentDataKeyUsesRemaining; | ||
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; | ||
encryptItems<T, K extends keyof T>(itemsToEncrypt: EncryptItemRequest<T, K>[]): Promise<EncryptedJayZItem<T, K>[]>; | ||
decryptItems<T, K extends keyof T>(itemsToDecrypt: EncryptedJayZItem<T, K>[]): Promise<T[]>; | ||
private zipItemsWithDataKey; | ||
} | ||
//# sourceMappingURL=JayZ.d.ts.map |
@@ -18,3 +18,2 @@ "use strict"; | ||
this.encryptor = new LibsodiumEncryptor_1.LibsodiumEncryptor(); | ||
this.timesDataKeyUsed = 0; | ||
this.keyProvider = config.keyProvider; | ||
@@ -26,52 +25,68 @@ this.encryptor = | ||
this.maxUsesPerDataKey = config.maxUsesPerDataKey || 1; | ||
this.currentDataKeyUsesRemaining = this.maxUsesPerDataKey; | ||
} | ||
encryptItem(item, fieldsToEncrypt) { | ||
encryptItems(itemsToEncrypt) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const { dataKey, encryptedDataKey } = yield this.getDataKey(); | ||
const { encryptedItem, nonce } = yield this.encryptor.encrypt({ | ||
item, | ||
fieldsToEncrypt, | ||
dataKey | ||
if (itemsToEncrypt.length === 0) { | ||
return []; | ||
} | ||
yield libsodium_wrappers_1.ready; | ||
const itemsWithKey = yield this.zipItemsWithDataKey(itemsToEncrypt); | ||
const items = itemsWithKey.map(({ itemToEncrypt, key }) => { | ||
const { item, fieldsToEncrypt } = itemToEncrypt; | ||
const { dataKey, encryptedDataKey } = key; | ||
const { encryptedItem, nonce } = this.encryptor.encrypt({ | ||
item, | ||
fieldsToEncrypt, | ||
dataKey | ||
}); | ||
const __jayz__metadata = { | ||
encryptedDataKey, | ||
nonce, | ||
scheme: this.encryptor.scheme, | ||
encryptedFieldNames: fieldsToEncrypt | ||
}; | ||
return Object.assign(Object.assign({}, encryptedItem), { __jayz__metadata }); | ||
}); | ||
const __jayz__metadata = { | ||
encryptedDataKey, | ||
nonce, | ||
scheme: this.encryptor.scheme, | ||
encryptedFieldNames: fieldsToEncrypt | ||
}; | ||
return Object.assign(Object.assign({}, encryptedItem), { __jayz__metadata }); | ||
return items; | ||
}); | ||
} | ||
decryptItem(encryptedJayZItem) { | ||
decryptItems(itemsToDecrypt) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
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; | ||
if (itemsToDecrypt.length === 0) { | ||
return []; | ||
} | ||
yield libsodium_wrappers_1.ready; | ||
const itemPromises = itemsToDecrypt.map((item) => __awaiter(this, void 0, void 0, function* () { | ||
const { nonce, encryptedDataKey, encryptedFieldNames } = item.__jayz__metadata; | ||
const encryptedItem = Object.assign({}, item); | ||
delete encryptedItem.__jayz__metadata; | ||
const dataKey = yield this.keyProvider.decryptDataKey(encryptedDataKey); | ||
const { decryptedItem } = this.encryptor.decrypt({ | ||
encryptedItem, | ||
fieldsToDecrypt: encryptedFieldNames, | ||
dataKey, | ||
nonce | ||
}); | ||
libsodium_wrappers_1.memzero(dataKey); | ||
return decryptedItem; | ||
})); | ||
return Promise.all(itemPromises); | ||
}); | ||
} | ||
getDataKey() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (this.dataKey === undefined) { | ||
this.timesDataKeyUsed = 1; | ||
this.dataKey = yield this.keyProvider.generateDataKey(); | ||
zipItemsWithDataKey(items) { | ||
const itemsWithDataKeys = items.map((itemToEncrypt) => { | ||
if (this.currentDataKey !== undefined && | ||
this.currentDataKeyUsesRemaining > 0) { | ||
this.currentDataKeyUsesRemaining -= 1; | ||
return { itemToEncrypt, key: this.currentDataKey }; | ||
} | ||
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; | ||
this.currentDataKey = this.keyProvider.generateDataKey(); | ||
this.currentDataKeyUsesRemaining = this.maxUsesPerDataKey - 1; | ||
return { itemToEncrypt, key: this.currentDataKey }; | ||
} | ||
return this.dataKey; | ||
}); | ||
const itemWithDataKeyPromises = itemsWithDataKeys.map(({ itemToEncrypt, key }) => __awaiter(this, void 0, void 0, function* () { return ({ itemToEncrypt, key: yield key }); })); | ||
return Promise.all(itemWithDataKeyPromises); | ||
} | ||
@@ -78,0 +93,0 @@ } |
@@ -10,4 +10,4 @@ import { DecryptParams, DecryptResult, Encryptor, EncryptParams, EncryptResult } from "./Encryptor"; | ||
readonly scheme = EncryptionScheme.V0_LIBSODIUM; | ||
encrypt<T, K extends keyof T>(params: EncryptParams<T, K>): Promise<EncryptResult<T, K>>; | ||
decrypt<T, K extends keyof T>(params: DecryptParams<T, K>): Promise<DecryptResult<T>>; | ||
encrypt<T, K extends keyof T>(params: EncryptParams<T, K>): EncryptResult<T, K>; | ||
decrypt<T, K extends keyof T>(params: DecryptParams<T, K>): DecryptResult<T>; | ||
private deriveKey; | ||
@@ -14,0 +14,0 @@ private convertBinaryFieldsToBuffers; |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -24,34 +15,28 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
encrypt(params) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield libsodium_wrappers_1.ready; | ||
const { item, fieldsToEncrypt, dataKey } = params; | ||
const nonce = libsodium_wrappers_1.randombytes_buf(libsodium_wrappers_1.crypto_secretbox_NONCEBYTES); | ||
const encryptionKey = this.deriveKey(dataKey, types_1.KeyType.ENCRYPTION); | ||
const encryptedFields = {}; | ||
fieldsToEncrypt.forEach((fieldName) => { | ||
encryptedFields[fieldName] = libsodium_wrappers_1.crypto_secretbox_easy(this.toBuffer(item[fieldName]), nonce, encryptionKey); | ||
}); | ||
libsodium_wrappers_1.memzero(encryptionKey); | ||
const encryptedItem = Object.assign(Object.assign({}, item), encryptedFields); | ||
return { encryptedItem, nonce }; | ||
const { item, fieldsToEncrypt, dataKey } = params; | ||
const nonce = libsodium_wrappers_1.randombytes_buf(libsodium_wrappers_1.crypto_secretbox_NONCEBYTES); | ||
const encryptionKey = this.deriveKey(dataKey, types_1.KeyType.ENCRYPTION); | ||
const encryptedFields = {}; | ||
fieldsToEncrypt.forEach((fieldName) => { | ||
encryptedFields[fieldName] = libsodium_wrappers_1.crypto_secretbox_easy(this.toBuffer(item[fieldName]), nonce, encryptionKey); | ||
}); | ||
libsodium_wrappers_1.memzero(encryptionKey); | ||
const encryptedItem = Object.assign(Object.assign({}, item), encryptedFields); | ||
return { encryptedItem, nonce }; | ||
} | ||
decrypt(params) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield libsodium_wrappers_1.ready; | ||
const { encryptedItem, fieldsToDecrypt, nonce, dataKey } = params; | ||
const decryptionKey = this.deriveKey(dataKey, types_1.KeyType.ENCRYPTION); | ||
const decryptedItem = Object.assign({}, encryptedItem); | ||
fieldsToDecrypt.forEach((fieldName) => { | ||
const cipherText = encryptedItem[fieldName]; | ||
const json = libsodium_wrappers_1.crypto_secretbox_open_easy(cipherText, nonce, decryptionKey); | ||
const fieldValue = JSON.parse(libsodium_wrappers_1.to_string(json)); | ||
// If you JSON.parse an object with a binary field that was stringified, | ||
// you don't get a Buffer/Uint8Array back but rather a JSON representation of it | ||
// So we special case here to convert JSON representations of buffers back to the expected type. | ||
decryptedItem[fieldName] = this.convertBinaryFieldsToBuffers(fieldValue); | ||
}); | ||
libsodium_wrappers_1.memzero(decryptionKey); | ||
return { decryptedItem }; | ||
const { encryptedItem, fieldsToDecrypt, nonce, dataKey } = params; | ||
const decryptionKey = this.deriveKey(dataKey, types_1.KeyType.ENCRYPTION); | ||
const decryptedItem = Object.assign({}, encryptedItem); | ||
fieldsToDecrypt.forEach((fieldName) => { | ||
const cipherText = encryptedItem[fieldName]; | ||
const json = libsodium_wrappers_1.crypto_secretbox_open_easy(cipherText, nonce, decryptionKey); | ||
const fieldValue = JSON.parse(libsodium_wrappers_1.to_string(json)); | ||
// If you JSON.parse an object with a binary field that was stringified, | ||
// you don't get a Buffer/Uint8Array back but rather a JSON representation of it | ||
// So we special case here to convert JSON representations of buffers back to the expected type. | ||
decryptedItem[fieldName] = this.convertBinaryFieldsToBuffers(fieldValue); | ||
}); | ||
libsodium_wrappers_1.memzero(decryptionKey); | ||
return { decryptedItem }; | ||
} | ||
@@ -58,0 +43,0 @@ deriveKey(dataKey, keyType) { |
{ | ||
"name": "@ginger.io/jay-z", | ||
"version": "0.0.9", | ||
"version": "0.0.10", | ||
"description": "Youve got 99 problems, but application-layer encryption aint one", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -54,3 +54,3 @@ # Jay-Z | ||
const encryptedItem = await jayZ.encryptItem(bankAccount, ["accountNumber", "routingNumber"]) | ||
const encryptedItem = await jayZ.encryptItems({ item: bankAccount, fieldsToEncrypt: ["accountNumber", "routingNumber"] }) | ||
``` | ||
@@ -68,3 +68,3 @@ | ||
```TypeScript | ||
const decryptedItem = await jayZ.decryptItem(encrypted)) | ||
const [decryptedItem] = await jayZ.decryptItems([encryptedItem])) | ||
``` | ||
@@ -77,3 +77,3 @@ | ||
```TypeScript | ||
const decryptedItem = await jayZ.decryptItem<BankAccount, any>(encrypted)) | ||
const [decryptedItem] = await jayZ.decryptItems<BankAccount, any>([encryptedItem])) | ||
``` | ||
@@ -80,0 +80,0 @@ |
@@ -30,7 +30,5 @@ import { EncryptionScheme, ItemWithEncryptedFields } from "./types" | ||
params: EncryptParams<T, K> | ||
): Promise<EncryptResult<T, K>> | ||
): EncryptResult<T, K> | ||
decrypt<T, K extends keyof T>( | ||
params: DecryptParams<T, K> | ||
): Promise<DecryptResult<T>> | ||
decrypt<T, K extends keyof T>(params: DecryptParams<T, K>): DecryptResult<T> | ||
} |
@@ -1,2 +0,2 @@ | ||
import { memzero } from "libsodium-wrappers" | ||
import { memzero, ready } from "libsodium-wrappers" | ||
import { DataKey, DataKeyProvider } from "./DataKeyProvider" | ||
@@ -13,2 +13,7 @@ import { Encryptor } from "./Encryptor" | ||
export type EncryptItemRequest<T, K extends keyof T> = { | ||
item: T | ||
fieldsToEncrypt: K[] | ||
} | ||
export class JayZ { | ||
@@ -19,5 +24,6 @@ private keyProvider: DataKeyProvider | ||
private maxUsesPerDataKey: number | ||
private timesDataKeyUsed: number = 0 | ||
private dataKey?: DataKey | ||
private currentDataKey?: Promise<DataKey> | ||
private currentDataKeyUsesRemaining: number | ||
constructor(config: JayZConfig) { | ||
@@ -30,67 +36,95 @@ this.keyProvider = config.keyProvider | ||
this.maxUsesPerDataKey = config.maxUsesPerDataKey || 1 | ||
this.currentDataKeyUsesRemaining = this.maxUsesPerDataKey | ||
} | ||
async encryptItem<T, K extends keyof T>( | ||
item: T, | ||
fieldsToEncrypt: K[] | ||
): Promise<EncryptedJayZItem<T, K>> { | ||
const { dataKey, encryptedDataKey } = await this.getDataKey() | ||
const { encryptedItem, nonce } = await this.encryptor.encrypt({ | ||
item, | ||
fieldsToEncrypt, | ||
dataKey | ||
async encryptItems<T, K extends keyof T>( | ||
itemsToEncrypt: EncryptItemRequest<T, K>[] | ||
): Promise<EncryptedJayZItem<T, K>[]> { | ||
if (itemsToEncrypt.length === 0) { | ||
return [] | ||
} | ||
await ready | ||
const itemsWithKey = await this.zipItemsWithDataKey(itemsToEncrypt) | ||
const items = itemsWithKey.map(({ itemToEncrypt, key }) => { | ||
const { item, fieldsToEncrypt } = itemToEncrypt | ||
const { dataKey, encryptedDataKey } = key | ||
const { encryptedItem, nonce } = this.encryptor.encrypt({ | ||
item, | ||
fieldsToEncrypt, | ||
dataKey | ||
}) | ||
const __jayz__metadata: EncryptedItemMetadata<T, K> = { | ||
encryptedDataKey, | ||
nonce, | ||
scheme: this.encryptor.scheme, | ||
encryptedFieldNames: fieldsToEncrypt | ||
} | ||
return { | ||
...encryptedItem, | ||
__jayz__metadata | ||
} | ||
}) | ||
const __jayz__metadata: EncryptedItemMetadata<T, K> = { | ||
encryptedDataKey, | ||
nonce, | ||
scheme: this.encryptor.scheme, | ||
encryptedFieldNames: fieldsToEncrypt | ||
} | ||
return items | ||
} | ||
return { | ||
...encryptedItem, | ||
__jayz__metadata | ||
async decryptItems<T, K extends keyof T>( | ||
itemsToDecrypt: EncryptedJayZItem<T, K>[] | ||
): Promise<T[]> { | ||
if (itemsToDecrypt.length === 0) { | ||
return [] | ||
} | ||
} | ||
async decryptItem<T, K extends keyof T>( | ||
encryptedJayZItem: EncryptedJayZItem<T, K> | ||
): Promise<T> { | ||
const { | ||
nonce, | ||
encryptedDataKey, | ||
encryptedFieldNames | ||
} = encryptedJayZItem.__jayz__metadata | ||
await ready | ||
const itemPromises = itemsToDecrypt.map(async (item) => { | ||
const { | ||
nonce, | ||
encryptedDataKey, | ||
encryptedFieldNames | ||
} = item.__jayz__metadata | ||
const encryptedItem = { ...encryptedJayZItem } | ||
delete (encryptedItem as any).__jayz__metadata | ||
const encryptedItem = { ...item } | ||
delete (encryptedItem as any).__jayz__metadata | ||
const dataKey = await this.keyProvider.decryptDataKey(encryptedDataKey) | ||
const dataKey = await this.keyProvider.decryptDataKey(encryptedDataKey) | ||
const { decryptedItem } = this.encryptor.decrypt<T, K>({ | ||
encryptedItem, | ||
fieldsToDecrypt: encryptedFieldNames, | ||
dataKey, | ||
nonce | ||
}) | ||
const { decryptedItem } = await this.encryptor.decrypt<T, K>({ | ||
encryptedItem, | ||
fieldsToDecrypt: encryptedFieldNames, | ||
dataKey, | ||
nonce | ||
memzero(dataKey) | ||
return decryptedItem | ||
}) | ||
memzero(dataKey) | ||
return decryptedItem | ||
return Promise.all(itemPromises) | ||
} | ||
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 zipItemsWithDataKey<T, K extends keyof T>( | ||
items: EncryptItemRequest<T, K>[] | ||
): Promise<{ itemToEncrypt: EncryptItemRequest<T, K>; key: DataKey }[]> { | ||
const itemsWithDataKeys = items.map((itemToEncrypt) => { | ||
if ( | ||
this.currentDataKey !== undefined && | ||
this.currentDataKeyUsesRemaining > 0 | ||
) { | ||
this.currentDataKeyUsesRemaining -= 1 | ||
return { itemToEncrypt, key: this.currentDataKey } | ||
} else { | ||
this.currentDataKey = this.keyProvider.generateDataKey() | ||
this.currentDataKeyUsesRemaining = this.maxUsesPerDataKey - 1 | ||
return { itemToEncrypt, key: this.currentDataKey } | ||
} | ||
}) | ||
return this.dataKey | ||
const itemWithDataKeyPromises = itemsWithDataKeys.map( | ||
async ({ itemToEncrypt, key }) => ({ itemToEncrypt, key: await key }) | ||
) | ||
return Promise.all(itemWithDataKeyPromises) | ||
} | ||
} |
@@ -32,7 +32,5 @@ import stringify from "fast-json-stable-stringify" | ||
async encrypt<T, K extends keyof T>( | ||
encrypt<T, K extends keyof T>( | ||
params: EncryptParams<T, K> | ||
): Promise<EncryptResult<T, K>> { | ||
await ready | ||
): EncryptResult<T, K> { | ||
const { item, fieldsToEncrypt, dataKey } = params | ||
@@ -58,7 +56,3 @@ const nonce = randombytes_buf(crypto_secretbox_NONCEBYTES) | ||
async decrypt<T, K extends keyof T>( | ||
params: DecryptParams<T, K> | ||
): Promise<DecryptResult<T>> { | ||
await ready | ||
decrypt<T, K extends keyof T>(params: DecryptParams<T, K>): DecryptResult<T> { | ||
const { encryptedItem, fieldsToDecrypt, nonce, dataKey } = params | ||
@@ -65,0 +59,0 @@ const decryptionKey = this.deriveKey(dataKey, KeyType.ENCRYPTION) |
@@ -5,3 +5,3 @@ import { | ||
ready, | ||
to_base64 | ||
to_base64, | ||
} from "libsodium-wrappers" | ||
@@ -20,3 +20,3 @@ import { DataKey, DataKeyProvider } from "../main/DataKeyProvider" | ||
"routingNumber", | ||
"notes" | ||
"notes", | ||
] | ||
@@ -26,3 +26,5 @@ | ||
const { jayz, bankAccount } = setup() | ||
const encryptedItem = await jayz.encryptItem(bankAccount, fieldsToEncrypt) | ||
const [encryptedItem] = await jayz.encryptItems([ | ||
{ item: bankAccount, fieldsToEncrypt }, | ||
]) | ||
@@ -35,3 +37,3 @@ expect(encryptedItem.pk).toEqual("account-123") | ||
expect(encryptedItem.notes).not.toEqual({ | ||
previousBalances: [0, 50] | ||
previousBalances: [0, 50], | ||
}) | ||
@@ -42,40 +44,96 @@ }) | ||
const { jayz, bankAccount } = setup() | ||
const encryptedItem = await jayz.encryptItem(bankAccount, fieldsToEncrypt) | ||
const decryptedItem = await jayz.decryptItem(encryptedItem) | ||
const [encryptedItem] = await jayz.encryptItems([ | ||
{ item: bankAccount, fieldsToEncrypt }, | ||
]) | ||
const [decryptedItem] = await jayz.decryptItems([encryptedItem]) | ||
expect(decryptedItem).toEqual(bankAccount) | ||
}) | ||
it("should not reuse data keys by default", async () => { | ||
it("should not reuse data keys by default when encryptItems invoked with multiple items", async () => { | ||
const keyProvider = new CountingKeyProvider() | ||
const { jayz, bankAccount } = setup({ | ||
keyProvider | ||
keyProvider, | ||
}) | ||
expect(keyProvider.keysIssued).toEqual(0) | ||
await jayz.encryptItem(bankAccount, fieldsToEncrypt) | ||
await jayz.encryptItems([ | ||
{ item: bankAccount, fieldsToEncrypt }, | ||
{ item: bankAccount, fieldsToEncrypt }, | ||
]) | ||
expect(keyProvider.keysIssued).toEqual(2) | ||
}) | ||
it("should not reuse data keys by default when encryptItems invoked multiple times", async () => { | ||
const keyProvider = new CountingKeyProvider() | ||
const { jayz, bankAccount } = setup({ | ||
keyProvider, | ||
}) | ||
expect(keyProvider.keysIssued).toEqual(0) | ||
await jayz.encryptItems([{ item: bankAccount, fieldsToEncrypt }]) | ||
expect(keyProvider.keysIssued).toEqual(1) | ||
await jayz.encryptItem(bankAccount, fieldsToEncrypt) | ||
await jayz.encryptItems([{ item: bankAccount, fieldsToEncrypt }]) | ||
expect(keyProvider.keysIssued).toEqual(2) | ||
}) | ||
it("should reuse data keys when configured to do so", async () => { | ||
it("should reuse data keys when encryptItems invoked once with multiple items", async () => { | ||
const keyProvider = new CountingKeyProvider() | ||
const { jayz, bankAccount } = setup({ | ||
keyProvider, | ||
maxUsesPerDataKey: 2 | ||
maxUsesPerDataKey: 2, | ||
}) | ||
const item1 = await jayz.encryptItem(bankAccount, fieldsToEncrypt) | ||
const [item1, item2, item3] = await jayz.encryptItems([ | ||
{ item: bankAccount, fieldsToEncrypt }, | ||
{ item: bankAccount, fieldsToEncrypt }, | ||
{ item: bankAccount, fieldsToEncrypt }, | ||
]) | ||
const [ | ||
decryptedItem1, | ||
decryptedItem2, | ||
decryptedItem3, | ||
] = await jayz.decryptItems([item1, item2, item3]) | ||
expect(decryptedItem1).toEqual(bankAccount) | ||
expect(decryptedItem2).toEqual(bankAccount) | ||
expect(decryptedItem3).toEqual(bankAccount) | ||
expect(keyProvider.keysIssued).toEqual(2) | ||
expect(item1.__jayz__metadata.encryptedDataKey).toEqual( | ||
item2.__jayz__metadata.encryptedDataKey | ||
) | ||
expect(item1.__jayz__metadata.encryptedDataKey).not.toEqual( | ||
item3.__jayz__metadata.encryptedDataKey | ||
) | ||
}) | ||
it("should reuse data keys when encryptItems invoked multiple times", async () => { | ||
const keyProvider = new CountingKeyProvider() | ||
const { jayz, bankAccount } = setup({ | ||
keyProvider, | ||
maxUsesPerDataKey: 2, | ||
}) | ||
const encryptAndDecrypt = async () => { | ||
const [encryptedItem] = await jayz.encryptItems([ | ||
{ item: bankAccount, fieldsToEncrypt }, | ||
]) | ||
const [decryptedItem] = await jayz.decryptItems([encryptedItem]) | ||
expect(decryptedItem).toEqual(bankAccount) | ||
} | ||
await encryptAndDecrypt() | ||
expect(keyProvider.keysIssued).toEqual(1) | ||
const item2 = await jayz.encryptItem(bankAccount, fieldsToEncrypt) | ||
await encryptAndDecrypt() | ||
expect(keyProvider.keysIssued).toEqual(1) | ||
const item3 = await jayz.encryptItem(bankAccount, fieldsToEncrypt) | ||
await encryptAndDecrypt() | ||
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) | ||
}) | ||
@@ -88,3 +146,3 @@ }) | ||
to_base64(randombytes_buf(crypto_kdf_KEYBYTES)) | ||
) | ||
), | ||
} | ||
@@ -105,4 +163,4 @@ ): { bankAccount: BankAccount; jayz: JayZ } { | ||
return { | ||
encryptedDataKey: key.slice(0), | ||
dataKey: key.slice(0) | ||
encryptedDataKey: key, | ||
dataKey: key, | ||
} | ||
@@ -109,0 +167,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
67274
986