Last active
February 24, 2022 18:26
-
-
Save janek26/f66e914f507b3b41a3ce2f134ad7ef23 to your computer and use it in GitHub Desktop.
Simple aes and sha256 for browsers using window.crypto
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// exports: | |
// - supportsCrypto() : bool | |
// - randomString(length : int) : string | |
// - aesEncrypt(value : string, password : string) : string | |
// - aesDecrypt(encryptedMsg : string, password : string) : string | |
// - sha256(value : string) : string | |
// if you use this code in Node import crypto and TextEncoder | |
import WebCrypto from 'node-webcrypto-ossl' | |
import { TextEncoder, TextDecoder } from 'util' | |
const crypto = new WebCrypto() | |
// In Browser use window.crypto instead | |
async function genEncryptionKey(password, salt, mode, length) { | |
const derived = { name: mode, length: length } | |
const encoded = new TextEncoder().encode(password) | |
const key = await crypto.subtle.importKey( | |
'raw', | |
encoded, | |
{ name: 'PBKDF2' }, | |
false, | |
['deriveKey'], | |
) | |
return crypto.subtle.deriveKey( | |
{ | |
name: 'PBKDF2', | |
hash: 'SHA-256', | |
salt: new TextEncoder().encode(salt), | |
iterations: 1000, | |
}, | |
key, | |
derived, | |
false, | |
['encrypt', 'decrypt'], | |
) | |
} | |
function bufferToHex(buffer) { | |
return Array.from(new Uint8Array(buffer)) | |
.map(b => b.toString(16).padStart(2, '0')) | |
.join('') | |
} | |
export const isBrowser = new Function( | |
'try {return this===window;}catch(e){ return false;}', | |
) | |
export function supportsCrypto() { | |
return isBrowser() | |
? !!(window.crypto && crypto.subtle && window.TextEncoder) | |
: true | |
} | |
export function randomString(length) { | |
const charset = | |
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' | |
let i | |
let result = '' | |
const isOpera = | |
isBrowser() && | |
Object.prototype.toString.call(window.opera) == '[object Opera]' | |
if (crypto && crypto.getRandomValues) { | |
let values = new Uint32Array(length) | |
crypto.getRandomValues(values) | |
for (i = 0; i < length; i++) { | |
result += charset[values[i] % charset.length] | |
} | |
return result | |
} else if (isOpera) { | |
//Opera's Math.random is secure, see http://lists.w3.org/Archives/Public/public-webcrypto/2013Jan/0063.html | |
for (i = 0; i < length; i++) { | |
result += charset[Math.floor(Math.random() * charset.length)] | |
} | |
return result | |
} else | |
throw new Error( | |
"Your browser sucks and can't generate secure random numbers", | |
) | |
} | |
export async function aesEncrypt(value, password) { | |
const iv = crypto.getRandomValues(new Uint8Array(12)) | |
const salt = randomString(16) | |
const key = await genEncryptionKey(password, salt, 'AES-GCM', 256) | |
const encoded = new TextEncoder().encode(value) | |
const cipherText = await crypto.subtle.encrypt( | |
{ | |
name: 'AES-GCM', | |
length: 256, | |
iv, | |
}, | |
key, | |
encoded, | |
) | |
return bufferToHex(cipherText) + ':' + bufferToHex(iv.buffer) + ':' + salt | |
} | |
export async function aesDecrypt(encString, password) { | |
function toUint8Array(hexString) { | |
const result = [] | |
while (hexString.length >= 2) { | |
result.push(parseInt(hexString.substring(0, 2), 16)) | |
hexString = hexString.substring(2, hexString.length) | |
} | |
return new Uint8Array(result) | |
} | |
const [cipherTextHex, ivHex, salt] = encString.split(':') | |
const cipherText = toUint8Array(cipherTextHex).buffer | |
const iv = toUint8Array(ivHex) | |
var key = await genEncryptionKey(password, salt, 'AES-GCM', 256) | |
var decrypted = await crypto.subtle.decrypt( | |
{ | |
name: 'AES-GCM', | |
length: 256, | |
iv, | |
}, | |
key, | |
cipherText, | |
) | |
return new TextDecoder().decode(decrypted) | |
} | |
export async function sha256(str) { | |
return bufferToHex( | |
await crypto.subtle.digest( | |
{ name: 'SHA-256' }, | |
new TextEncoder().encode(str), | |
), | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment