UNPKG

@starbase/cryptic

Version:
255 lines (216 loc) 7.62 kB
'use strict'; function Cryptic(webCrypto, encoder, decoder) { let crypto = webCrypto; if (typeof window !== 'undefined') { crypto = window.crypto || webCrypto; } let TextEncoder = encoder; let TextDecoder = decoder; if (typeof window !== 'undefined') { TextEncoder = window.TextEncoder; TextDecoder = window.TextDecoder; } const toHex = (byteArray) => { return Array.from(new Uint8Array(byteArray)).map(val => { return ('0' + val.toString(16)).slice(-2); }).join(''); }; const fromHex = (str) => { let result = new Uint8Array(str.match(/.{0,2}/g).map(val => { return parseInt(val, 16); })); return result.slice(0, result.length - 1); }; const encode = (byteArray) => { return btoa(Array.from(new Uint8Array(byteArray)).map(val => { return String.fromCharCode(val); }).join('')).replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, ''); }; const decode = (str) => { return new Uint8Array(atob(str.replace(/\_/g, '/').replace(/\-/g, '+')).split('').map(val => { return val.charCodeAt(0); })); }; const fromText = (string) => { return new TextEncoder().encode(string); }; const toText = (byteArray) => { return new TextDecoder().decode(byteArray); }; const combine = (bitsA = [], bitsB = []) => { let A = bitsA; let B = bitsB; if (typeof bitsA === 'string') { A = decode(bitsA); } if (typeof bitsB === 'string') { B = decode(bitsB); } let a = new Uint8Array(A); let b = new Uint8Array(B); let c = new Uint8Array(a.length + b.length); c.set(a); c.set(b, a.length); return c; }; const random = (size) => { return crypto.getRandomValues(new Uint8Array(size)); }; const createECDH = async (curve = "P-256") => { let DH = await crypto.subtle.generateKey({ "name": "ECDH", "namedCurve": curve }, true, ['deriveBits']); let pub = await crypto.subtle.exportKey('spki', DH.publicKey); let key = await crypto.subtle.exportKey('pkcs8', DH.privateKey); return { "pub": encode(pub), "key": encode(key) }; }; const createECDSA = async (curve = "P-256") => { let user = await crypto.subtle.generateKey({ "name": "ECDSA", "namedCurve": curve }, true, ['sign', 'verify']); let pub = await crypto.subtle.exportKey('spki', user.publicKey); let key = await crypto.subtle.exportKey('pkcs8', user.privateKey); return { "pub": encode(pub), "key": encode(key) }; }; const ecdsaSign = async (key, msg, curve = "P-256", hashAlg = "SHA-256") => { let message = msg.toString(); let signKey = await crypto.subtle.importKey('pkcs8', decode(key), { "name": "ECDSA", "namedCurve": curve }, false, ['sign']); let sig = await crypto.subtle.sign({ "name": "ECDSA", "hash": hashAlg }, signKey, fromText(message)); return encode(sig); }; const ecdsaVerify = async (pub, sig, msg, curve = "P-256", hashAlg = "SHA-256") => { let message = msg.toString(); let verifyKey = await crypto.subtle.importKey('spki', decode(pub), { "name": "ECDSA", "namedCurve": curve }, false, ['verify']); let verified = await crypto.subtle.verify({ "name": "ECDSA", "hash": hashAlg }, verifyKey, decode(sig), fromText(message)); return verified; }; const hmacSign = async (bits, msg, hashAlg="SHA-256") => { let message = msg.toString(); let hmacKey = await crypto.subtle.importKey('raw', bits, { "name": "HMAC", "hash": hashAlg, }, false, ['sign']); let sig = await crypto.subtle.sign({ "name": "HMAC", "hash": hashAlg }, hmacKey, fromText(message)); return encode(sig); }; const hmacVerify = async (bits, sig, msg, hashAlg="SHA-256") => { let message = msg.toString(); let verifyKey = await crypto.subtle.importKey('raw', bits, { "name": "HMAC", "hash": hashAlg, }, false, ['verify']); let verified = await crypto.subtle.verify({ "name": "HMAC", "hash": hashAlg }, verifyKey, decode(sig), fromText(message)); return verified; }; const digest = async (bits, hashAlg = "SHA-256") => { let result = await crypto.subtle.digest({ "name": hashAlg }, bits); return toHex(result); }; const pbkdf2 = async (bits, salt, iterations = 1, size = 256, hashAlg = "SHA-256") => { let key = await crypto.subtle.importKey('raw', bits, { "name": "PBKDF2" }, false, ['deriveBits']); let result = await crypto.subtle.deriveBits({ "name": "PBKDF2", "salt": salt, "iterations": iterations, "hash": hashAlg }, key, size); return encode(result); }; const kdf = async (bits, salt, info, size, hashAlg="SHA-256") => { let key = await hmacSign(bits, toText(info)); let hash = await pbkdf2(decode(key), salt, 1, size, hashAlg); return hash; }; const ecdh = async (key, pub, curve = "P-256", size = 256) => { let pubKey = await crypto.subtle.importKey('spki', decode(pub), { "name": "ECDH", "namedCurve": curve }, true, []); let privateKey = await crypto.subtle.importKey('pkcs8', decode(key), { "name": "ECDH", "namedCurve": curve }, true, ['deriveBits']); let shared = await crypto.subtle.deriveBits({ "name": "ECDH", "public": pubKey }, privateKey, size); let bits = encode(shared); return bits; }; const encrypt = async (plaintext, bits, AD = null) => { let key = await crypto.subtle.importKey('raw', bits, { "name": "AES-GCM" }, false, ['encrypt']); let iv = random(12); let msg = fromText(plaintext); let cipher = await crypto.subtle.encrypt({ "name": "AES-GCM", "iv": iv, "additionalData": AD || fromText('') }, key, msg); return encode(iv) + '.' + encode(cipher); }; const decrypt = async (ciphertext = "", bits, AD = null) => { let key = await crypto.subtle.importKey('raw', bits, { "name": "AES-GCM" }, false, ['decrypt']); let iv = decode(ciphertext.split('.')[0]); let cipher = decode(ciphertext.split('.')[1]); let decrypted = await crypto.subtle.decrypt({ "name": "AES-GCM", "iv": iv, "additionalData": AD || fromText('') }, key, cipher).catch(err => { throw({"message":"Failed to decrypt message.", "error":err}); }); return toText(decrypted); }; const passwordEncrypt = async (message, password = "", iterations = 100000) => { let salt = random(32); let keyBits = await pbkdf2(fromText(password), salt, iterations, 256); let encrypted = await encrypt(message, decode(keyBits)); return encode(fromText(iterations.toString())) + '.' + encode(salt) + '.' + encrypted; }; const passwordDecrypt = async (ciphertext = "", password = "") => { let iterations = toText(decode(ciphertext.split('.')[0])); let salt = ciphertext.split('.')[1]; let keyBits = await pbkdf2(fromText(password), decode(salt), iterations, 256); let encrypted = ciphertext.split('.').slice(2).join('.'); let decrypted = await decrypt(encrypted, decode(keyBits)); return decrypted; }; return {combine, createECDH, createECDSA, decode, encode, decrypt, digest, ecdh, ecdsaSign, ecdsaVerify, encode, encrypt, fromHex, fromText, hmacSign, hmacVerify, kdf, passwordDecrypt, passwordEncrypt, pbkdf2, random, "sign":ecdsaSign, toHex, toText, "verify":ecdsaVerify}; } if (typeof module !== 'undefined' && module && module.exports) { module.exports = Cryptic; }