UNPKG

@mdaemon/rsa-message

Version:

RSA message encryption, signing, decryption, and verification using webcrypto or node crypto

13 lines (12 loc) 17.8 kB
(function(p,w){typeof exports=="object"&&typeof module!="undefined"?module.exports=w():typeof define=="function"&&define.amd?define(w):(p=typeof globalThis!="undefined"?globalThis:p||self,p.RSAMessage=w())})(this,(function(){"use strict";function p(o,e,r,t){function i(s){return s instanceof r?s:new r(function(y){y(s)})}return new(r||(r=Promise))(function(s,y){function d(n){try{l(t.next(n))}catch(f){y(f)}}function c(n){try{l(t.throw(n))}catch(f){y(f)}}function l(n){n.done?s(n.value):i(n.value).then(d,c)}l((t=t.apply(o,e||[])).next())})}typeof SuppressedError=="function"&&SuppressedError;var w="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";const F=typeof atob=="function"&&atob;function $(o){return Buffer.from(o,"base64").toString("binary")}var S;const D=/[\t\n\r\x20\x0C]+/g,P=String.fromCharCode;function N(o){if(!o)return o;o=String(o).replace(D,"");var e,r,t,i,s,y,d,c,l=o.length,n=0,f=0,g="",h=[];if(S==null){S={};for(var E=0,v=w.length;E<v;E++)S[w.charAt(E)]=E}do i=S[o.charAt(n++)],s=S[o.charAt(n++)],y=S[o.charAt(n++)],d=S[o.charAt(n++)],c=i<<18|s<<12|y<<6|d,e=c>>16&255,r=c>>8&255,t=c&255,y==64?h[f++]=P(e):d==64?h[f++]=P(e,r):h[f++]=P(e,r,t);while(n<l);return g=h.join(""),g.replace(/\0+$/,"")}const O=F||typeof Buffer=="function"&&$||N,R=typeof btoa=="function"&&btoa;function G(o){return Buffer.from(o,"binary").toString("base64")}function H(o){if(!o)return o;var e,r,t,i,s,y,d,c,l=0,n=0,f="",g=[];do e=o.charCodeAt(l++),r=o.charCodeAt(l++),t=o.charCodeAt(l++),c=e<<16|r<<8|t,i=c>>18&63,s=c>>12&63,y=c>>6&63,d=c&63,g[n++]=w.charAt(i)+w.charAt(s)+w.charAt(y)+w.charAt(d);while(l<o.length);f=g.join("");var h=o.length%3;return(h?f.slice(0,h-3):f)+"===".slice(h||3)}const x=R||typeof Buffer=="function"&&G||H;function J(o){return unescape(encodeURI(o))}function B(o){return decodeURIComponent(escape(o))}/** * Base64 string encoding and decoding utility. * * play @ https://duzun.me/playground/encode#base64Encode=Test%20String%20 * * original of _btoa and _atob by: Tyler Akins (http://rumkin.com) * * * @license MIT * @version 2.2.0 * @author Dumitru Uzun (DUzun.Me) */function j(o){let e=o;return e&&(e=O(String(e).replace(/_/g,"/").replace(/-/g,"+"))),e}function M(o){return o&&x(J(o))}function m(o,e){let r=j(o);if(r)try{r=B(r)}catch(t){}return r}const a=()=>globalThis.crypto,k=()=>new TextEncoder,A=()=>new TextDecoder,C=o=>{const e=new Uint8Array(o);let r="";for(const t of e)r+=String.fromCharCode(t);return x(r)},U=o=>{const e=m(o),r=new ArrayBuffer(e.length),t=new Uint8Array(r);for(let i=0;i<e.length;i++)t[i]=e.charCodeAt(i);return r};class u extends Error{constructor(e,r,t){super(e),this.operation=r,this.originalError=t,this.name="CryptoOperationError"}}class b extends Error{constructor(e,r,t){super(e),this.keyType=r,this.originalError=t,this.name="KeyImportError"}}class T{generateAndSetMasterAESKey(){return p(this,void 0,void 0,function*(){const e=yield this.generateAESKey(),r=yield a().subtle.exportKey("jwk",e),t=JSON.stringify(r),i=yield this.encryptMessage(t,"self"),s=this.exportEncryptedMessage(i);return this.encryptedMasterAESKey=s,this.masterAESKeyEncryptor="self",s})}setEncryptedMasterAESKey(e,r="self"){this.encryptedMasterAESKey=e,this.masterAESKeyEncryptor=r}getDecryptedMasterAESKey(){return p(this,void 0,void 0,function*(){if(!this.encryptedMasterAESKey)throw new Error("No master AES key set");const e=this.importEncryptedMessage(this.encryptedMasterAESKey),{iv:r,encryptedMessage:t,encryptedAESKey:i,signature:s}=e;if(!i)throw new Error("Master AES key must have been encrypted with RSA");let y;try{y=yield this.importPrivateKey(this.privateKey,"decrypt")}catch(n){throw new Error(`Failed to import private key: ${n}`)}let d;try{d=yield a().subtle.decrypt({name:"RSA-OAEP"},y,new Uint8Array(i))}catch(n){throw new u(`Failed to decrypt AES key: ${n instanceof Error?n.message:"Unknown error"}`,"decrypt",n instanceof Error?n:void 0)}let c;try{c=yield a().subtle.importKey("raw",d,"AES-GCM",!0,["decrypt"])}catch(n){throw new b(`Failed to import AES key: ${n instanceof Error?n.message:"Unknown error"}`,"private",n instanceof Error?n:void 0)}let l;try{l=yield a().subtle.decrypt({name:"AES-GCM",iv:r},c,new Uint8Array(t))}catch(n){throw new u(`Failed to decrypt message: ${n instanceof Error?n.message:"Unknown error"}`,"decrypt",n instanceof Error?n:void 0)}try{const n=A().decode(l);if(!(yield this.verifySignature(s,n,this.masterAESKeyEncryptor||"self")))throw new Error("Signature verification failed");const f=JSON.parse(n);return yield a().subtle.importKey("jwk",f,{name:"AES-GCM"},!0,["encrypt","decrypt"])}catch(n){throw new u(`Failed to verify signature: ${n instanceof Error?n.message:"Unknown error"}`,"verify",n instanceof Error?n:void 0)}})}exportMasterAESKeyForUser(e){return p(this,void 0,void 0,function*(){if(!this.encryptedMasterAESKey)throw new Error("No master AES key set");const r=yield this.getDecryptedMasterAESKey(),t=yield a().subtle.exportKey("jwk",r),i=JSON.stringify(t),s=yield this.encryptMessage(i,e);return this.exportEncryptedMessage(s)})}setMasterAESKeyFromEncrypted(e){return p(this,arguments,void 0,function*(r,t="self"){this.encryptedMasterAESKey=r,this.masterAESKeyEncryptor=t;const i=yield this.getDecryptedMasterAESKey();if(t!=="self"){const s=yield a().subtle.exportKey("jwk",i),y=JSON.stringify(s);this.publicKeys.has("self")||this.setPublicKey("self",this.publicKey,this.verifyKey);const d=yield this.encryptMessage(y,"self"),c=this.exportEncryptedMessage(d);this.encryptedMasterAESKey=c,this.masterAESKeyEncryptor="self"}})}encryptWithMasterAESKey(e){return p(this,void 0,void 0,function*(){if(!this.encryptedMasterAESKey)throw new Error("No master AES key set");const r=yield this.getDecryptedMasterAESKey(),t=k().encode(e),i=a().getRandomValues(new Uint8Array(12)),s=yield a().subtle.encrypt({name:"AES-GCM",iv:i},r,t),y=yield this.signMessage(e);return{iv:i,encryptedMessage:s,signature:y}})}decryptWithMasterAESKey(e,r){return p(this,void 0,void 0,function*(){if(!this.encryptedMasterAESKey)throw new Error("No master AES key set");const t=yield this.getDecryptedMasterAESKey(),{iv:i,encryptedMessage:s,signature:y}=e;let d;try{d=yield a().subtle.decrypt({name:"AES-GCM",iv:i},t,new Uint8Array(s))}catch(c){throw new u(`Failed to decrypt with master AES key: ${c instanceof Error?c.message:"Unknown error"}`,"decrypt",c instanceof Error?c:void 0)}try{const c=A().decode(d);if(!(yield this.verifySignature(y,c,r)))throw new Error("Signature verification failed");return c}catch(c){throw new u(`Failed to verify signature: ${c instanceof Error?c.message:"Unknown error"}`,"verify",c instanceof Error?c:void 0)}})}decryptWithMasterAESKeyUnsafe(e,r){return p(this,void 0,void 0,function*(){if(!this.encryptedMasterAESKey)throw new Error("No master AES key set");const t=yield this.getDecryptedMasterAESKey(),{iv:i,encryptedMessage:s,signature:y}=e;let d;try{d=yield a().subtle.decrypt({name:"AES-GCM",iv:i},t,new Uint8Array(s))}catch(n){throw new u(`Failed to decrypt with master AES key: ${n instanceof Error?n.message:"Unknown error"}`,"decrypt",n instanceof Error?n:void 0)}const c=A().decode(d);let l=!1;try{l=yield this.verifySignature(y,c,r)}catch(n){l=!1}return{message:c,verified:l}})}constructor(){this.publicKeys=new Map,this.verifyKeys=new Map,this.ecdhPrivateKey=null,this.ecdhPublicKeys=new Map,this.sharedKeys=new Map,this.encryptedMasterAESKey=null,this.masterAESKeyEncryptor=null,this.genKeyPair=(...e)=>p(this,[...e],void 0,function*(r="decrypt"){const t=r==="decrypt"?["encrypt","decrypt"]:["sign","verify"],i=yield a().subtle.generateKey({name:r==="decrypt"?"RSA-OAEP":"RSA-PSS",modulusLength:2048,publicExponent:new Uint8Array([1,0,1]),hash:"SHA-256"},!0,t),s=yield a().subtle.exportKey("jwk",i.publicKey),y=yield a().subtle.exportKey("jwk",i.privateKey);return{publicKey:M(JSON.stringify(s)),privateKey:M(JSON.stringify(y))}}),this.importPrivateKey=(e,r)=>p(this,void 0,void 0,function*(){const t={name:r==="decrypt"?"RSA-OAEP":"RSA-PSS",hash:"SHA-256"};r==="sign"&&(t.saltLength=32);try{const i=JSON.parse(m(e));return yield a().subtle.importKey("jwk",i,t,!1,[r])}catch(i){throw new b(`Failed to import private key: ${i instanceof Error?i.message:"Unknown error"}`,"private",i instanceof Error?i:void 0)}}),this.importPublicKey=(e,r)=>p(this,void 0,void 0,function*(){const t={name:r==="encrypt"?"RSA-OAEP":"RSA-PSS",hash:"SHA-256"};r==="verify"&&(t.saltLength=32);try{const i=JSON.parse(m(e));return yield a().subtle.importKey("jwk",i,t,!1,[r])}catch(i){throw new b(`Failed to import public key: ${i instanceof Error?i.message:"Unknown error"}`,"public",i instanceof Error?i:void 0)}}),this.encryptMessage=(e,r,...t)=>p(this,[e,r,...t],void 0,function*(i,s,y=!1){const d=this.publicKeys.get(s);if(!d)throw new Error("Public key not found for user");const c=k().encode(i);let l,n;if(y&&this.encryptedMasterAESKey)l=yield this.getDecryptedMasterAESKey(),n=void 0;else{l=yield this.generateAESKey();const E=yield this.importPublicKey(d,"encrypt"),v=yield a().subtle.exportKey("raw",l);n=yield a().subtle.encrypt({name:"RSA-OAEP"},E,v)}const f=a().getRandomValues(new Uint8Array(12)),g=yield a().subtle.encrypt({name:"AES-GCM",iv:f},l,c),h=yield this.signMessage(i);return{iv:f,encryptedMessage:g,encryptedAESKey:n,signature:h}}),this.decryptMessage=(e,r,...t)=>p(this,[e,r,...t],void 0,function*(i,s,y=!1){const{iv:d,encryptedMessage:c,encryptedAESKey:l,signature:n}=i;let f;if(y&&this.encryptedMasterAESKey)f=yield this.getDecryptedMasterAESKey();else if(l){let h;try{h=yield this.importPrivateKey(this.privateKey,"decrypt")}catch(v){throw new Error(`Failed to import private key: ${v}`)}let E="";try{E=yield a().subtle.decrypt({name:"RSA-OAEP"},h,new Uint8Array(l))}catch(v){throw new u(`Failed to decrypt AES key: ${v instanceof Error?v.message:"Unknown error"}`,"decrypt",v instanceof Error?v:void 0)}try{f=yield a().subtle.importKey("raw",E,"AES-GCM",!0,["decrypt"])}catch(v){throw new b(`Failed to import AES key: ${v instanceof Error?v.message:"Unknown error"}`,"private",v instanceof Error?v:void 0)}}else throw new Error("No AES key available for decryption - either provide encryptedAESKey or use master AES key");let g="";try{g=yield a().subtle.decrypt({name:"AES-GCM",iv:d},f,new Uint8Array(c))}catch(h){throw new u(`Failed to decrypt message: ${h instanceof Error?h.message:"Unknown error"}`,"decrypt",h instanceof Error?h:void 0)}try{const h=A().decode(g);if(!(yield this.verifySignature(n,h,s)))throw new Error("Signature verification failed");return h}catch(h){throw new u(`Failed to verify signature: ${h instanceof Error?h.message:"Unknown error"}`,"verify",h instanceof Error?h:void 0)}}),this.signMessage=e=>p(this,void 0,void 0,function*(){const r=k().encode(e);try{const t=yield this.importPrivateKey(this.signKey,"sign");return yield a().subtle.sign({name:"RSA-PSS",saltLength:32},t,r)}catch(t){throw new u(`Failed to sign message: ${t instanceof Error?t.message:"Unknown error"}`,"sign",t instanceof Error?t:void 0)}}),this.verifySignature=(e,r,t)=>p(this,void 0,void 0,function*(){const i=this.verifyKeys.get(t);if(!i)throw new Error("Public key not found for user");try{const s=yield this.importPublicKey(i,"verify"),y=k().encode(r);return yield a().subtle.verify({name:"RSA-PSS",saltLength:32},s,new Uint8Array(e),y)}catch(s){throw new u(`Failed to verify signature: ${s instanceof Error?s.message:"Unknown error"}`,"verify",s instanceof Error?s:void 0)}}),this.privateKey="",this.publicKey="",this.verifyKey="",this.signKey=""}get publickey(){return this.publicKey}get verifykey(){return this.verifyKey}get privatekey(){return this.privateKey}get signkey(){return this.signKey}generateAESKey(){return p(this,void 0,void 0,function*(){return yield a().subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt","decrypt"])})}init(e,r,t,i){return p(this,void 0,void 0,function*(){if(e&&r&&t&&i)return this.publicKey=e,this.privateKey=r,this.verifyKey=t,this.signKey=i,{publicKey:this.publicKey,verifyKey:this.verifyKey};const s=yield this.genKeyPair(),y=yield this.genKeyPair("sign");return this.publicKey=s.publicKey,this.privateKey=s.privateKey,this.verifyKey=y.publicKey,this.signKey=y.privateKey,{publicKey:s.publicKey,verifyKey:y.publicKey}})}decryptMessageUnsafe(e,r){return p(this,arguments,void 0,function*(t,i,s=!1){const{iv:y,encryptedMessage:d,encryptedAESKey:c,signature:l}=t;let n;if(s&&this.encryptedMasterAESKey)n=yield this.getDecryptedMasterAESKey();else if(c){let E;try{E=yield this.importPrivateKey(this.privateKey,"decrypt")}catch(K){throw new Error(`Failed to import private key: ${K}`)}let v="";try{v=yield a().subtle.decrypt({name:"RSA-OAEP"},E,new Uint8Array(c))}catch(K){throw new u(`Failed to decrypt AES key: ${K instanceof Error?K.message:"Unknown error"}`,"decrypt",K instanceof Error?K:void 0)}try{n=yield a().subtle.importKey("raw",v,"AES-GCM",!0,["decrypt"])}catch(K){throw new b(`Failed to import AES key: ${K instanceof Error?K.message:"Unknown error"}`,"private",K instanceof Error?K:void 0)}}else throw new Error("No AES key available for decryption - either provide encryptedAESKey or use master AES key");let f="";try{f=yield a().subtle.decrypt({name:"AES-GCM",iv:y},n,new Uint8Array(d))}catch(E){throw new u(`Failed to decrypt message: ${E instanceof Error?E.message:"Unknown error"}`,"decrypt",E instanceof Error?E:void 0)}const g=A().decode(f);let h=!1;try{h=yield this.verifySignature(l,g,i)}catch(E){h=!1}return{message:g,verified:h}})}setPublicKey(e,r,t){if(!e||!r)throw new Error("Invalid arguments");this.publicKeys.set(e,r),t&&this.verifyKeys.set(e,t)}setVerifyKey(e,r){if(!e||!r)throw new Error("Invalid arguments");this.verifyKeys.set(e,r)}hasPublicKey(e){return this.publicKeys.has(e)}hasVerifyKey(e){return this.verifyKeys.has(e)}exportEncryptedMessage(e){return M(JSON.stringify({iv:String.fromCharCode(...e.iv),encryptedMessage:C(e.encryptedMessage),encryptedAESKey:e.encryptedAESKey?C(e.encryptedAESKey):void 0,signature:C(e.signature)}))}importEncryptedMessage(e){const r=JSON.parse(m(e));return{iv:new Uint8Array([...r.iv].map(t=>t.charCodeAt(0))),encryptedMessage:U(r.encryptedMessage),encryptedAESKey:r.encryptedAESKey?U(r.encryptedAESKey):void 0,signature:U(r.signature)}}generateECDHKeyPair(){return p(this,void 0,void 0,function*(){try{const e=yield a().subtle.generateKey({name:"ECDH",namedCurve:"P-256"},!0,["deriveBits","deriveKey"]);this.ecdhPrivateKey=e.privateKey;const r=yield a().subtle.exportKey("jwk",e.publicKey);return{publicKey:M(JSON.stringify(r))}}catch(e){throw new u(`Failed to generate ECDH key pair: ${e instanceof Error?e.message:"Unknown error"}`,"encrypt",e instanceof Error?e:void 0)}})}setECDHPublicKey(e,r){return p(this,void 0,void 0,function*(){try{const t=JSON.parse(m(r)),i=yield a().subtle.importKey("jwk",t,{name:"ECDH",namedCurve:"P-256"},!1,[]);this.ecdhPublicKeys.set(e,i)}catch(t){throw new b(`Failed to import ECDH public key: ${t instanceof Error?t.message:"Unknown error"}`,"public",t instanceof Error?t:void 0)}})}deriveSharedKey(e,r){return p(this,void 0,void 0,function*(){if(!this.ecdhPrivateKey)throw new Error("ECDH private key not generated. Call generateECDHKeyPair() first.");const t=this.ecdhPublicKeys.get(e);if(!t)throw new Error(`ECDH public key not found for user: ${e}`);try{const i=r||a().getRandomValues(new Uint8Array(16)),s=yield a().subtle.deriveBits({name:"ECDH",public:t},this.ecdhPrivateKey,256),y=yield a().subtle.importKey("raw",s,"PBKDF2",!1,["deriveKey"]),d=yield a().subtle.deriveKey({name:"PBKDF2",salt:i,iterations:1e5,hash:"SHA-256"},y,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"]);return this.sharedKeys.set(e,d),i}catch(i){throw new u(`Failed to derive shared key: ${i instanceof Error?i.message:"Unknown error"}`,"encrypt",i instanceof Error?i:void 0)}})}encryptWithSharedKey(e,r){return p(this,void 0,void 0,function*(){const t=this.sharedKeys.get(r);if(!t)throw new Error(`Shared key not found for user: ${r}. Call deriveSharedKey() first.`);try{const i=k().encode(e),s=a().getRandomValues(new Uint8Array(12)),y=a().getRandomValues(new Uint8Array(16)),d=yield a().subtle.encrypt({name:"AES-GCM",iv:s},t,i);return{salt:y,encryptedMessage:d,iv:s}}catch(i){throw new u(`Failed to encrypt with shared key: ${i instanceof Error?i.message:"Unknown error"}`,"encrypt",i instanceof Error?i:void 0)}})}decryptWithSharedKey(e,r){return p(this,void 0,void 0,function*(){const t=this.sharedKeys.get(r);if(!t)throw new Error(`Shared key not found for user: ${r}. Call deriveSharedKey() first.`);try{const{iv:i,encryptedMessage:s}=e,y=yield a().subtle.decrypt({name:"AES-GCM",iv:i},t,new Uint8Array(s));return A().decode(y)}catch(i){throw new u(`Failed to decrypt with shared key: ${i instanceof Error?i.message:"Unknown error"}`,"decrypt",i instanceof Error?i:void 0)}})}exportSharedKeyData(e){return M(JSON.stringify({salt:String.fromCharCode(...e.salt),encryptedMessage:C(e.encryptedMessage),iv:String.fromCharCode(...e.iv)}))}importSharedKeyData(e){const r=JSON.parse(m(e));return{salt:new Uint8Array([...r.salt].map(t=>t.charCodeAt(0))),encryptedMessage:U(r.encryptedMessage),iv:new Uint8Array([...r.iv].map(t=>t.charCodeAt(0)))}}hasSharedKey(e){return this.sharedKeys.has(e)}hasECDHPublicKey(e){return this.ecdhPublicKeys.has(e)}removeSharedKey(e){this.sharedKeys.delete(e)}removeECDHPublicKey(e){this.ecdhPublicKeys.delete(e)}decryptWithSharedKeyUnsafe(e,r){return p(this,void 0,void 0,function*(){const t=this.sharedKeys.get(r);if(!t)throw new Error(`Shared key not found for user: ${r}. Call deriveSharedKey() first.`);try{const{iv:i,encryptedMessage:s}=e,y=yield a().subtle.decrypt({name:"AES-GCM",iv:i},t,new Uint8Array(s));return{message:A().decode(y),verified:!0}}catch(i){throw new u(`Failed to decrypt with shared key: ${i instanceof Error?i.message:"Unknown error"}`,"decrypt",i instanceof Error?i:void 0)}})}}return T}));