You've got 99 problems, but application-layer encryption ain't one.
Jay-Z is a TypeScript library that makes sever-side data encryption super easy at the application layer. It's built on libsodium and supports AWS KMS out of the box.
And if you're persisting data to DynamoDB - JayZ loves Beyonce. She supports him out the box (obviously).
Given the prevalence of mass data leaks, server-side encryption should be a requirement for any app you build that touches sentive data - e.g. PII.
Most cloud-based databases support a server-side encryption option. For example AWS DynamoDB.
This is a nice feature to have. But it's not sufficient security since data is encrypted at the data layer instead of the application layer. In the case of AWS, this means that anybody with access to your data store via the API or console is able to read the data is plaintext.
Jay-Z helps you with this problem by encrypting your data at the application layer, before sending it to your data store. That way you need more than just API or console access to read the data - you need the key.
JayZ is designed to work with KMS. But you can bring your own keys too.
First install jay-z - npm install @ginger.io/jay-z
import { KMS } from "aws-sdk"
import { KMSDataKeyProvider, JayZ } from "@ginger.io/jay-z"
const kmsKeyId = "..." // the KMS key id or arn you want to use
const keyProvider = new KMSDataKeyProvider(kmsKeyId, new KMS())
const jayZ = new JayZ({ keyProvider })
await jayZ.ready // wait for JayZ to be ready for encryption operations
type BankAccount = {
id: string
accountNumber: string
routingNumber: string
}
const bankAccount: BankAccount = {
id: "1",
accountNumber: "an-123",
routingNumber: "rn-123"
}
const encryptedItem = await jayZ.encryptItem({ item: bankAccount, fieldsToEncrypt: ["accountNumber", "routingNumber"] })
Here you specify only the fields you want encrypted. JayZ doesn't suffer foolish mistakes - so this API is completely type-safe.
The return type of encryptedItem
is a TypeScript Mapped Type. Encrypted fields types are changed to Uint8Array
s and the types of non-encrypted fields are passed through.
If you have the encrypted item in scope with the right types, you can just do:
const decryptedItem = await jayZ.decryptItem(encryptedItem))
And the correct type, BankAccount
in this example will be automatically inferred.
If you need to specify the type, just do:
const decryptedItem = await jayZ.decryptItem<BankAccount, any>(encryptedItem))
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:
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
.
-
Every time you encrypt data, JayZ uses the passed
DataKeyProvider
to generate key material. For example, if you're using theKMSDataKeyProvider
it will make an API call togenerateDataKey
. -
That key material is passed through libsodium's key derivation function,
crypto_kdf_derive_from_key
(https://libsodium.gitbook.io/doc/key_derivation) to produce an encryption key. The reason we do this is we might want to eventually support deriving othe types of keys as well - e.g. a signing key for a custom signature. -
Then, JayZ uses libsodium's
secretbox
to perform authenticated encryption and decryption using the derived key. Encryption performed on a per-field basis, and only on the specified fields. -
A
__jayz__metadata
field is appended to your object. This contains data like the encrypted data key (provided by the KeyProvider), the generated nonce, a list of field names that were encrypted and the encryption scheme version. -
When it's time to decrypt data, JayZ grabs the
__jayz__metadata
field off the object and asks theDataKeyProvider
to decrypt the data key (e.g.KMSDataKeyProvider
makes an API call todecrypt
). We re-derive the encryption key (same step as #2 above) and use that to decrypt each encrypted field on your object.