mirror of
https://github.com/fzxx/XiangYue.git
synced 2025-11-25 03:15:16 +08:00
2 lines
5.3 KiB
JavaScript
2 lines
5.3 KiB
JavaScript
const encryptionMethod1={async deriveMasterKey(password,seed){try{const pwdBuf=utils.stringToArrayBuffer(password);const masterBits=sodium.crypto_pwhash(64,new Uint8Array(pwdBuf),new Uint8Array(seed),sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,sodium.crypto_pwhash_ALG_ARGON2ID13);const hkdfKey=await crypto.subtle.importKey('raw',masterBits,{name:'HKDF'},false,['deriveBits']);const[aesKeyBits,chachaKeyBits,aesIv,chachaNonce]=await Promise.all([crypto.subtle.deriveBits({name:'HKDF',hash:'SHA-512',salt:seed,info:utils.stringToArrayBuffer('AES-CTR-Key')},hkdfKey,256),crypto.subtle.deriveBits({name:'HKDF',hash:'SHA-512',salt:seed,info:utils.stringToArrayBuffer('ChaCha20-Key')},hkdfKey,256),crypto.subtle.deriveBits({name:'HKDF',hash:'SHA-512',salt:seed,info:utils.stringToArrayBuffer('AES-CTR-IV')},hkdfKey,128),crypto.subtle.deriveBits({name:'HKDF',hash:'SHA-512',salt:seed,info:utils.stringToArrayBuffer('ChaCha20-Nonce')},hkdfKey,96)]);const aesCtrKey=await crypto.subtle.importKey('raw',aesKeyBits,{name:'AES-CTR'},false,['encrypt','decrypt']);return{aesCtrKey,chacha20Key:new Uint8Array(chachaKeyBits),aesCtrIv:new Uint8Array(aesIv),chacha20Nonce:new Uint8Array(chachaNonce)};}catch(e){throw new Error(`主密钥派生失败: ${e.message}`);}},async deriveKeyPBKDF2(password,salt){try{const pwdBuf=utils.stringToArrayBuffer(password);const pbkdf2Key=await crypto.subtle.importKey('raw',pwdBuf,{name:'PBKDF2'},false,['deriveBits']);const keyBits=await crypto.subtle.deriveBits({name:'PBKDF2',salt,iterations:500000,hash:'SHA-256'},pbkdf2Key,512);const hkdfKey=await crypto.subtle.importKey('raw',keyBits,{name:'HKDF'},false,['deriveBits']);const[aesKeyBits,chachaKeyBits]=await Promise.all([crypto.subtle.deriveBits({name:'HKDF',hash:'SHA-256',salt:new Uint8Array(0),info:utils.stringToArrayBuffer('AES-CTR')},hkdfKey,256),crypto.subtle.deriveBits({name:'HKDF',hash:'SHA-256',salt:new Uint8Array(0),info:utils.stringToArrayBuffer('ChaCha20')},hkdfKey,256)]);const aesCtrKey=await crypto.subtle.importKey('raw',aesKeyBits,{name:'AES-CTR'},false,['encrypt','decrypt']);return{aesCtrKey,chacha20Key:new Uint8Array(chachaKeyBits)};}catch(e){throw new Error(`PBKDF2密钥派生失败: ${e.message}`);}},async encryptAESCTR(plaintext,key,iv){try{return crypto.subtle.encrypt({name:'AES-CTR',counter:new Uint8Array(iv),length:128},key,plaintext);}catch(e){throw new Error(`AES-CTR加密失败: ${e.message}`);}},async decryptAESCTR(ciphertext,key,iv){try{return crypto.subtle.decrypt({name:'AES-CTR',counter:new Uint8Array(iv),length:128},key,ciphertext);}catch(e){throw new Error(`AES-CTR解密失败: ${e.message}`);}},async encryptChaCha20Poly1305(plaintext,key,nonce,assocData){try{return sodium.crypto_aead_chacha20poly1305_ietf_encrypt(new Uint8Array(plaintext),new Uint8Array(assocData),null,new Uint8Array(nonce),key);}catch(e){throw new Error(`ChaCha20-Poly1305加密失败: ${e.message}`);}},async decryptChaCha20Poly1305(ciphertext,key,nonce,assocData){try{const decData=sodium.crypto_aead_chacha20poly1305_ietf_decrypt(null,new Uint8Array(ciphertext),new Uint8Array(assocData),new Uint8Array(nonce),key);return decData.buffer;}catch(e){throw new Error(`ChaCha20-Poly1305解密失败: ${e.message}`);}},async encrypt(plaintext,password,outputMode='base64'){try{const seed=utils.generateRandomBytes(16);const[masterKey,compData]=await Promise.all([this.deriveMasterKey(password,seed),utils.compressData(utils.stringToArrayBuffer(plaintext))]);const aesEnc=await this.encryptAESCTR(compData,masterKey.aesCtrKey,masterKey.aesCtrIv);const chachaEnc=await this.encryptChaCha20Poly1305(aesEnc,masterKey.chacha20Key,masterKey.chacha20Nonce,seed);const outBuf=new Uint8Array(seed.byteLength+chachaEnc.byteLength);outBuf.set(new Uint8Array(seed),0);outBuf.set(new Uint8Array(chachaEnc),seed.byteLength);let result=utils.arrayBufferToBase64(outBuf.buffer);const fmtMap={chinese:mappingMode1.base64ToChinese,emoji:mappingMode2.base64ToEmoji,'zero-width':mappingMode3.base64ToZeroWidth};return fmtMap[outputMode]?fmtMap[outputMode](result):result;}catch(e){throw new Error(`加密过程发生错误: ${e.message}`);}},async decrypt(ciphertext,password){try{const ctxType=utils.detectCiphertextType(ciphertext);const fmtMap={emoji:mappingMode2.emojiToBase64,chinese:mappingMode1.chineseToBase64,'zero-width':mappingMode3.zeroWidthToBase64};let procCtx=ciphertext;if(fmtMap[ctxType])procCtx=fmtMap[ctxType](procCtx);else if(['base64','weibase64'].includes(ctxType))procCtx=procCtx.replace(/[^A-Za-z0-9+/=]/g,'');const ctxBuf=utils.base64ToArrayBuffer(procCtx);let plaintext;try{const seed=ctxBuf.slice(0,16);const masterKey=await this.deriveMasterKey(password,seed);const chachaDec=await this.decryptChaCha20Poly1305(ctxBuf.slice(16),masterKey.chacha20Key,masterKey.chacha20Nonce,seed);plaintext=await this.decryptAESCTR(chachaDec,masterKey.aesCtrKey,masterKey.aesCtrIv);}catch(e){const salt=ctxBuf.slice(0,16);const nonce=ctxBuf.slice(16,28);const[aesKey,chachaKey]=await Promise.all([this.deriveKeyPBKDF2(password,salt).then(k=>k.aesCtrKey),this.deriveKeyPBKDF2(password,salt).then(k=>k.chacha20Key)]);const chachaDec=await this.decryptChaCha20Poly1305(ctxBuf.slice(28),chachaKey,nonce);plaintext=await this.decryptAESCTR(chachaDec.slice(16),aesKey,chachaDec.slice(0,16));}
|
|
return utils.arrayBufferToString(await utils.decompressData(plaintext));}catch(e){throw new Error(`解密过程发生错误: ${e.message}`);}}}; |