Skip to content

Instantly share code, notes, and snippets.

@Elanza-48
Created September 10, 2023 16:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Elanza-48/7328b89a965ab9b871728b16b66b2522 to your computer and use it in GitHub Desktop.
Save Elanza-48/7328b89a965ab9b871728b16b66b2522 to your computer and use it in GitHub Desktop.
Web Crypto Lib for message (object) encryption & decryption using string key using AES.
'use strict';
class CryptLib {
constructor() {}
async arrayBufferToHexString(buffer) {
if (!(buffer instanceof ArrayBuffer)) throw "not an ArrayBuffer !";
let view = new DataView(buffer);
let hexString = '';
for (let i = 0; i < view.byteLength; i++) {
const hexByte = view.getUint8(i).toString(16).padStart(2, '0');
hexString += hexByte;
}
return hexString;
}
async hexStringToArrayBuffer(hexString) {
if(typeof hexString !== 'string') throw "not a string !";
let buffer = new ArrayBuffer(hexString.length / 2);
let view = new DataView(buffer);
for (let i = 0; i < hexString.length; i += 2) {
const byte = parseInt(hexString.substr(i, 2), 16);
view.setUint8(i / 2, byte);
}
return buffer;
}
async getHashSha256String(str) {
if (!str) {
throw 'needs key';
}
let digest = '';
try {
const buffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(str));
digest = await this.arrayBufferToHexString(buffer);
} catch (e) {
console.error(e);
}
return digest;
}
async getHashSha512String(str) {
if (!str) {
throw 'needs key';
}
let digest = '';
try {
const buffer = await crypto.subtle.digest("SHA-512", new TextEncoder().encode(str));
digest = await this.arrayBufferToHexString(buffer);
} catch (e) {
console.error(e);
}
return digest;
}
async derivePBKDF2(keyString){
let key = null;
try{
key = await window.crypto.subtle.importKey(
"raw",
new TextEncoder().encode(keyString),
{"name": "PBKDF2"},
false,
["deriveKey"],
);
} catch (e){
console.error(e);
}
return key;
}
async deriveAESCryptoKeyFromPBKDF2(importedKey, salt){
let key = null;
try{
key = await window.crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt: new TextEncoder().encode(salt),
iterations: 120000,
hash: "SHA-512"
},
importedKey,
{
name: "AES-GCM",
length: 128
},
false,
["encrypt", "decrypt"]
);
} catch (e){
console.error(e);
}
return key;
}
async encryptPlainTextWithRandomIV(plainText, keyString) {
const enc = new TextEncoder();
const encoded = enc.encode(plainText);
// nonce length is 64;
const nonce = await this.getHashSha256String(keyString);
let key = await this.derivePBKDF2(keyString);
key = await this.deriveAESCryptoKeyFromPBKDF2(key, nonce);
let cipher = "";
try {
let buffer = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
length: 128,
iv: new TextEncoder().encode(nonce),
},
key,
encoded,
);
cipher = await this.arrayBufferToHexString(buffer);
} catch (e){
console.error(e);
}
return cipher;
}
async decryptCipherTextWithRandomIV(cipherText, keyString) {
const buffer = await this.hexStringToArrayBuffer(cipherText);
const nonce = await this.getHashSha256String(keyString);
let key = await this.derivePBKDF2(keyString);
key = await this.deriveAESCryptoKeyFromPBKDF2(key, nonce);
let data = null;
try {
const cipher = await window.crypto.subtle.decrypt(
{
name: "AES-GCM",
length: 128,
iv: new TextEncoder().encode(nonce),
},
key,
buffer,
);
const dec = new TextDecoder();
data = dec.decode(cipher);
} catch (e){
console.error(e);
}
return data;
}
}
const cryptLib = new CryptLib();
export default cryptLib;
@Elanza-48
Copy link
Author

Elanza-48 commented Sep 10, 2023

📌 Anyone can directly test this code in browser console.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment