Security News
Cloudflare Adds Security.txt Setup Wizard
Cloudflare has launched a setup wizard allowing users to easily create and manage a security.txt file for vulnerability disclosure on their websites.
Simplified and opinionated crypto library (wraps the Web Crypto API) for end-to-end encryption.
This cryptography thing is a pain to figure out and easy to get wrong. The library was built to implement symmetric end-to-end encryption in a web app.
SHA-256
for password hashingAES-GCM
for wrapping and encryptionBase64
as the outputDISCLAIMER: This is a high level explanation to get you started and not meant to be a replacement for security training.
In a simplistic way, symmetric encryption is when you use the same key for both encryption and decryption. As opposed to asymmetric encryption where you have a public key that you can freely share used to encrypt the data and a private key that you have to keep secret used to decrypt the data. Symmetric encryption is better for when you will be both encrypting and decrypting your own data and asymmetric is better when you are sending/receiving the data between different people since anybody can use the public key to encrypt data that only you (or anyone with the private key) can read.
For symmetric encryption you need a key. You can generate this key using keydude.generateEncryptionDecryptionKey()
. You must keep this key secret. You also want to make it so that if somebody gets access to it they can't just use it to decode all of your data. For this you 'wrap' the key before putting it in persistent storage using keydude.wrapKey()
. This is the equivalent of putting a physical key inside a safe box with password.
Wrapping a key requires two things, a passphrase and an initialization vector (IV). The passphrase is easy to understand, you can generate one for each user (you, the developer, will be able to decode the data if you want to) or you can let the user provide it in the client and not store it anywhere (the user data will be completely inaccessible to you the developer). The initialization vector sounds fancy, but it is just an array of cryptographicaly random bytes used to make the encryption more secure.
OK that may require a bit more of explaining. The AES-GCM algorithm is a block-cypher which is fancy speak for 'it encrypts blocks of data of a predetermined size at a time'. So if you had repeating blocks encrypted with the same key they would look the same. The initialization vector provided with each encrypting is used by the algorithm to prevent these blocks from being the same.
You can generate the IV using keydude.generateIV()
which returns a base64 encoded string. You will have to generate one and store it as you will have to provide it every time you wrap and unwrap your encryption/decryption key.
In summary you, generate a new key for a user with keydude.generateEncryptionDecryptionKey()
, generate an IV using keydude.generateIV()
, then wrap that key with keydude.wrapKey(<passphrase>, <generated IV>, <generated key>)
. You can store the generated IV and the wrapped key in the database. When you need to use the key to encrypt/decrypt just use keydude.unwrapKey(<passphrase>, <generated IV>, <wrapped key>)
. Finally, for convenience, if you are in a trusted client, once the user provides the passphrase and you download and unwrap the key you can re-wrap it and store it locally using some other passphrase so that the user does not have to keep entering the password. This could be using a PIN or some piece of user data like a user id.
After key management, this part is going to be very easy. Using the key that you extracted from keydude.unwrapKey()
you can encrypt your data using keydude.encrypt(<data object>, <unwrapped key>)
. This will return a single base64 encoded string containing both a new initialization vector (encrypt
generates a new one for every encryption/decryption for the algorithm to be secure) and the encrypted data. You can safely store this in you database or local storage.
Whenever you want to access this information again just call keydude.decrypt(<encrypted data generated with keydude.encrypt()>, <unwrapped key>)
.
npm install --save keydude
yarn add keydude
<script crossorigin src="https://unpkg.com/keydude@1/dist/keydude.js"></script>
All functions return a Promise. The sample uses async/await because it is easier to read.
import keydude from 'keydude'
// This should come from the user or something else
// like a user id (if storing localy) different for every user
const passphrase = 'my-C0mpl3x-p@ssKe7!$s.'
const newKey = await keydude.generateEncryptionDecryptionKey()
const keyWrappingIV = await keydude.generateIV()
// sample: 'T0IsmW6JljSCU1jC'
const wrappedKey = await keydude.wrapKey(passphrase, passphraseIV, key)
// sample: 'oc7/LI1u7tTPGuOZm3oOC20ztbEOTU0Dgp7I5QJPawXGMv44mNqJLIgZ9VNVVgpVbZBUJGpFr7GLJDu5',
// ...store keyWrappingIV and wrappedKey somewhere
// as you will need to unwrap the key in every
// session
const unwrappedKey = await keydude.unwrapKey(
passphrase,
keyWrappingIV,
wrappedKey
)
const encryptedData = await keydude.encrypt(
{ id: 'someid', other: Date.parse('2025-02-06') },
unwrappedKey
)
// sample: 'VL3RX4U9b55Y6OyTV2/3ifKeNS7/wgnr9ZXiiajADL8bHVu8dyZj9RjzA/Vi4z1M0L0wQ5nV84NHG+FHzrzB9BqghEhpqmzwbQ=='
const decryptedData = await keydude.decrypt(encryptedData, unwrappedKey)
// sample: { id: 'someid', other: Date.parse('2025-02-06') }
Function | Description |
---|---|
generateEncryptionDecryptionKey() | Generates a new encryption/decryption key. You should use wrapKey before storing the key anywhere and then unwrapKey when you need to use it. |
generateIV() | Generate a secure 96-bit initialization vector and returns it as a base64 encoded string. |
wrapKey(passphrase, base64PassphraseIV, keyToWrap) | Wrap (encode) the key using a key generated from the passphrase. |
unwrapKey(passphrase, base64PassphraseIV, wrappedKeyObject) | Unwraps a previously wrapped key so that it can be used. |
encrypt(dataObject, encryptionDecryptionKey) | This will call JSON.stringify, compress, and finally encrypt the provided dataObject. |
decrypt(encryptedDataObject, encryptionDecryptionKey) | Call this on the result of an encrypt call in order to decrypt the object. |
AES-GCM is used as the algorithm for encryption and decryption as well as wrapping and unwrapping keys.
After hours of research I found that many articles point to AES-GCM as the algorithm that strikes the best balance of security and performance. Here are a couple of quotes from the Wikipedia article on GCM.
"Galois/Counter Mode (GCM) is a mode of operation for symmetric key cryptographic block ciphers that has been widely adopted because of its efficiency and performance."
"GCM mode is used in the IEEE 802.1AE (MACsec) Ethernet security, IEEE 802.11ad (also known as WiGig), ANSI (INCITS) Fibre Channel Security Protocols (FC-SP), IEEE P1619.1 tape storage, IETF IPsec standards,[4][5] SSH[6] and TLS 1.2.[7][8] AES-GCM is included in the NSA Suite B Cryptography. GCM mode is used in the SoftEther VPN server and client,[9] as well as OpenVPN since version 2.4."
from Wikipedia article
The output of all data that is meant to be stored (everything except the generated keys) is encoded in base64. While this encoding increases the size of the data, it significantly simplifies moving the data around as it uses web safe characters. This means that it is easy to store the data as string in local storage options as well as NoSQL databases as string.
The key used to wrap/unwrap the encryption/decryption keys is generated from a passphrase. A SHA-256 hash is generated from the passphrase which is then used to generate a 256-bit key.
I was not able to find a reliable source with recommendations for iv length. What I did find was a lot of recommendations to use 96-bits with no documented source. The closest I could find was the following quote from a NIST report.
"The default length of the IV is 96 bits,[...]"
FAQs
Simplified and opinionated crypto library (wraps the Web Crypto API)
We found that keydude demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Cloudflare has launched a setup wizard allowing users to easily create and manage a security.txt file for vulnerability disclosure on their websites.
Security News
The Socket Research team breaks down a malicious npm package targeting the legitimate DOMPurify library. It uses obfuscated code to hide that it is exfiltrating browser and crypto wallet data.
Security News
ENISA’s 2024 report highlights the EU’s top cybersecurity threats, including rising DDoS attacks, ransomware, supply chain vulnerabilities, and weaponized AI.