Skip to content

Instantly share code, notes, and snippets.

@iamnivekx
Last active April 24, 2024 02:08
Show Gist options
  • Save iamnivekx/af067fe0d2af0d71fbdee0f2645672aa to your computer and use it in GitHub Desktop.
Save iamnivekx/af067fe0d2af0d71fbdee0f2645672aa to your computer and use it in GitHub Desktop.
encrypt and decrypt the text using the password.
/**
* This file is used to encrypt and decrypt the text using the password.
* The password is used to derive the key using PBKDF2.
* The key is used to encrypt and decrypt the text using AES-GCM.
* The salt is used to derive the key using PBKDF2.
* The nonce is used to encrypt and decrypt the text using AES-GCM.
* The ciphertext is the encrypted text.
* The salt, nonce, and ciphertext are concatenated and converted to Base64.
* inspire by https://github.com/MetaMask/metamask-extension/blob/develop/app/scripts/controllers/user-storage/encryption.ts
*/
import { gcm } from '@noble/ciphers/aes';
import { randomBytes } from '@noble/ciphers/webcrypto';
import { pbkdf2, Pbkdf2Opt } from '@noble/hashes/pbkdf2';
import { sha256 } from '@noble/hashes/sha256';
import { concatBytes, utf8ToBytes } from '@noble/hashes/utils';
const ALGORITHM_KEY_SIZE = 16;
const ALGORITHM_NONCE_SIZE = 12;
const PBKDF2_SALT_SIZE = 16;
const PBKDF2_ITERATIONS = 900_000;
const pbkdf2Opt: Pbkdf2Opt = { c: PBKDF2_ITERATIONS, dkLen: ALGORITHM_KEY_SIZE };
export function bytesToBase64(byteArray: Uint8Array) {
return Buffer.from(byteArray).toString('base64');
}
export function base64ToBytes(base64: string) {
return new Uint8Array(Buffer.from(base64, 'base64'));
}
export function bytesToUtf8(bytes: Uint8Array) {
// if (typeof bytes !== 'string') throw new Error(`bytesToUtf8 expected string, got ${typeof str}`);
const decoder = new TextDecoder('utf-8');
return decoder.decode(bytes);
}
/**
* This function is used to encrypt the text with the key
* @param text: the text to encrypt, it can be any string.
* @param password: the password, it can be any string.
* @returns [salt(PBKDF2_SALT_SIZE bytes)][nonce(ALGORITHM_NONCE_SIZE bytes)][ciphertext(? bytes)] | base64
*/
export function encrypt(text: string, password: string) {
// prepend salt.
const salt = randomBytes(PBKDF2_SALT_SIZE);
// Derive a key using PBKDF2.
const key = pbkdf2(sha256, password, salt, pbkdf2Opt);
// prepend salt.
const plaintext = utf8ToBytes(text);
// prepend nonce.
const nonce = randomBytes(ALGORITHM_NONCE_SIZE);
// Encrypt
const encrypted = gcm(key, nonce).encrypt(plaintext);
const ciphertextAndNonceAndSalt = concatBytes(salt, nonce, encrypted);
// Convert to Base64
return bytesToBase64(ciphertextAndNonceAndSalt);
}
/**
* This function is used to decrypt the text with the key
* @param text: encrypted text, it should be base64.
* @param password: the password, it can be any string.
* @returns: the decrypted text.
*/
export function decrypt(text: string, password: string) {
// Decode the base64.
const ciphertextAndNonceAndSalt = base64ToBytes(text);
// Create buffers of salt nonce and ciphertext.
// [salt(PBKDF2_SALT_SIZE bytes)][nonce(ALGORITHM_NONCE_SIZE bytes)][ciphertext(? bytes)]
const salt = ciphertextAndNonceAndSalt.slice(0, PBKDF2_SALT_SIZE);
const nonce = ciphertextAndNonceAndSalt.slice(PBKDF2_SALT_SIZE, PBKDF2_SALT_SIZE + ALGORITHM_NONCE_SIZE);
const ciphertext = ciphertextAndNonceAndSalt.slice(PBKDF2_SALT_SIZE + ALGORITHM_NONCE_SIZE, ciphertextAndNonceAndSalt.length);
// Derive the key using PBKDF2.
const key = pbkdf2(sha256, password, salt, pbkdf2Opt);
// Decrypt the ciphertext.
const decrypted = gcm(key, nonce).decrypt(ciphertext);
// Return result.
return bytesToUtf8(decrypted);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment