Skip to content

Instantly share code, notes, and snippets.

@Elanza-48
Created September 10, 2023 16:57
Show Gist options
  • Select an option

  • Save Elanza-48/7328b89a965ab9b871728b16b66b2522 to your computer and use it in GitHub Desktop.

Select an option

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

Elanza-48 commented Sep 10, 2023

Copy link
Copy Markdown
Author

๐Ÿ“Œ 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