This commit is contained in:
风之暇想
2025-07-16 19:10:25 +08:00
committed by GitHub
parent 151cecda55
commit bcc6329cee
7 changed files with 18 additions and 22 deletions

View File

@@ -1,3 +1 @@
const encryptionMethod1={async deriveKey(password,salt){try{const passwordBuffer=utils.stringToArrayBuffer(password);const pbkdf2Key=await crypto.subtle.importKey('raw',passwordBuffer,{name:'PBKDF2'},false,['deriveBits']);const pbkdf2Bits=await crypto.subtle.deriveBits({name:'PBKDF2',salt,iterations:500000,hash:'SHA-256'},pbkdf2Key,512);const hkdfKey=await crypto.subtle.importKey('raw',pbkdf2Bits,{name:'HKDF'},false,['deriveBits']);const aesBits=await crypto.subtle.deriveBits({name:'HKDF',hash:'SHA-256',salt:new Uint8Array(0),info:utils.stringToArrayBuffer('AES-CTR')},hkdfKey,256);const chachaBits=await 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',aesBits,{name:'AES-CTR'},false,['encrypt','decrypt']);return{aesCtrKey,chachaKey:new Uint8Array(chachaBits)};}catch(error){console.error('密钥派生失败:',error);throw new Error('密钥派生过程发生错误,请检查输入参数');}},async encryptAESCTR(plaintext,key){try{const counter=utils.generateRandomBytes(16);const encrypted=await crypto.subtle.encrypt({name:'AES-CTR',counter,length:128},key,plaintext);return new Uint8Array([...counter,...new Uint8Array(encrypted)]).buffer;}catch(error){console.error('AES-CTR加密失败:',error);throw new Error('AES-CTR加密过程发生错误');}},async decryptAESCTR(ciphertext,key){try{const counter=ciphertext.slice(0,16);const encrypted=ciphertext.slice(16);return crypto.subtle.decrypt({name:'AES-CTR',counter,length:128},key,encrypted);}catch(error){console.error('AES-CTR解密失败:',error);throw new Error('AES-CTR解密过程发生错误可能密钥不正确');}},async encryptChaCha20Poly1305(plaintext,key){try{const nonce=sodium.randombytes_buf(12);const encrypted=sodium.crypto_aead_chacha20poly1305_ietf_encrypt(new Uint8Array(plaintext),null,null,nonce,key);return new Uint8Array([...nonce,...encrypted]).buffer;}catch(error){console.error('ChaCha20加密失败:',error);throw new Error('ChaCha20加密过程发生错误');}},async decryptChaCha20Poly1305(ciphertext,key){try{const nonce=ciphertext.slice(0,12);const encrypted=ciphertext.slice(12);return sodium.crypto_aead_chacha20poly1305_ietf_decrypt(null,new Uint8Array(encrypted),null,new Uint8Array(nonce),key);}catch(error){console.error('ChaCha20解密失败:',error);throw new Error('ChaCha20解密过程发生错误可能密钥或数据已损坏');}},isBase64(str){return/^[A-Za-z0-9+/=]+$/.test(str);},async encrypt(plaintext,password,isBase64Enabled=true){try{const salt=utils.generateRandomBytes(16);const{aesCtrKey,chachaKey}=await this.deriveKey(password,salt);const compressed=await utils.compressData(utils.stringToArrayBuffer(plaintext));const aesCtrEncrypted=await this.encryptAESCTR(compressed,aesCtrKey);const chachaEncrypted=await this.encryptChaCha20Poly1305(aesCtrEncrypted,chachaKey);const combined=new Uint8Array([...salt,...new Uint8Array(chachaEncrypted)]);let result=utils.arrayBufferToBase64(combined.buffer);if(!isBase64Enabled){result=mappingMode1.base64ToChinese(result);}
return result;}catch(error){console.error('数据加密失败:',error);throw new Error(`加密过程发生错误: ${error.message}`);}},async decrypt(ciphertext,password){try{let processedCiphertext=ciphertext;if(!this.isBase64(ciphertext)){processedCiphertext=mappingMode1.chineseToBase64(ciphertext);}
const ciphertextBuffer=utils.base64ToArrayBuffer(processedCiphertext);const salt=ciphertextBuffer.slice(0,16);const encryptedData=ciphertextBuffer.slice(16);const{aesCtrKey,chachaKey}=await this.deriveKey(password,salt);const chachaDecrypted=await this.decryptChaCha20Poly1305(encryptedData,chachaKey);const aesCtrDecrypted=await this.decryptAESCTR(chachaDecrypted,aesCtrKey);return utils.arrayBufferToString(await utils.decompressData(aesCtrDecrypted));}catch(error){console.error('数据解密失败:',error);if(error.message.includes('decryption failed')){throw new Error('解密失败:可能密码错误或数据已损坏');}else if(error.message.includes('key derivation')){throw new Error('密钥派生失败:请检查密码长度和复杂度');}else{throw new Error(`解密过程发生错误: ${error.message}`);}}}};
const encryptionMethod1={async deriveKey(password,salt){try{const passwordBuffer=utils.stringToArrayBuffer(password);const pbkdf2Key=await crypto.subtle.importKey('raw',passwordBuffer,{name: 'PBKDF2'},false,['deriveBits']);const pbkdf2Bits=await crypto.subtle.deriveBits({name: 'PBKDF2',salt,iterations: 500000,hash: 'SHA-256'},pbkdf2Key,512);const hkdfKey=await crypto.subtle.importKey('raw',pbkdf2Bits,{name: 'HKDF'},false,['deriveBits']);const aesBits=await crypto.subtle.deriveBits({name: 'HKDF',hash: 'SHA-256',salt: new Uint8Array(0),info: utils.stringToArrayBuffer('AES-CTR')},hkdfKey,256);const chachaBits=await 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',aesBits,{name: 'AES-CTR'},false,['encrypt','decrypt']);return{aesCtrKey,chachaKey: new Uint8Array(chachaBits)};}catch(error){console.error('密钥派生失败:',error);throw new Error('密钥派生过程发生错误,请检查输入参数');}},async encryptAESCTR(plaintext,key){try{const counter=utils.generateRandomBytes(16);const encrypted=await crypto.subtle.encrypt({name: 'AES-CTR',counter,length: 128},key,plaintext);return new Uint8Array([...counter,...new Uint8Array(encrypted)]).buffer;}catch(error){console.error('AES-CTR加密失败:',error);throw new Error('AES-CTR加密过程发生错误');}},async decryptAESCTR(ciphertext,key){try{const counter=ciphertext.slice(0,16);const encrypted=ciphertext.slice(16);return crypto.subtle.decrypt({name: 'AES-CTR',counter,length: 128},key,encrypted);}catch(error){console.error('AES-CTR解密失败:',error);throw new Error('AES-CTR解密过程发生错误可能密钥不正确');}},async encryptChaCha20Poly1305(plaintext,key){try{const nonce=sodium.randombytes_buf(12);const encrypted=sodium.crypto_aead_chacha20poly1305_ietf_encrypt(new Uint8Array(plaintext),null,null,nonce,key);return new Uint8Array([...nonce,...encrypted]).buffer;}catch(error){console.error('ChaCha20加密失败:',error);throw new Error('ChaCha20加密过程发生错误');}},async decryptChaCha20Poly1305(ciphertext,key){try{const nonce=ciphertext.slice(0,12);const encrypted=ciphertext.slice(12);return sodium.crypto_aead_chacha20poly1305_ietf_decrypt(null,new Uint8Array(encrypted),null,new Uint8Array(nonce),key);}catch(error){console.error('ChaCha20解密失败:',error);throw new Error('ChaCha20解密过程发生错误可能密钥或数据已损坏');}},isBase64(str){return/^[A-Za-z0-9+/=]+$/.test(str);},async encrypt(plaintext,password,outputMode='base64'){try{const salt=utils.generateRandomBytes(16);const{aesCtrKey,chachaKey}=await this.deriveKey(password,salt);const compressed=await utils.compressData(utils.stringToArrayBuffer(plaintext));const aesCtrEncrypted=await this.encryptAESCTR(compressed,aesCtrKey);const chachaEncrypted=await this.encryptChaCha20Poly1305(aesCtrEncrypted,chachaKey);const combined=new Uint8Array([...salt,...new Uint8Array(chachaEncrypted)]);let result=utils.arrayBufferToBase64(combined.buffer);switch(outputMode){case 'chinese': result=mappingMode1.base64ToChinese(result);break;case 'emoji': result=mappingMode2.base64ToEmoji(result);break;}return result;}catch(error){console.error('数据加密失败:',error);throw new Error(`加密过程发生错误: ${error.message}`);}},async decrypt(ciphertext,password){try{const detectedMode=utils.detectCiphertextType(ciphertext);let processedCiphertext=ciphertext;if(detectedMode==='emoji'){processedCiphertext=mappingMode2.emojiToBase64(ciphertext);}else if(detectedMode==='chinese'){processedCiphertext=mappingMode1.chineseToBase64(ciphertext);}const ciphertextBuffer=utils.base64ToArrayBuffer(processedCiphertext);const salt=ciphertextBuffer.slice(0,16);const encryptedData=ciphertextBuffer.slice(16);const{aesCtrKey,chachaKey}=await this.deriveKey(password,salt);const chachaDecrypted=await this.decryptChaCha20Poly1305(encryptedData,chachaKey);const aesCtrDecrypted=await this.decryptAESCTR(chachaDecrypted,aesCtrKey);return utils.arrayBufferToString(await utils.decompressData(aesCtrDecrypted));}catch(error){console.error('数据解密失败:',error);if(error.message.includes('decryption failed')){throw new Error('解密失败:可能密码错误或数据已损坏');}else if(error.message.includes('key derivation')){throw new Error('密钥派生失败:请检查密码长度和复杂度');}else{throw new Error(`解密过程发生错误: ${error.message}`);}}}};

File diff suppressed because one or more lines are too long

1
js/mapping-mode-2.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1 @@
const DEFAULT_PASSWORD='a184f7b849ffffed24d266a30298c72ef2f5ad040db73bf37151fac767630728';async function handleStream(stream,writerData){const writer=stream.writable.getWriter();writer.write(writerData);writer.close();const reader=stream.readable.getReader();const chunks=[];while(true){const{done,value}=await reader.read();if(done)break;chunks.push(value);}
return new Blob(chunks).arrayBuffer();}
const utils={arrayBufferToBase64(buffer){let binary='';const bytes=new Uint8Array(buffer);for(let i=0;i<bytes.byteLength;i++){binary+=String.fromCharCode(bytes[i]);}
return btoa(binary);},base64ToArrayBuffer(base64){const binaryString=atob(base64);const bytes=new Uint8Array(binaryString.length);for(let i=0;i<binaryString.length;i++){bytes[i]=binaryString.charCodeAt(i);}
return bytes.buffer;},stringToArrayBuffer(str){return new TextEncoder().encode(str).buffer;},arrayBufferToString(buffer){return new TextDecoder().decode(buffer);},generateRandomBytes(length){return crypto.getRandomValues(new Uint8Array(length));},async compressData(data,method='deflate'){if(!window.CompressionStream)throw new Error('当前浏览器不支持CompressionStream API');const cs=new CompressionStream(method);return handleStream(cs,data);},async decompressData(data,method='deflate'){if(!window.DecompressionStream)throw new Error('当前浏览器不支持DecompressionStream API');const ds=new DecompressionStream(method);return handleStream(ds,data);},generatePassword(length){const charset='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+~`|}{[]:;?><,./-=';let password='';for(let i=0;i<length;i++){const randomIndex=Math.floor(Math.random()*charset.length);password+=charset.charAt(randomIndex);}
return password;},copyToClipboard(text){navigator.clipboard.writeText(text).catch(err=>{console.error('复制失败:',err);showNotification('复制到剪贴板失败,请手动复制',false);});}};
const DEFAULT_PASSWORD='a184f7b849ffffed24d266a30298c72ef2f5ad040db73bf37151fac767630728';async function handleStream(stream,writerData){const writer=stream.writable.getWriter();writer.write(writerData);writer.close();const reader=stream.readable.getReader();const chunks=[];while(true){const{done,value}=await reader.read();if(done)break;chunks.push(value);}return new Blob(chunks).arrayBuffer();}const utils={arrayBufferToBase64(buffer){let binary='';const bytes=new Uint8Array(buffer);for(let i=0;i < bytes.byteLength;i++){binary+=String.fromCharCode(bytes[i]);}return btoa(binary);},base64ToArrayBuffer(base64){const binaryString=atob(base64);const bytes=new Uint8Array(binaryString.length);for(let i=0;i < binaryString.length;i++){bytes[i]=binaryString.charCodeAt(i);}return bytes.buffer;},stringToArrayBuffer(str){return new TextEncoder().encode(str).buffer;},arrayBufferToString(buffer){return new TextDecoder().decode(buffer);},generateRandomBytes(length){return crypto.getRandomValues(new Uint8Array(length));},async compressData(data,method='deflate'){if(!window.CompressionStream)throw new Error('当前浏览器不支持CompressionStream API');const cs=new CompressionStream(method);return handleStream(cs,data);},async decompressData(data,method='deflate'){if(!window.DecompressionStream)throw new Error('当前浏览器不支持DecompressionStream API');const ds=new DecompressionStream(method);return handleStream(ds,data);},generatePassword(length){const charset='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+~`|}{[]:;?><,./-=';let password='';for(let i=0;i < length;i++){const randomIndex=Math.floor(Math.random()*charset.length);password+=charset.charAt(randomIndex);}return password;},copyToClipboard(text){navigator.clipboard.writeText(text).catch(err=>{console.error('复制失败:',err);showNotification('复制到剪贴板失败,请手动复制',false);});},detectCiphertextType(ciphertext){if(/^[A-Za-z0-9+/=]+$/.test(ciphertext)){return 'base64';}const emojiRegex=/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u;if(emojiRegex.test(ciphertext)){return 'emoji';}return 'chinese';}};

File diff suppressed because one or more lines are too long