commlink
Version:
JavaScript Encrypted Communications
1 lines • 6.01 kB
JavaScript
;function Commlink(crypto){let commlink={};"undefined"!=typeof window&&(crypto=window.crypto||crypto);const toHex=commlink.toHex=(byteArray=>Array.from(new Uint8Array(byteArray)).map(val=>("0"+val.toString(16)).slice(-2)).join("")),encode=(commlink.fromHex=(str=>{let result=new Uint8Array(str.match(/.{0,2}/g).map(val=>parseInt(val,16)));return result.slice(0,result.length-1)}),commlink.encode=(byteArray=>btoa(Array.from(new Uint8Array(byteArray)).map(val=>String.fromCharCode(val)).join("")).replace(/\+/g,"-").replace(/\//g,"_").replace(/\=/g,""))),decode=commlink.decode=(str=>new Uint8Array(atob(str.replace(/\_/g,"/").replace(/\-/g,"+")).split("").map(val=>val.charCodeAt(0)))),fromText=commlink.fromText=(string=>new Uint8Array(string.split("").map(val=>val.charCodeAt(0)))),toText=commlink.toText=(byteArray=>Array.from(new Uint8Array(byteArray)).map(val=>String.fromCharCode(val)).join("")),combine=commlink.combine=((bitsA=[],bitsB=[])=>{let A=bitsA,B=bitsB;"string"==typeof bitsA&&(A=decode(bitsA)),"string"==typeof bitsB&&(B=decode(bitsB));let a=new Uint8Array(A),b=new Uint8Array(B),c=new Uint8Array(a.length+b.length);return c.set(a),c.set(b,a.length),c}),random=commlink.random=(size=>crypto.getRandomValues(new Uint8Array(size))),hmacSign=(commlink.createECDH=(async(curve="P-256")=>{let DH=await crypto.subtle.generateKey({name:"ECDH",namedCurve:curve},!0,["deriveBits"]),pub=await crypto.subtle.exportKey("raw",DH.publicKey),key=encode(await crypto.subtle.exportKey("pkcs8",DH.privateKey));return{pub:encode(pub),key:key}}),commlink.createECDSA=(async(curve="P-256")=>{let user=await crypto.subtle.generateKey({name:"ECDSA",namedCurve:curve},!0,["sign","verify"]),pub=await crypto.subtle.exportKey("raw",user.publicKey),key=encode(await crypto.subtle.exportKey("pkcs8",user.privateKey));return{pub:encode(pub),key:key}}),commlink.ecdsaSign=commlink.sign=(async(key,msg,curve="P-256",hashAlg="SHA-256")=>{let message=msg.toString(),signKey=await crypto.subtle.importKey("pkcs8",decode(key),{name:"ECDSA",namedCurve:curve},!1,["sign"]),sig=await crypto.subtle.sign({name:"ECDSA",hash:hashAlg},signKey,fromText(message));return encode(sig)}),commlink.ecdsaVerify=commlink.verify=(async(pub,sig,msg,curve="P-256",hashAlg="SHA-256")=>{let message=msg.toString(),verifyKey=await crypto.subtle.importKey("raw",decode(pub),{name:"ECDSA",namedCurve:curve},!1,["verify"]);return await crypto.subtle.verify({name:"ECDSA",hash:hashAlg},verifyKey,decode(sig),fromText(message))}),commlink.hmacSign=(async(bits,msg,hashAlg="SHA-256")=>{let message=msg.toString(),hmacKey=await crypto.subtle.importKey("raw",bits,{name:"HMAC",hash:hashAlg},!1,["sign"]),sig=await crypto.subtle.sign({name:"HMAC",hash:hashAlg},hmacKey,fromText(message));return encode(sig)})),pbkdf2=(commlink.hmacVerify=(async(bits,sig,msg,hashAlg="SHA-256")=>{let message=msg.toString(),verifyKey=await crypto.subtle.importKey("raw",bits,{name:"HMAC",hash:hashAlg},!1,["verify"]);return await crypto.subtle.verify({name:"HMAC",hash:hashAlg},verifyKey,decode(sig),fromText(message))}),commlink.digest=(async(bits,hashAlg="SHA-256")=>{let digest=await crypto.subtle.digest({name:hashAlg},bits);return toHex(digest)}),commlink.pbkdf2=(async(bits,salt,iterations=1,size=256,hashAlg="SHA-256")=>{let key=await crypto.subtle.importKey("raw",bits,{name:"PBKDF2"},!1,["deriveBits"]),result=await crypto.subtle.deriveBits({name:"PBKDF2",salt:salt,iterations:iterations,hash:hashAlg},key,size);return encode(result)})),encrypt=(commlink.hkdf=(async(bits,salt,info,size,hashAlg="SHA-256")=>{let ikm=bits,len=size,hashSize=256;if("SHA-512"===hashAlg.toLocaleUpperCase()&&(hashSize=512),len>255*hashSize)throw"Error: Size exceeds maximum output length for selected hash.";if(len<8)throw"Error: Size cannot be smaller 8 bits.";if(len/8!==parseInt(len/8))throw"Error: Size must be a multiple of 8 bits.";let PRK=await hmacSign(salt,toText(ikm),hashAlg),result=new Uint8Array([]),T=new Uint8Array([]),rounds=Math.ceil(size/hashSize);for(let i=0;i<rounds;i++){let num=toText(new Uint8Array([i+1])),msg=toText(T)+toText(info)+num;T=decode(await hmacSign(decode(PRK),msg,hashAlg)),result=combine(result,T)}return await encode(result.slice(0,len/8))}),commlink.ecdh=(async(key,pub,curve="P-256",size=256)=>{let pubKey=await crypto.subtle.importKey("raw",decode(pub),{name:"ECDH",namedCurve:curve},!0,[]),privateKey=await crypto.subtle.importKey("pkcs8",decode(key),{name:"ECDH",namedCurve:curve},!0,["deriveBits"]),shared=await crypto.subtle.deriveBits({name:"ECDH",public:pubKey},privateKey,size);return encode(shared)}),commlink.encrypt=(async(plaintext,bits,AD=null)=>{let key=await crypto.subtle.importKey("raw",bits,{name:"AES-GCM"},!1,["encrypt"]),iv=random(12),msg=fromText(plaintext),cipher=await crypto.subtle.encrypt({name:"AES-GCM",iv:iv,additionalData:AD||fromText("")},key,msg);return encode(iv)+"."+encode(cipher)})),decrypt=commlink.decrypt=(async(ciphertext="",bits,AD=null)=>{let key=await crypto.subtle.importKey("raw",bits,{name:"AES-GCM"},!1,["decrypt"]),iv=decode(ciphertext.split(".")[0]),cipher=decode(ciphertext.split(".")[1]),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)});commlink.passwordEncrypt=(async(message,password="",iterations=1e5)=>{let salt=random(32),keyBits=await pbkdf2(fromText(password),salt,iterations,256),encrypted=await encrypt(message,decode(keyBits));return encode(fromText(iterations.toString()))+"."+encode(salt)+"."+encrypted}),commlink.passwordDecrypt=(async(ciphertext="",password="")=>{let iterations=toText(decode(ciphertext.split(".")[0])),salt=ciphertext.split(".")[1],keyBits=await pbkdf2(fromText(password),decode(salt),iterations,256),encrypted=ciphertext.split(".").slice(2).join(".");return await decrypt(encrypted,decode(keyBits))});return commlink}"undefined"!=typeof module&&module&&module.exports&&(module.exports=Commlink);