spd-lib 1.2.7 → 1.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/worker.js +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const fs=require("fs"),zlib=require("zlib"),sodium=require("libsodium-wrappers"),crypto=require("crypto"),argon2=require("argon2");class SPD{constructor(){this.data=[],this.keyPair,this.userKey,this.salt,this.hash="sha3-512",this.compression_level=9}async init(){await sodium.ready}async changePasscode(a,t){if(!a||!t)throw new Error("Old and new passcodes must be provided.");await sodium.ready;const{pqcKey:r,salt:e}=await this.convertPasscodeToPQCKeySalted(a,this.salt),i=r.privateKey,s={data:this.data,salt:Array.from(this.salt)},o=Buffer.from(JSON.stringify(s),"utf8"),n=crypto.createHmac("sha512",i).update(o).digest(),c=this.generateMAC(o,this.userKey);if(!crypto.timingSafeEqual(n,c))throw new Error("Old passcode is incorrect or data integrity check failed.");const y=await Promise.all(this.data.map((async a=>{const t=sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(Buffer.from(a.data),Buffer.from(a.nonce),i),r=zlib.inflateSync(t).toString("utf8");return{name:a.dataName,parsed:await this.CSTI(r,a.dataType),type:a.dataType}}))),{pqcKey:d,salt:l}=await this.convertPasscodeToPQCKey(t),p=d.privateKey;this.salt=l,this.userKey=p,this.data=[];for(const a of y)await this.addData(a.name,a.parsed)}generateMAC(a,t,r={}){const e=Buffer.concat([a,Buffer.from(r.dataName||"","utf8"),Buffer.from(r.dataType||"","utf8")]);return crypto.createHmac("sha512",t).update(e).digest()}checkPasscodeStrength(a){if("string"!=typeof a||a.length<12)throw new Error("Passcode must be at least 12 characters long.");if([/[a-z]/,/[A-Z]/,/[0-9]/,/[^A-Za-z0-9]/].filter((t=>!t.test(a))).length>1)throw new Error("Passcode must contain at least three of the following: lowercase letters, uppercase letters, digits, special characters.");return 1}#a;async setPassKey(a){this.checkPasscodeStrength(a),await sodium.ready,await this.init();const{pqcKey:t,salt:r}=await this.convertPasscodeToPQCKey(a),e=t.privateKey;this.userKey=e,this.salt=r}setHash(a="sha3-512"){this.hash=a}setCompressionLevel(a=9){this.compression_level=a}getSodium(){return sodium}sanitizeName(a){if("string"!=typeof a)throw new Error("dataName must be a string");return a.normalize("NFKC").trim().toLowerCase().replace(/[^a-z0-9_\-]/g,"_")}async addData(a,t){const r=await this.CITS(t);await sodium.ready;const e=Buffer.from(r[0]),i=zlib.deflateSync(e,{level:this.compression_level}),s=(this.sanitizeName(a),sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)),o=sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(i,null,null,s,this.userKey),n=crypto.createHash(this.hash).update(o).digest("hex");this.data.push({dataName:a,nonce:Buffer.from(s),data:Buffer.from(o),hash:n,dataType:r[1]})}async addMany(a){await sodium.ready;const t=a.map((async({name:a,data:t})=>{const r=await this.CITS(t),e=Buffer.from(r[0]),i=zlib.deflateSync(e,{level:this.compression_level}),s=sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES),o=sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(i,null,null,s,this.userKey),n=crypto.createHash(this.hash).update(o).digest("hex");return{dataName:a,nonce:Array.from(s),data:Array.from(o),hash:n,dataType:r[1]}})),r=await Promise.all(t);this.data.push(...r)}clearCache(){this.data=[],this.salt=null,this.userKey=null}async saveToFile(a,t){if("string"!=typeof a||!a.trim())throw new Error("Invalid output path.");const{encryptedSalt:r,saltNonce:e,wrapSalt:i}=await SPD.encryptSalt(this.salt,t),s={data:this.data,encryptedSalt:r,saltNonce:e,wrapSalt:i,version:24},o=JSON.stringify(s),n=Buffer.from(o,"utf8"),c=this.generateMAC(n,this.userKey,{dataName:"",dataType:"application/json"}),y={payload:Buffer.from(n),mac:Buffer.from(c)},d=zlib.deflateSync(Buffer.from(JSON.stringify(y)),{level:this.compression_level});fs.writeFileSync(a,d,{mode:384})}static async loadFromFile(a,t,r="sha3-512",e=9){try{if(!(a&&"string"==typeof a&&a.trim()&&t&&"string"==typeof t&&t.trim()))throw new Error("Invalid SPD path or passcode.");await sodium.ready;const i=fs.readFileSync(a),s=zlib.inflateSync(i,{level:e}).toString("utf8"),{payload:o,mac:n}=JSON.parse(s),c=Buffer.from(o),y=new SPD,{data:d,encryptedSalt:l,saltNonce:p,wrapSalt:f,version:u}=JSON.parse(Buffer.from(o).toString("utf-8"));if("number"!=typeof u||24!==u)throw new Error(`Unsupported SPD version: ${u}`);const h=await SPD.decryptSalt(l,p,f,t),{pqcKey:m}=await y.convertPasscodeToPQCKey(t,new Uint8Array(h)),w=m.privateKey,S=crypto.createHmac("sha512",w).update(Buffer.concat([c,Buffer.from("","utf8"),Buffer.from("application/json","utf8")])).digest();if(!crypto.timingSafeEqual(Buffer.from(n),S))throw new Error("MAC verification failed — data may be tampered with.");const g=new SPD;return g.setHash(r),g.setCompressionLevel(e),g.userKey=w,g.keyPair={publicKey:w.publicKey},g.data=d.map((a=>({dataName:a.dataName,nonce:Buffer.from(a.nonce),data:Buffer.from(a.data),hash:a.hash,dataType:a.dataType}))),await Promise.all(g.data.map((a=>{if(crypto.createHash(r).update(Buffer.from(a.data)).digest("hex")!==a.hash)throw new Error(`Data integrity check failed for ${a.dataName}`)}))),g}catch(a){throw new Error(a)}}async extractData(){await sodium.ready;const a=this.data.map((async a=>{try{const t=sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null,a.data,null,a.nonce,this.userKey),r=zlib.inflateSync(t,{level:this.compression_level}).toString("utf8"),e=await this.CSTI(r,a.dataType);return[a.dataName,e]}catch(t){throw new Error(`Failed to process ${a.dataName}: ${t.message}`)}})),t=await Promise.all(a);return Object.fromEntries(t)}static async derivePBK(a,t){if(!(t&&t instanceof Uint8Array&&16===t.length))throw new Error("Invalid salt.");if(!(a&&"string"==typeof a&&t&&t instanceof Uint8Array&&16===t.length))throw new Error("Invalid passcode or salt.");return{pbk:(await argon2.hash(a,{salt:Buffer.from(t),type:argon2.argon2id,raw:1,memoryCost:65536,timeCost:5,parallelism:1,hashLength:sodium.crypto_kx_SEEDBYTES})).slice(0,sodium.crypto_kx_SEEDBYTES),salt:t}}async saveData(a=""){const{encryptedSalt:t,saltNonce:r,wrapSalt:e}=await SPD.encryptSalt(this.salt,a),i={data:this.data,encryptedSalt:t,saltNonce:r,wrapSalt:e,version:24},s=JSON.stringify(i),o=Buffer.from(s,"utf8"),n=this.generateMAC(o,this.userKey,{dataName:"",dataType:"application/json"}),c={payload:Buffer.from(o),mac:Buffer.from(n)};return zlib.deflateSync(Buffer.from(JSON.stringify(c)),{level:this.compression_level}).toString("base64")}static async decryptSalt(a,t,r,e){const{pbk:i}=await SPD.derivePBK(e,new Uint8Array(r));await sodium.ready;const s=sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null,new Uint8Array(a),null,new Uint8Array(t),new Uint8Array(i));if(!s)throw new Error("Failed to decrypt salt.");return s}static async encryptSalt(a,t){const r=crypto.getRandomValues(new Uint8Array(16)),{pbk:e}=await SPD.derivePBK(t,r);await sodium.ready;const i=sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES),s=sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(a,null,null,i,new Uint8Array(e));return{encryptedSalt:Array.from(s),saltNonce:Array.from(i),wrapSalt:Array.from(r)}}static async loadFromString(a,t,r="sha3-512",e=9){try{if(!(a&&"string"==typeof a&&a.trim()&&t&&"string"==typeof t&&t.trim()))throw new Error("Invalid SPD path or passcode.");await sodium.ready;const i=Buffer.from(a,"base64"),s=zlib.inflateSync(i,{level:e}).toString("utf8"),{payload:o,mac:n}=JSON.parse(s),{data:c,encryptedSalt:y,saltNonce:d,wrapSalt:l,version:p}=JSON.parse(Buffer.from(o).toString("utf-8")),f=await SPD.decryptSalt(y,d,l,t);if("number"!=typeof p||24!==p)throw new Error(`Unsupported SPD version: ${p}`);const u=Buffer.from(o),h=new SPD,{pqcKey:m}=await h.convertPasscodeToPQCKey(t,new Uint8Array(f)),w=m.privateKey,S=crypto.createHmac("sha512",w).update(Buffer.concat([u,Buffer.from("","utf8"),Buffer.from("application/json","utf8")])).digest();if(!crypto.timingSafeEqual(Buffer.from(n),S))throw new Error("MAC verification failed — data may be tampered with.");const g=new SPD;return g.setHash(r),g.setCompressionLevel(e),g.userKey=w,g.keyPair={publicKey:w.publicKey},g.data=c.map((a=>({dataName:a.dataName,nonce:Buffer.from(a.nonce),data:Buffer.from(a.data),hash:a.hash,dataType:a.dataType}))),await Promise.all(g.data.map((a=>{if(crypto.createHash(r).update(Buffer.from(a.data)).digest("hex")!==a.hash)throw new Error(`Data integrity check failed for ${a.dataName}`)}))),g}catch(a){throw new Error(a)}}static toBase64(a){return Buffer.from(a).toString("base64")}static fromBase64(a){return new Uint8Array(Buffer.from(a,"base64"))}async convertPasscodeToPQCKey(a,t=crypto.getRandomValues(new Uint8Array(16))){if("string"!=typeof a||a.length<8)throw new Error("Invalid passcode.");const{pbk:r}=await SPD.derivePBK(a,t);await this.init();const e=sodium.crypto_kx_seed_keypair(r);return sodium.memzero(r),{pqcKey:{privateKey:e.privateKey},salt:t}}async TDT(a){return{"[object Array]":"Array","[object Uint8Array]":"Uint8Array","[object Uint16Array]":"Uint16Array","[object Uint32Array]":"Uint32Array","[object BigInt64Array]":"BigInt64Array","[object BigUint64Array]":"BigUint64Array","[object Float32Array]":"Float32Array","[object Float64Array]":"Float64Array","[object Map]":"Map","[object Set]":"Set","[object Date]":"Date","[object RegExp]":"RegExp","[object Error]":"Error"}[Object.prototype.toString.call(a)]}async isNumArr(a){if("Uint8Array"===a||"Uint16Array"===a||"Uint32Array"===a||"BigInt64Array"===a||"BigUint64Array"===a||"Float32Array"===a||"Float64Array"===a)return 1}async isSWM(a){if("Map"===a||"Set"===a||"WeakMap"===a||"WeakSet"===a)return 1}async isDRE(a){if("Date"===a||"RegExp"===a||"Error"===a)return 1}async CITS(a){const t=typeof a;if("string"===t||"number"===t||"boolean"===t)return[a.toString(),t];if("object"==typeof a){const t=await this.TDT(a);return"Array"===t?[JSON.stringify(a),"Array"]:await this.isNumArr(t)?[JSON.stringify(Array.from(a)),t]:await this.isSWM(t)?[JSON.stringify([...a]),t]:await this.isDRE(t)?[a.toString(),t]:[JSON.stringify(a),typeof a]}}async CSTI(a,t){try{switch(t){case"string":if("string"!=typeof a)throw new Error("Expected string");return a;case"number":const r=parseFloat(a);if(isNaN(r))throw new Error("Invalid number");return r;case"boolean":if("true"!==a&&"false"!==a)throw new Error("Invalid boolean");return"true"===a;case"Array":case"object":const e=JSON.parse(a);if("object"!=typeof e||null===e)throw new Error("Invalid object");return e;case"Uint8Array":return new Uint8Array(JSON.parse(a));case"Uint16Array":return new Uint16Array(JSON.parse(a));case"Uint32Array":return new Uint32Array(JSON.parse(a));case"BigInt64Array":return new BigInt64Array(JSON.parse(a));case"BigUint64Array":return new BigUint64Array(JSON.parse(a));case"Float32Array":return new Float32Array(JSON.parse(a));case"Float64Array":return new Float64Array(JSON.parse(a));case"Map":return new Map(JSON.parse(a));case"Set":return new Set(JSON.parse(a));case"Date":const i=new Date(a);if(isNaN(i.getTime()))throw new Error("Invalid Date");return i;case"RegExp":return new RegExp(a);case"Error":return new Error(a);default:throw new Error(`Unknown or unsupported type: ${t}`)}}catch(a){throw new Error(`Failed to restore data of type "${t}": ${a.message}`)}}}class SPD_Legacy{constructor(){this.data=[],this.keyPair,this.userKey,this.salt,this.init()}async init(){await sodium.ready,this.keyPair=sodium.crypto_box_keypair()}async setPassKey(a){await sodium.ready,this.init();const{pqcKey:t,salt:r}=await(new SPD_Legacy).convertPasscodeToPQCKey(a),e=t.publicKey;this.userKey=e,this.salt=r}async addData(a,t){const r=await this.CITS(t);await sodium.ready;const e=Buffer.from(r[0]),i=zlib.deflateSync(e,{level:9}),s=sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES),o=sodium.crypto_secretbox_easy(i,s,this.userKey),n=crypto.createHash("sha256").update(o).digest("hex");this.data.push({dataName:a,nonce:Array.from(s),data:Array.from(o),hash:n,dataType:r[1]})}saveToFile(a){if(!(a&&"string"==typeof a&&a.trim()&&this.salt&&this.salt instanceof Uint8Array&&16===this.salt.length))throw new Error("Invalid output path or salt.");const t=JSON.stringify({data:this.data,salt:Array.from(this.salt)}),r=zlib.deflateSync(t,{level:9});fs.writeFileSync(a,r,{mode:384})}static async loadFromFile(a,t){if(!(a&&"string"==typeof a&&a.trim()&&t&&"string"==typeof t&&t.trim()))throw new Error("Invalid SPD path or passcode.");await sodium.ready;try{const r=fs.readFileSync(a),e=zlib.inflateSync(r,{level:9}).toString("utf8"),{data:i,salt:s}=JSON.parse(e),o=new SPD_Legacy,{pqcKey:n}=await o.convertPasscodeToPQCKeySalted(t,new Uint8Array(s)),c=n.publicKey,y=new SPD_Legacy;y.userKey=c,y.keyPair={publicKey:c.publicKey},y.data=i.map((a=>({dataName:a.dataName,nonce:Buffer.from(a.nonce),data:Buffer.from(a.data),hash:a.hash,dataType:a.dataType})));const d=y.data.map((a=>new Promise(((t,r)=>{crypto.createHash("sha256").update(a.data).digest("hex")!==a.hash?r(new Error(`Data integrity check failed for ${a.dataName}`)):t()}))));return await Promise.all(d),y}catch(a){throw new Error(`Failed to load SPD file: ${a.message}`)}}async extractData(){await sodium.ready;try{const a=this.data.map((async a=>{try{const t=sodium.crypto_secretbox_open_easy(a.data,a.nonce,this.userKey),r=zlib.inflateSync(t,{level:9}).toString("utf8"),e=await this.CSTI(r,a.dataType);return[a.dataName,e]}catch(t){throw new Error(`Failed to process ${a.dataName}: ${t.message}`)}})),t=await Promise.all(a);return Object.fromEntries(t)}catch(a){throw new Error(`Data extraction failed: ${a.message}`)}}static async derivePBK(a,t){if(!(a&&"string"==typeof a&&a.trim()&&t&&t instanceof Uint8Array&&16===t.length))throw new Error("Invalid passcode or salt.");return new Promise(((r,e)=>{crypto.pbkdf2(a,t,1e5,32,"sha256",((a,i)=>{a?e(a):r({pbk:i,salt:t})}))}))}saveData(){const a=JSON.stringify({data:this.data,salt:Array.from(this.salt)});return zlib.deflateSync(a,{level:9})}static async loadFromString(a,t){if(!(a&&"string"==typeof a&&a.trim()&&t&&"string"==typeof t&&t.trim()))throw new Error("Invalid SPD data or passcode.");await sodium.ready;try{const r=Buffer.from(a,"base64"),e=zlib.inflateSync(r,{level:9}).toString("utf8"),{data:i,salt:s}=JSON.parse(e),o=new SPD_Legacy,{pqcKey:n}=await o.convertPasscodeToPQCKeySalted(t,new Uint8Array(s)),c=n.publicKey,y=new SPD_Legacy;y.userKey=c,y.keyPair={publicKey:c.publicKey},y.data=i.map((a=>({dataName:a.dataName,nonce:Buffer.from(a.nonce),data:Buffer.from(a.data),hash:a.hash,dataType:a.dataType})));const d=y.data.map((a=>new Promise(((t,r)=>{crypto.createHash("sha256").update(a.data).digest("hex")!==a.hash?r(new Error(`Data integrity check failed for ${a.dataName}`)):t()}))));return await Promise.all(d),y}catch(a){throw new Error(`Failed to load SPD data: ${a.message}`)}}async convertPasscodeToPQCKeySalted(a,t){if(!a||"string"!=typeof a||!a.trim()||a.length<8||!t||!(t instanceof Uint8Array)||16!==t.length)throw new Error("Invalid passcode or salt.");const{pbk:r}=await SPD.derivePBK(a,t);await sodium.ready;return{pqcKey:{publicKey:sodium.crypto_kx_seed_keypair(r).publicKey},salt:t}}async convertPasscodeToPQCKey(a){if(!a||"string"!=typeof a||!a.trim()||a.length<8)throw new Error("Invalid passcode.");const{pbk:t,salt:r}=await SPD.derivePBK(a,crypto.getRandomValues(new Uint8Array(16)));await sodium.ready;return{pqcKey:{publicKey:sodium.crypto_kx_seed_keypair(t.slice(0,sodium.crypto_kx_SEEDBYTES)).publicKey},salt:r}}async TDT(a){return{"[object Array]":"Array","[object Uint8Array]":"Uint8Array","[object Uint16Array]":"Uint16Array","[object Uint32Array]":"Uint32Array","[object BigInt64Array]":"BigInt64Array","[object BigUint64Array]":"BigUint64Array","[object Float32Array]":"Float32Array","[object Float64Array]":"Float64Array","[object Map]":"Map","[object Set]":"Set","[object Date]":"Date","[object RegExp]":"RegExp","[object Error]":"Error"}[Object.prototype.toString.call(a)]}async isNumArr(a){if("Uint8Array"===a||"Uint16Array"===a||"Uint32Array"===a||"BigInt64Array"===a||"BigUint64Array"===a||"Float32Array"===a||"Float64Array"===a)return 1}async isSWM(a){if("Map"===a||"Set"===a||"WeakMap"===a||"WeakSet"===a)return 1}async isDRE(a){if("Date"===a||"RegExp"===a||"Error"===a)return 1}async CITS(a){const t=typeof a;if("string"===t||"number"===t||"boolean"===t)return[a.toString(),t];if("object"==typeof a){const t=await this.TDT(a);return"Array"===t?[JSON.stringify(a),"Array"]:await this.isNumArr(t)?[JSON.stringify(Array.from(a)),t]:await this.isSWM(t)?[JSON.stringify([...a]),t]:await this.isDRE(t)?[a.toString(),t]:[JSON.stringify(a),typeof a]}}async CSTI(a,t){return"string"===t?a:"number"===t?parseFloat(a):"boolean"===t?"true"===a&&"false"!==a:"object"===t||"Array"===t?JSON.parse(a):"Uint8Array"===t?new Uint8Array(JSON.parse(a)):"Uint16Array"===t?new Uint16Array(JSON.parse(a)):"Uint32Array"===t?new Uint32Array(JSON.parse(a)):"BigInt64Array"===t?new BigInt64Array(JSON.parse(a)):"BigUint64Array"===t?new BigUint64Array(JSON.parse(a)):"Float32Array"===t?new Float32Array(JSON.parse(a)):"Float64Array"===t?new Float64Array(JSON.parse(a)):"Map"===t?new Map(JSON.parse(a)):"Set"===t?new Set(JSON.parse(a)):"WeakMap"===t?new WeakMap(JSON.parse(a)):"WeakSet"===t?new WeakSet(JSON.parse(a)):"Date"===t?new Date(a):"RegExp"===t?new RegExp(a):"Error"===t?new Error(a):void 0}}module.exports={SPD,SPD_LEG:SPD_Legacy};
|
|
1
|
+
const fs=require("fs"),zlib=require("zlib"),sodium=require("libsodium-wrappers"),crypto=require("crypto"),argon2=require("argon2"),{Worker}=require("worker_threads"),path=require("path/win32"),worker=new Worker("."+path.join(__dirname.replace(path.dirname(__dirname),""),"worker.js").replace(/\\/g,"/"));class SPD{constructor(){this.data=[],this.keyPair,this.userKey,this.salt,this.hash="sha3-512",this.compression_level=9,this.version=24}async init(){await sodium.ready}async stop(){await worker.terminate()}async changePasscode(t,e){if(!t||!e)throw new Error("Old and new passcodes must be provided.");await sodium.ready;const{pqcKey:a,salt:r}=await this.convertPasscodeToPQCKeySalted(t,this.salt),s=a.privateKey,i={data:this.data,salt:Array.from(this.salt)},o=Buffer.from(JSON.stringify(i),"utf8"),n=crypto.createHmac("sha512",s).update(o).digest(),c=this.generateMAC(o,this.userKey);if(!crypto.timingSafeEqual(n,c))throw new Error("Old passcode is incorrect or data integrity check failed.");const y=await Promise.all(this.data.map((async t=>{const e=sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(Buffer.from(t.data),Buffer.from(t.nonce),s),a=zlib.inflateSync(e).toString("utf8");return{name:t.dataName,parsed:await this.CSTI(a,t.dataType),type:t.dataType}}))),{pqcKey:d,salt:l}=await this.convertPasscodeToPQCKey(e),p=d.privateKey;this.salt=l,this.userKey=p,this.data=[];for(const t of y)await this.addData(t.name,t.parsed)}generateMAC(t,e,a={}){const r=Buffer.concat([t,Buffer.from(a.dataName||"","utf8"),Buffer.from(a.dataType||"","utf8")]);return crypto.createHmac("sha512",e).update(r).digest()}checkPasscodeStrength(t){if("string"!=typeof t||t.length<12)throw new Error("Passcode must be at least 12 characters long.");if([/[a-z]/,/[A-Z]/,/[0-9]/,/[^A-Za-z0-9]/].filter((e=>!e.test(t))).length>1)throw new Error("Passcode must contain at least three of the following: lowercase letters, uppercase letters, digits, special characters.");return 1}#t;async setPassKey(t){this.checkPasscodeStrength(t),await sodium.ready,await this.init();const{pqcKey:e,salt:a}=await this.convertPasscodeToPQCKey(t),r=e.privateKey;this.userKey=r,this.salt=a}setHash(t="sha3-512"){this.hash=t}setCompressionLevel(t=9){this.compression_level=t}getSodium(){return sodium}sanitizeName(t){if("string"!=typeof t)throw new Error("dataName must be a string");return t.normalize("NFKC").trim().toLowerCase().replace(/[^a-z0-9_\-]/g,"_")}async addData(t,e){const a=await this.CITS(e);await sodium.ready;const{encrypted:r,nonce:s,hash:i}=await function(t){return new Promise(((e,a)=>{worker.once("message",(t=>{if(t.error)return a(new Error(t.error));e(t)})),worker.once("error",a),worker.postMessage(t)}))}({type:"compress-encrypt",data:a[0],level:this.compression_level,key:this.userKey,hash:this.hash});this.data.push({dataName:t,nonce:Buffer.from(s),data:Buffer.from(r),hash:i,dataType:a[1]})}async addMany(t){await sodium.ready;const e=t.map((async({name:t,data:e})=>{const a=await this.CITS(e),r=Buffer.from(a[0]),s=zlib.deflateSync(r,{level:this.compression_level}),i=sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES),o=sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(s,null,null,i,this.userKey),n=crypto.createHash(this.hash).update(o).digest("hex");return{dataName:t,nonce:Array.from(i),data:Array.from(o),hash:n,dataType:a[1]}})),a=await Promise.all(e);this.data.push(...a)}clearCache(){this.data=[],this.salt=null,this.userKey=null}async saveToFile(t,e){if("string"!=typeof t||!t.trim())throw new Error("Invalid output path.");const{encryptedSalt:a,saltNonce:r,wrapSalt:s}=await SPD.encryptSalt(this.salt,e),i={data:this.data,encryptedSalt:a,saltNonce:r,wrapSalt:s,version:24},o=JSON.stringify(i),n=Buffer.from(o,"utf8"),c=this.generateMAC(n,this.userKey,{dataName:"",dataType:"application/json"}),y={payload:Buffer.from(n),mac:Buffer.from(c)},d=zlib.deflateSync(Buffer.from(JSON.stringify(y)),{level:this.compression_level});fs.writeFileSync(t,d,{mode:384})}static async loadFromFile(t,e,a="sha3-512",r=9){try{if(!(t&&"string"==typeof t&&t.trim()&&e&&"string"==typeof e&&e.trim()))throw new Error("Invalid SPD path or passcode.");await sodium.ready;const s=fs.readFileSync(t),i=zlib.inflateSync(s,{level:r}).toString("utf8"),{payload:o,mac:n}=JSON.parse(i),c=Buffer.from(o),y=new SPD,{data:d,encryptedSalt:l,saltNonce:p,wrapSalt:f,version:h}=JSON.parse(Buffer.from(o).toString("utf-8"));if("number"!=typeof h||24!==h)throw new Error(`Unsupported SPD version: ${h}`);y.version=h;const u=await SPD.decryptSalt(l,p,f,e),{pqcKey:m}=await y.convertPasscodeToPQCKey(e,new Uint8Array(u)),w=m.privateKey,S=crypto.createHmac("sha512",w).update(Buffer.concat([c,Buffer.from("","utf8"),Buffer.from("application/json","utf8")])).digest();if(!crypto.timingSafeEqual(Buffer.from(n),S))throw new Error("MAC verification failed — data may be tampered with.");const g=new SPD;return g.setHash(a),g.setCompressionLevel(r),g.userKey=w,g.keyPair={publicKey:w.publicKey},g.data=d.map((t=>({dataName:t.dataName,nonce:Buffer.from(t.nonce),data:Buffer.from(t.data),hash:t.hash,dataType:t.dataType}))),await Promise.all(g.data.map((t=>{if(crypto.createHash(a).update(Buffer.from(t.data)).digest("hex")!==t.hash)throw new Error(`Data integrity check failed for ${t.dataName}`)}))),g}catch(t){throw new Error(t)}}async extractData(){await sodium.ready;const t=this.data.map((async t=>{try{const e=sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null,t.data,null,t.nonce,this.userKey),a=zlib.inflateSync(e,{level:this.compression_level}).toString("utf8"),r=await this.CSTI(a,t.dataType);return[t.dataName,r]}catch(e){throw new Error(`Failed to process ${t.dataName}: ${e.message}`)}})),e=await Promise.all(t);return Object.fromEntries(e)}static async derivePBK(t,e){if(!(e&&e instanceof Uint8Array&&16===e.length))throw new Error("Invalid salt.");if(!(t&&"string"==typeof t&&e&&e instanceof Uint8Array&&16===e.length))throw new Error("Invalid passcode or salt.");return{pbk:(await argon2.hash(t,{salt:Buffer.from(e),type:argon2.argon2id,raw:1,memoryCost:65536,timeCost:5,parallelism:1,hashLength:sodium.crypto_kx_SEEDBYTES})).slice(0,sodium.crypto_kx_SEEDBYTES),salt:e}}async saveData(t=""){const{encryptedSalt:e,saltNonce:a,wrapSalt:r}=await SPD.encryptSalt(this.salt,t),s={data:this.data,encryptedSalt:e,saltNonce:a,wrapSalt:r,version:24},i=JSON.stringify(s),o=Buffer.from(i,"utf8"),n=this.generateMAC(o,this.userKey,{dataName:"",dataType:"application/json"}),c={payload:Buffer.from(o),mac:Buffer.from(n)};return zlib.deflateSync(Buffer.from(JSON.stringify(c)),{level:this.compression_level}).toString("base64")}static async decryptSalt(t,e,a,r){const{pbk:s}=await SPD.derivePBK(r,new Uint8Array(a));await sodium.ready;const i=sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null,new Uint8Array(t),null,new Uint8Array(e),new Uint8Array(s));if(!i)throw new Error("Failed to decrypt salt.");return i}static async encryptSalt(t,e){const a=crypto.getRandomValues(new Uint8Array(16)),{pbk:r}=await SPD.derivePBK(e,a);await sodium.ready;const s=sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES),i=sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(t,null,null,s,new Uint8Array(r));return{encryptedSalt:Array.from(i),saltNonce:Array.from(s),wrapSalt:Array.from(a)}}static async loadFromString(t,e,a="sha3-512",r=9){try{if(!(t&&"string"==typeof t&&t.trim()&&e&&"string"==typeof e&&e.trim()))throw new Error("Invalid SPD path or passcode.");await sodium.ready;const s=Buffer.from(t,"base64"),i=zlib.inflateSync(s,{level:r}).toString("utf8"),{payload:o,mac:n}=JSON.parse(i),{data:c,encryptedSalt:y,saltNonce:d,wrapSalt:l,version:p}=JSON.parse(Buffer.from(o).toString("utf-8")),f=await SPD.decryptSalt(y,d,l,e);if("number"!=typeof p||24!==p)throw new Error(`Unsupported SPD version: ${p}`);u.version=p;const h=Buffer.from(o),u=new SPD,{pqcKey:m}=await u.convertPasscodeToPQCKey(e,new Uint8Array(f)),w=m.privateKey,S=crypto.createHmac("sha512",w).update(Buffer.concat([h,Buffer.from("","utf8"),Buffer.from("application/json","utf8")])).digest();if(!crypto.timingSafeEqual(Buffer.from(n),S))throw new Error("MAC verification failed — data may be tampered with.");const g=new SPD;return g.setHash(a),g.setCompressionLevel(r),g.userKey=w,g.keyPair={publicKey:w.publicKey},g.data=c.map((t=>({dataName:t.dataName,nonce:Buffer.from(t.nonce),data:Buffer.from(t.data),hash:t.hash,dataType:t.dataType}))),await Promise.all(g.data.map((t=>{if(crypto.createHash(a).update(Buffer.from(t.data)).digest("hex")!==t.hash)throw new Error(`Data integrity check failed for ${t.dataName}`)}))),g}catch(t){throw new Error(t)}}static toBase64(t){return Buffer.from(t).toString("base64")}static fromBase64(t){return new Uint8Array(Buffer.from(t,"base64"))}async convertPasscodeToPQCKey(t,e=crypto.getRandomValues(new Uint8Array(16))){if("string"!=typeof t||t.length<8)throw new Error("Invalid passcode.");const{pbk:a}=await SPD.derivePBK(t,e);await this.init();const r=sodium.crypto_kx_seed_keypair(a);return sodium.memzero(a),{pqcKey:{privateKey:r.privateKey},salt:e}}async TDT(t){return{"[object Array]":"Array","[object Uint8Array]":"Uint8Array","[object Uint16Array]":"Uint16Array","[object Uint32Array]":"Uint32Array","[object BigInt64Array]":"BigInt64Array","[object BigUint64Array]":"BigUint64Array","[object Float32Array]":"Float32Array","[object Float64Array]":"Float64Array","[object Map]":"Map","[object Set]":"Set","[object Date]":"Date","[object RegExp]":"RegExp","[object Error]":"Error"}[Object.prototype.toString.call(t)]}async isNumArr(t){if("Uint8Array"===t||"Uint16Array"===t||"Uint32Array"===t||"BigInt64Array"===t||"BigUint64Array"===t||"Float32Array"===t||"Float64Array"===t)return 1}async isSWM(t){if("Map"===t||"Set"===t||"WeakMap"===t||"WeakSet"===t)return 1}async isDRE(t){if("Date"===t||"RegExp"===t||"Error"===t)return 1}async CITS(t){const e=typeof t;if("string"===e||"number"===e||"boolean"===e)return[t.toString(),e];if("object"==typeof t){const e=await this.TDT(t);return"Array"===e?[JSON.stringify(t),"Array"]:await this.isNumArr(e)?[JSON.stringify(Array.from(t)),e]:await this.isSWM(e)?[JSON.stringify([...t]),e]:await this.isDRE(e)?[t.toString(),e]:[JSON.stringify(t),typeof t]}}async CSTI(t,e){try{switch(e){case"string":if("string"!=typeof t)throw new Error("Expected string");return t;case"number":const a=parseFloat(t);if(isNaN(a))throw new Error("Invalid number");return a;case"boolean":if("true"!==t&&"false"!==t)throw new Error("Invalid boolean");return"true"===t;case"Array":case"object":const r=JSON.parse(t);if("object"!=typeof r||null===r)throw new Error("Invalid object");return r;case"Uint8Array":return new Uint8Array(JSON.parse(t));case"Uint16Array":return new Uint16Array(JSON.parse(t));case"Uint32Array":return new Uint32Array(JSON.parse(t));case"BigInt64Array":return new BigInt64Array(JSON.parse(t));case"BigUint64Array":return new BigUint64Array(JSON.parse(t));case"Float32Array":return new Float32Array(JSON.parse(t));case"Float64Array":return new Float64Array(JSON.parse(t));case"Map":return new Map(JSON.parse(t));case"Set":return new Set(JSON.parse(t));case"Date":const s=new Date(t);if(isNaN(s.getTime()))throw new Error("Invalid Date");return s;case"RegExp":return new RegExp(t);case"Error":return new Error(t);default:throw new Error(`Unknown or unsupported type: ${e}`)}}catch(t){throw new Error(`Failed to restore data of type "${e}": ${t.message}`)}}}class SPD_Legacy{constructor(){this.data=[],this.keyPair,this.userKey,this.salt,this.init()}async init(){await sodium.ready,this.keyPair=sodium.crypto_box_keypair()}async setPassKey(t){await sodium.ready,this.init();const{pqcKey:e,salt:a}=await(new SPD_Legacy).convertPasscodeToPQCKey(t),r=e.publicKey;this.userKey=r,this.salt=a}async addData(t,e){const a=await this.CITS(e);await sodium.ready;const r=Buffer.from(a[0]),s=zlib.deflateSync(r,{level:9}),i=sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES),o=sodium.crypto_secretbox_easy(s,i,this.userKey),n=crypto.createHash("sha256").update(o).digest("hex");this.data.push({dataName:t,nonce:Array.from(i),data:Array.from(o),hash:n,dataType:a[1]})}saveToFile(t){if(!(t&&"string"==typeof t&&t.trim()&&this.salt&&this.salt instanceof Uint8Array&&16===this.salt.length))throw new Error("Invalid output path or salt.");const e=JSON.stringify({data:this.data,salt:Array.from(this.salt)}),a=zlib.deflateSync(e,{level:9});fs.writeFileSync(t,a,{mode:384})}static async loadFromFile(t,e){if(!(t&&"string"==typeof t&&t.trim()&&e&&"string"==typeof e&&e.trim()))throw new Error("Invalid SPD path or passcode.");await sodium.ready;try{const a=fs.readFileSync(t),r=zlib.inflateSync(a,{level:9}).toString("utf8"),{data:s,salt:i}=JSON.parse(r),o=new SPD_Legacy,{pqcKey:n}=await o.convertPasscodeToPQCKeySalted(e,new Uint8Array(i)),c=n.publicKey,y=new SPD_Legacy;y.userKey=c,y.keyPair={publicKey:c.publicKey},y.data=s.map((t=>({dataName:t.dataName,nonce:Buffer.from(t.nonce),data:Buffer.from(t.data),hash:t.hash,dataType:t.dataType})));const d=y.data.map((t=>new Promise(((e,a)=>{crypto.createHash("sha256").update(t.data).digest("hex")!==t.hash?a(new Error(`Data integrity check failed for ${t.dataName}`)):e()}))));return await Promise.all(d),y}catch(t){throw new Error(`Failed to load SPD file: ${t.message}`)}}async extractData(){await sodium.ready;try{const t=this.data.map((async t=>{try{const e=sodium.crypto_secretbox_open_easy(t.data,t.nonce,this.userKey),a=zlib.inflateSync(e,{level:9}).toString("utf8"),r=await this.CSTI(a,t.dataType);return[t.dataName,r]}catch(e){throw new Error(`Failed to process ${t.dataName}: ${e.message}`)}})),e=await Promise.all(t);return Object.fromEntries(e)}catch(t){throw new Error(`Data extraction failed: ${t.message}`)}}static async derivePBK(t,e){if(!(t&&"string"==typeof t&&t.trim()&&e&&e instanceof Uint8Array&&16===e.length))throw new Error("Invalid passcode or salt.");return new Promise(((a,r)=>{crypto.pbkdf2(t,e,1e5,32,"sha256",((t,s)=>{t?r(t):a({pbk:s,salt:e})}))}))}saveData(){const t=JSON.stringify({data:this.data,salt:Array.from(this.salt)});return zlib.deflateSync(t,{level:9})}static async loadFromString(t,e){if(!(t&&"string"==typeof t&&t.trim()&&e&&"string"==typeof e&&e.trim()))throw new Error("Invalid SPD data or passcode.");await sodium.ready;try{const a=Buffer.from(t,"base64"),r=zlib.inflateSync(a,{level:9}).toString("utf8"),{data:s,salt:i}=JSON.parse(r),o=new SPD_Legacy,{pqcKey:n}=await o.convertPasscodeToPQCKeySalted(e,new Uint8Array(i)),c=n.publicKey,y=new SPD_Legacy;y.userKey=c,y.keyPair={publicKey:c.publicKey},y.data=s.map((t=>({dataName:t.dataName,nonce:Buffer.from(t.nonce),data:Buffer.from(t.data),hash:t.hash,dataType:t.dataType})));const d=y.data.map((t=>new Promise(((e,a)=>{crypto.createHash("sha256").update(t.data).digest("hex")!==t.hash?a(new Error(`Data integrity check failed for ${t.dataName}`)):e()}))));return await Promise.all(d),y}catch(t){throw new Error(`Failed to load SPD data: ${t.message}`)}}async convertPasscodeToPQCKeySalted(t,e){if(!t||"string"!=typeof t||!t.trim()||t.length<8||!e||!(e instanceof Uint8Array)||16!==e.length)throw new Error("Invalid passcode or salt.");const{pbk:a}=await SPD.derivePBK(t,e);await sodium.ready;return{pqcKey:{publicKey:sodium.crypto_kx_seed_keypair(a).publicKey},salt:e}}async convertPasscodeToPQCKey(t){if(!t||"string"!=typeof t||!t.trim()||t.length<8)throw new Error("Invalid passcode.");const{pbk:e,salt:a}=await SPD.derivePBK(t,crypto.getRandomValues(new Uint8Array(16)));await sodium.ready;return{pqcKey:{publicKey:sodium.crypto_kx_seed_keypair(e.slice(0,sodium.crypto_kx_SEEDBYTES)).publicKey},salt:a}}async TDT(t){return{"[object Array]":"Array","[object Uint8Array]":"Uint8Array","[object Uint16Array]":"Uint16Array","[object Uint32Array]":"Uint32Array","[object BigInt64Array]":"BigInt64Array","[object BigUint64Array]":"BigUint64Array","[object Float32Array]":"Float32Array","[object Float64Array]":"Float64Array","[object Map]":"Map","[object Set]":"Set","[object Date]":"Date","[object RegExp]":"RegExp","[object Error]":"Error"}[Object.prototype.toString.call(t)]}async isNumArr(t){if("Uint8Array"===t||"Uint16Array"===t||"Uint32Array"===t||"BigInt64Array"===t||"BigUint64Array"===t||"Float32Array"===t||"Float64Array"===t)return 1}async isSWM(t){if("Map"===t||"Set"===t||"WeakMap"===t||"WeakSet"===t)return 1}async isDRE(t){if("Date"===t||"RegExp"===t||"Error"===t)return 1}async CITS(t){const e=typeof t;if("string"===e||"number"===e||"boolean"===e)return[t.toString(),e];if("object"==typeof t){const e=await this.TDT(t);return"Array"===e?[JSON.stringify(t),"Array"]:await this.isNumArr(e)?[JSON.stringify(Array.from(t)),e]:await this.isSWM(e)?[JSON.stringify([...t]),e]:await this.isDRE(e)?[t.toString(),e]:[JSON.stringify(t),typeof t]}}async CSTI(t,e){return"string"===e?t:"number"===e?parseFloat(t):"boolean"===e?"true"===t&&"false"!==t:"object"===e||"Array"===e?JSON.parse(t):"Uint8Array"===e?new Uint8Array(JSON.parse(t)):"Uint16Array"===e?new Uint16Array(JSON.parse(t)):"Uint32Array"===e?new Uint32Array(JSON.parse(t)):"BigInt64Array"===e?new BigInt64Array(JSON.parse(t)):"BigUint64Array"===e?new BigUint64Array(JSON.parse(t)):"Float32Array"===e?new Float32Array(JSON.parse(t)):"Float64Array"===e?new Float64Array(JSON.parse(t)):"Map"===e?new Map(JSON.parse(t)):"Set"===e?new Set(JSON.parse(t)):"WeakMap"===e?new WeakMap(JSON.parse(t)):"WeakSet"===e?new WeakSet(JSON.parse(t)):"Date"===e?new Date(t):"RegExp"===e?new RegExp(t):"Error"===e?new Error(t):void 0}}module.exports={SPD,SPD_LEG:SPD_Legacy,SPD_Vault:class{#e=new Map;#a=new Map;constructor(t=3e5){this.timeoutMs=t}#r(t=512){const e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()[]{}\\|;:'\",./? ",a=new Uint32Array(t);return crypto.getRandomValues(a),Array.from(a,(t=>e[t%89])).join("")}#s(t){this.#a.has(t)&&clearTimeout(this.#a.get(t));const e=setTimeout((()=>{this.#e.delete(t),this.#a.delete(t)}),this.timeoutMs);this.#a.set(t,e)}gen_key(t=""){if(!this.#e.has(t)){const e=this.#r(500+Math.floor(200*Math.random()));this.#e.set(t,e),this.#s(t)}}push_key(t="",e=""){this.#e.has(t)||(this.#e.set(t,e),this.#s(t))}use_key(t="",e=t=>{}){this.#e.has(t)&&(e(this.#e.get(t)),this.#s(t))}pull_key(t=""){return this.#e.has(t)?(this.#s(t),this.#e.get(t)):undefined}destroy_key(t=""){this.#e.delete(t),this.#a.has(t)&&(clearTimeout(this.#a.get(t)),this.#a.delete(t))}stop(){this.#e.forEach(((t,e)=>{clearTimeout(this.#a.get(e))}))}}};
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const{parentPort}=require("worker_threads"),zlib=require("zlib"),crypto=require("crypto"),sodium=require("libsodium-wrappers"),argon2=require("argon2");(async()=>{await sodium.ready,parentPort.on("message",(async e=>{try{switch(e.type){case"compress-encrypt":{const a=zlib.deflateSync(Buffer.from(e.data),{level:e.level}),t=sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES),r=sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(a,null,null,t,e.key),s=crypto.createHash(e.hash).update(r).digest("hex");parentPort.postMessage({encrypted:r,nonce:t,hash:s});break}case"decrypt-decompress":{const a=sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null,e.encrypted,null,e.nonce,e.key),t=zlib.inflateSync(a);parentPort.postMessage({data:t.toString("utf8")});break}case"hash":{const a=crypto.createHash(e.hash).update(Buffer.from(e.data)).digest("hex");parentPort.postMessage({hash:a});break}case"mac":{const a=Buffer.concat([Buffer.from(e.dataBuffer),Buffer.from(e.meta?.dataName||"","utf8"),Buffer.from(e.meta?.dataType||"","utf8")]),t=crypto.createHmac(e.hash||"sha512",e.key).update(a).digest();parentPort.postMessage({mac:Array.from(t)});break}case"expectedMac":{const a=crypto.createHmac(e.hash||"sha512",e.key).update(Buffer.concat([e.buffer,Buffer.from(e.meta,"utf8"),Buffer.from(e._type,"utf8")])).digest();parentPort.postMessage({expectedMac:a});break}case"argon2":{const a=await argon2.hash(e.passcode,{salt:Buffer.from(e.salt),type:argon2.argon2id,raw:1,memoryCost:65536,timeCost:5,parallelism:1,hashLength:32});parentPort.postMessage({pbk:Array.from(a)});break}case"decrypt-salt:Uint8Array":{const a=sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null,new Uint8Array(e.encryptedSalt),null,new Uint8Array(e.saltNonce),new Uint8Array(e.key));parentPort.postMessage({salt:a});break}case"encrypt-salt:Array":{const a=sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES),t=sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(e.salt,null,null,a,new Uint8Array(e.key));parentPort.postMessage({nonce:a,encryptedSalt:t});break}default:throw new Error("Unsupported task type.")}}catch(e){parentPort.postMessage({error:e.message})}}))})();
|