mirror of
https://github.com/fzxx/XiangYue.git
synced 2025-11-25 03:15:16 +08:00
v1.4.0.0
This commit is contained in:
14
README.md
14
README.md
@@ -1,14 +1,14 @@
|
||||
# 想曰
|
||||
|
||||
[](https://github.com/fzxx/XiangYue) [](https://github.com/fzxx/XiangYue) [](https://github.com/fzxx/XiangYue/issues?q=is%3Aissue+is%3Aclosed) [](https://github.com/fzxx/XiangYue/blob/main/main/License.txt)
|
||||
[](https://github.com/fzxx/XiangYue?tab=readme-ov-file#%EF%B8%8F-%E6%8A%80%E6%9C%AF%E7%BB%86%E8%8A%82) [](https://github.com/fzxx/XiangYue?tab=readme-ov-file#-%E7%89%B9%E7%82%B9)
|
||||
[](https://github.com/fzxx/XiangYue?tab=readme-ov-file#%EF%B8%8F-%E6%8A%80%E6%9C%AF%E7%BB%86%E8%8A%82) [](https://github.com/fzxx/XiangYue?tab=readme-ov-file#-%E7%89%B9%E7%82%B9)
|
||||
|
||||
<span style="color: Blue;"><strong><a href="https://github.com/fzxx/XiangYue" style="color: inherit;">想曰(yuē)</a></strong></span> 是基于现代加密技术的文本加密工具,使用**多算法级联加密**方案,确保数据在本地完成加密/解密,保护隐私安全。
|
||||
|
||||
|
||||
## 🌟 特点
|
||||
|
||||
- ㊙️**密文**:支持 `中文 / Base64 / Emoji / 零宽` 密文
|
||||
- ㊙️**密文**:支持 `中文/Base64/Emoji/零宽/日语` 密文
|
||||
- 🔐**密钥**:`Argon2id + HKDF-SHA512`,有效抵御暴力破解
|
||||
- 🔒**级联算法**:采用 `AES256-CTR` 与 `ChaCha20-Poly1305-IETF` 级联加密,**安全性极高**
|
||||
- 📄**数据**:所有操作在本地完成,数据不离开设备
|
||||
@@ -39,6 +39,12 @@ J7ni11NnCUEe1+GtZcIWoJcKNgzsyN8K8BQBKnDn/1mLPkv2ul1VUcedyoIgZpXcNUKfy3HhZI6soaa5
|
||||
|
||||
`https://github.com/fzxx/͏͏͏XiangYue`
|
||||
|
||||
##### 日语密文
|
||||
|
||||
```plaintext
|
||||
ヷㇴょわㇿデぞズゆェピゆベナこマびむしヾノざュゝるスしニユダクぷすゾゔうゼダりち〴ㇹぐぁぇヘぷゼぺにづヂボゔㇲこぱミみぼメェだは
|
||||
```
|
||||
|
||||
## 🖥️在线与离线使用
|
||||
|
||||
[](https://xyue.515188.xyz/) [](https://xshuo.515188.xyz/) [](https://github.com/fzxx/XiangYue/releases)
|
||||
@@ -84,6 +90,10 @@ J7ni11NnCUEe1+GtZcIWoJcKNgzsyN8K8BQBKnDn/1mLPkv2ul1VUcedyoIgZpXcNUKfy3HhZI6soaa5
|
||||
- 密文总长度没有变,只是**转为不可见字符藏在两个可见字符中间**,是为了让人知道密文在哪,方便复制。
|
||||
- 可以,只需**手动替换**密文中的可见字符(零宽密文**只是对人不可见**,机器是秒识别的,但识别不代表能解密)。
|
||||
|
||||
#### 有增加非对称加密的计划吗?
|
||||
|
||||
- 有,视Stars情况再考虑,太少人用没有增加的必要。
|
||||
|
||||
## 📖 许可证
|
||||
|
||||
[想曰](https://github.com/fzxx/XiangYue) - [私下研究专用许可](https://github.com/fzxx/XiangYue?tab=License-1-ov-file#)
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
83
js/library/tailwind-3.4.17.js
Normal file
83
js/library/tailwind-3.4.17.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
||||
const CharSets1={"A":["甜","猛","浪","帮","拧","双","烟","高","立","百"],"B":["轻","真","看","按","夕","暑","涛","单","拍","助"],"C":["凸","引","号","咯","到","鸣","喊","是","疑","桂"],"D":["缓","页","离","灾","苦","疏","也","拒","触","灯"],"E":["泉","室","城","杏","尝","细","曲","靠","机","院"],"F":["峦","望","蹲","聚","旋","楼","应","晴","曾","爱"],"G":["茶","叹","梨","别","怯","于","圆","昼","缺","少"],"H":["梁","集","还","棋","熟","气","抓","摸","视","才"],"I":["棉","浓","着","湖","晃","但","团","甚","吓","涨"],"J":["猴","除","拉","意","成","抬","唱","春","宠","救"],"K":["为","卷","愁","功","晨","留","线","啦","忙","闪"],"L":["想","片","愿","准","兔","烦","勿","消","棕","吹"],"M":["棒","本","盯","却","喝","虹","桥","嘻","被","更"],"N":["拆","学","已","稻","使","摆","熊","秒","慢","越"],"O":["拿","拥","人","补","闹","了","没","续","火","停"],"P":["雀","羊","墙","并","冷","情","琴","散","江","小"],"Q":["响","太","虎","桌","压","我","今","影","拙","个"],"R":["跑","过","店","蜂","晓","变","条","雷","方","正"],"S":["凭","晚","得","耳","伴","星","贵","洁","蚊","叫"],"T":["定","多","堆","山","否","凉","柚","米","平","旗"],"U":["香","床","经","穿","迷","允","稍","老","观","瓣"],"V":["羞","搭","表","竖","陪","稠","柱","锅","窗","答"],"W":["分","送","包","重","只","滑","虚","柔","担","你"],"X":["聪","接","然","赶","兴","斜","医","敲","劝","虽"],"Y":["扳","难","非","温","增","顺","梅","水","躺","沉"],"Z":["生","块","地","易","直","黑","李","问","狼","往"],"a":["木","鱼","雨","慌","园","时","鼓","若","思","桃"],"b":["连","鹿","密","诉","厅","简","剩","年","错","现"],"c":["头","转","跟","用","扶","屋","满","墨","旧","虾"],"d":["薄","嗯","帅","知","志","捏","就","韧","柜","根"],"e":["起","岭","升","梦","钻","净","书","种","海","隐"],"f":["化","馆","啊","推","脆","灵","月","退","桑","吧"],"g":["败","爬","令","演","而","横","霞","光","张","疼"],"h":["心","急","夏","编","坐","扎","暖","加","挤","藤"],"i":["门","倦","盛","鲜","忧","矮","粗","感","假","听"],"j":["纸","必","鞋","蟹","厨","友","兆","电","勤","无"],"k":["因","凹","的","软","把","累","象","吉","池","带"],"l":["烫","眠","紫","降","扁","房","渐","呜","针","说"],"m":["读","静","白","枕","闭","窄","蛇","谈","红","休"],"n":["支","向","咕","握","冰","云","盆","寒","提","漂"],"o":["风","进","拖","闲","短","跳","托","夜","粒","显"],"p":["牛","金","很","呢","花","句","哪","以","清","洋"],"q":["嫩","减","酸","觉","嘘","弱","躲","秋","背","好"],"r":["谷","保","倚","遍","燕","咩","快","画","不","回"],"s":["盘","勺","亮","登","浮","皱","树","兰","列","节"],"t":["弟","行","扇","镜","阶","衣","篇","哦","富","奔"],"u":["叠","枝","挑","碗","零","大","品","冬","幸","呀"],"v":["惭","系","户","或","捧","钟","确","合","辣","兄"],"w":["袜","活","随","结","巧","乘","箱","次","眨","雪"],"x":["略","美","吵","落","厚","颗","料","一","呵","鹊"],"y":["繁","松","青","存","排","初","猜","纯","闻","千"],"z":["卧","堂","等","讲","困","热","灰","且","都","哎"],"0":["举","给","荔","忽","促","见","长","失","路","冻"],"1":["有","喵","撕","极","替","刚","首","睁","猫","从"],"2":["哇","趁","扛","峰","止","麦","叶","抖","柳","抱"],"3":["铺","剪","荷","新","拽","度","跨","晕","痛","段"],"4":["姐","哈","碰","飞","同","天","跃","倒","对","恋"],"5":["解","未","走","所","空","樱","音","断","蕊","醒"],"6":["哄","宽","蓝","游","勇","瀑","掐","惊","伞","蝶"],"7":["实","泊","狐","船","藏","淡","肯","又","妹","层"],"8":["朵","万","摇","豆","帽","呱","狮","粉","枣","叉"],"9":["斤","岂","豹","暗","梯","护","让","半","明","他"],"+":["绝","硬","莲","懂","枯","鸟","道","福","扭","乏"],"/":["扯","橙","尖","竟","古","稀","慕","街","站","全"],"=":["车","在","杯","念","竹","迎","亿","椅","河","优"]};const CN2B64=new Map();for(const[b64,arr]of Object.entries(CharSets1)){const code=b64.charCodeAt(0);for(const ch of arr){CN2B64.set(ch,code);}}
|
||||
const mappingMode1={base64ToChinese(b64){const out=[];for(const ch of b64){const pool=CharSets1[ch];if(pool)out.push(pool[(Math.random()*10)|0]);}
|
||||
return out.join('');},chineseToBase64(str){const out=[];for(const ch of str){const code=CN2B64.get(ch);if(code!==undefined)out.push(String.fromCharCode(code));}
|
||||
const CharSets1={"A":["甜","猛","浪","帮","拧","双","烟","高","立","百"],"B":["轻","真","看","按","夕","暑","涛","单","拍","助"],"C":["凸","引","号","咯","到","鸣","喊","是","疑","桂"],"D":["缓","页","离","灾","苦","疏","也","拒","触","灯"],"E":["泉","室","城","杏","尝","细","曲","靠","机","院"],"F":["峦","望","蹲","聚","旋","楼","应","晴","曾","爱"],"G":["茶","叹","梨","别","怯","于","圆","昼","缺","少"],"H":["梁","集","还","棋","熟","气","抓","摸","视","才"],"I":["棉","浓","着","湖","晃","但","团","甚","吓","涨"],"J":["猴","除","拉","意","成","抬","唱","春","宠","救"],"K":["为","卷","愁","功","晨","留","线","啦","忙","闪"],"L":["想","片","愿","准","兔","烦","勿","消","棕","吹"],"M":["棒","本","盯","却","喝","虹","桥","嘻","被","更"],"N":["拆","学","已","稻","使","摆","熊","秒","慢","越"],"O":["拿","拥","人","补","闹","了","没","续","火","停"],"P":["雀","羊","墙","并","冷","情","琴","散","江","小"],"Q":["响","太","虎","桌","压","我","今","影","拙","个"],"R":["跑","过","店","蜂","晓","变","条","雷","方","正"],"S":["凭","晚","得","耳","伴","星","贵","洁","蚊","叫"],"T":["定","多","堆","山","否","凉","柚","米","平","旗"],"U":["香","床","经","穿","迷","允","稍","老","观","瓣"],"V":["羞","搭","表","竖","陪","稠","柱","锅","窗","答"],"W":["分","送","包","重","只","滑","虚","柔","担","你"],"X":["聪","接","然","赶","兴","斜","医","敲","劝","虽"],"Y":["扳","难","非","温","增","顺","梅","水","躺","沉"],"Z":["生","块","地","易","直","黑","李","问","狼","往"],"a":["木","鱼","雨","慌","园","时","鼓","若","思","桃"],"b":["连","鹿","密","诉","厅","简","剩","年","错","现"],"c":["头","转","跟","用","扶","屋","满","墨","旧","虾"],"d":["薄","嗯","帅","知","志","捏","就","韧","柜","根"],"e":["起","岭","升","梦","钻","净","书","种","海","隐"],"f":["化","馆","啊","推","脆","灵","月","退","桑","吧"],"g":["败","爬","令","演","而","横","霞","光","张","疼"],"h":["心","急","夏","编","坐","扎","暖","加","挤","藤"],"i":["门","倦","盛","鲜","忧","矮","粗","感","假","听"],"j":["纸","必","鞋","蟹","厨","友","兆","电","勤","无"],"k":["因","凹","的","软","把","累","象","吉","池","带"],"l":["烫","眠","紫","降","扁","房","渐","呜","针","说"],"m":["读","静","白","枕","闭","窄","蛇","谈","红","休"],"n":["支","向","咕","握","冰","云","盆","寒","提","漂"],"o":["风","进","拖","闲","短","跳","托","夜","粒","显"],"p":["牛","金","很","呢","花","句","哪","以","清","洋"],"q":["嫩","减","酸","觉","嘘","弱","躲","秋","背","好"],"r":["谷","保","倚","遍","燕","咩","快","画","不","回"],"s":["盘","勺","亮","登","浮","皱","树","兰","列","节"],"t":["弟","行","扇","镜","阶","衣","篇","哦","富","奔"],"u":["叠","枝","挑","碗","零","大","品","冬","幸","呀"],"v":["惭","系","户","或","捧","钟","确","合","辣","兄"],"w":["袜","活","随","结","巧","乘","箱","次","眨","雪"],"x":["略","美","吵","落","厚","颗","料","一","呵","鹊"],"y":["繁","松","青","存","排","初","猜","纯","闻","千"],"z":["卧","堂","等","讲","困","热","灰","且","都","哎"],"0":["举","给","荔","忽","促","见","长","失","路","冻"],"1":["有","喵","撕","极","替","刚","首","睁","猫","从"],"2":["哇","趁","扛","峰","止","麦","叶","抖","柳","抱"],"3":["铺","剪","荷","新","拽","度","跨","晕","痛","段"],"4":["姐","哈","碰","飞","同","天","跃","倒","对","恋"],"5":["解","未","走","所","空","樱","音","断","蕊","醒"],"6":["哄","宽","蓝","游","勇","瀑","掐","惊","伞","蝶"],"7":["实","泊","狐","船","藏","淡","肯","又","妹","层"],"8":["朵","万","摇","豆","帽","呱","狮","粉","枣","叉"],"9":["斤","岂","豹","暗","梯","护","让","半","明","他"],"+":["绝","硬","莲","懂","枯","鸟","道","福","扭","乏"],"/":["扯","橙","尖","竟","古","稀","慕","街","站","全"],"=":["车","在","杯","念","竹","迎","亿","椅","河","优"]};const CN2B64={};for(const[b64,arr]of Object.entries(CharSets1)){const code=b64.charCodeAt(0);for(const ch of arr){CN2B64[ch]=code;}}
|
||||
const mappingMode1={Base64ToChinese(b64){const out=[];for(const ch of b64){const pool=CharSets1[ch];if(pool)out.push(pool[(Math.random()*10)|0]);}
|
||||
return out.join('');},ChineseToBase64(str){const out=[];for(const ch of str){const code=CN2B64[ch];if(code!==undefined)out.push(String.fromCharCode(code));}
|
||||
return out.join('');}};
|
||||
File diff suppressed because one or more lines are too long
@@ -1,43 +1,42 @@
|
||||
const CharSets3={'0':'\u200C','1':'\u200D','2':'\u2060','3':'\u2061','4':'\u2062','5':'\u2063','6':'\u2064','7':'\u206A','8':'\u206B','9':'\u206C','a':'\u206D','b':'\u206E','c':'\u206F','d':'\u034F','e':'\uFEFF','f':'\u061C'};const ReverseCharSets3=Object.fromEntries(Object.entries(CharSets3).map(([k,v])=>[v,k]));const PAD_MARKER='\u200B';const BYTE2ZW=new Array(256);for(let i=0;i<256;i++){BYTE2ZW[i]=CharSets3[(i>>>4).toString(16)]+CharSets3[(i&0x0F).toString(16)];}
|
||||
const C4=CharSets3;const BASE64_POOL='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';const randB64=n=>{if(n<=0)return'';const buf=new Uint32Array(n);crypto.getRandomValues(buf);let s='';for(let i=0;i<n;i++)s+=BASE64_POOL[buf[i]&63];return s;};const mappingMode3={base64ToZeroWidth(str,prefixLen=1,suffixLen=1){const bytes=new TextEncoder().encode(str);const zw=new Array(bytes.length);for(let i=0;i<bytes.length;i++)zw[i]=BYTE2ZW[bytes[i]];const padBits=(4-((bytes.length*8)&3))&3;return randB64(prefixLen)+zw.join('')+C4[padBits.toString(16)]+randB64(suffixLen);},zeroWidthToBase64(str){const nib=[];for(const ch of str){const n=parseInt(ReverseCharSets3[ch],16);if(!isNaN(n))nib.push(n);}
|
||||
const C4=CharSets3;const BASE64_POOL='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';const randB64=n=>{if(n<=0)return'';const buf=new Uint32Array(n);crypto.getRandomValues(buf);let s='';for(let i=0;i<n;i++)s+=BASE64_POOL[buf[i]&63];return s;};const mappingMode3={Base64ToZeroWidth(str,prefixLen=1,suffixLen=1){const bytes=new TextEncoder().encode(str);const zw=new Array(bytes.length);for(let i=0;i<bytes.length;i++)zw[i]=BYTE2ZW[bytes[i]];const padBits=(4-((bytes.length*8)&3))&3;return randB64(prefixLen)+zw.join('')+C4[padBits.toString(16)]+randB64(suffixLen);},zeroWidthToBase64(str){const nib=[];for(const ch of str){const n=parseInt(ReverseCharSets3[ch],16);if(!isNaN(n))nib.push(n);}
|
||||
if(nib.length<2)return'';const padBits=nib.pop()*4;const dataBits=nib.length*4-padBits;const byteLen=dataBits>>3;if(byteLen<=0)return'';const bytes=new Uint8Array(byteLen);let buf=0,bits=0,idx=0;for(let i=0;i<nib.length;i++){buf=(buf<<4)|nib[i];bits+=4;if(bits>=8){bytes[idx++]=buf>>>(bits-8);bits-=8;}}
|
||||
return new TextDecoder().decode(bytes);},async encodeBlob(blob,prefixLen=4,suffixLen=4,chunkSize=512*1024){if(!(blob instanceof Blob))throw new TypeError('输入必须是Blob对象');const cores=navigator.hardwareConcurrency||4;const tasks=Math.ceil(blob.size/chunkSize);const results=new Array(tasks);let done=0;const workerSrc=`
|
||||
|
||||
const C4 = ${JSON.stringify(CharSets3)};
|
||||
const C4 = ${JSON.stringify(CharSets3)};
|
||||
|
||||
const BYTE2ZW = ${JSON.stringify(BYTE2ZW)};
|
||||
const BYTE2ZW = ${JSON.stringify(BYTE2ZW)};
|
||||
|
||||
const BASE64_POOL = '${BASE64_POOL}';
|
||||
const BASE64_POOL = '${BASE64_POOL}';
|
||||
|
||||
const rand = n => {
|
||||
const rand = n => {
|
||||
|
||||
const buf = new Uint32Array(n);
|
||||
const buf = new Uint32Array(n);
|
||||
|
||||
crypto.getRandomValues(buf);
|
||||
crypto.getRandomValues(buf);
|
||||
|
||||
let s = '';
|
||||
let s = '';
|
||||
|
||||
for (let i = 0; i < n; ++i) s += BASE64_POOL[buf[i] & 63];
|
||||
for (let i = 0; i < n; ++i) s += BASE64_POOL[buf[i] & 63];
|
||||
|
||||
return s;
|
||||
return s;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
onmessage = async ({data}) => {
|
||||
onmessage = async ({data}) => {
|
||||
|
||||
const slice = data.blob.slice(data.offset, data.offset + data.chunkSize);
|
||||
const slice = data.blob.slice(data.offset, data.offset + data.chunkSize);
|
||||
|
||||
const bytes = new Uint8Array(await slice.arrayBuffer());
|
||||
const bytes = new Uint8Array(await slice.arrayBuffer());
|
||||
|
||||
let zw = '';
|
||||
let zw = '';
|
||||
|
||||
for (let i = 0; i < bytes.length; i++) zw += BYTE2ZW[bytes[i]];
|
||||
for (let i = 0; i < bytes.length; i++) zw += BYTE2ZW[bytes[i]];
|
||||
|
||||
const padBits = (4 - ((bytes.length * 8) & 3)) & 3;
|
||||
const padBits = (4 - ((bytes.length * 8) & 3)) & 3;
|
||||
|
||||
postMessage({idx: data.idx, res: rand(data.prefixLen) + zw + C4[padBits.toString(16)] + rand(data.suffixLen)});
|
||||
postMessage({idx: data.idx, res: rand(data.prefixLen) + zw + C4[padBits.toString(16)] + rand(data.suffixLen)});
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
`;const url=URL.createObjectURL(new Blob([workerSrc],{type:'text/javascript'}));return new Promise((resolve,reject)=>{const workers=Array.from({length:cores},()=>new Worker(url));workers.forEach(w=>{w.onmessage=({data})=>{results[data.idx]=data.res;if(++done===tasks){workers.forEach(x=>x.terminate());URL.revokeObjectURL(url);resolve(results.join(''));}};w.onerror=e=>{reject(e);workers.forEach(x=>x.terminate());URL.revokeObjectURL(url);};});for(let i=0;i<Math.min(cores,tasks);i++){workers[i].postMessage({blob,prefixLen,suffixLen,idx:i,offset:i*chunkSize,chunkSize});}});},decodeToBlob(str,mimeType='text/plain'){try{const b64=this.zeroWidthToBase64(str);const bin=atob(b64);const bytes=new Uint8Array(bin.length);for(let i=0;i<bin.length;i++)bytes[i]=bin.charCodeAt(i);return new Blob([bytes],{type:mimeType});}
|
||||
catch{return new Blob(['decode error'],{type:'text/plain'});}},getEmojiLength:s=>[...s].length};
|
||||
`;const url=URL.createObjectURL(new Blob([workerSrc],{type:'text/javascript'}));return new Promise((resolve,reject)=>{const workers=Array.from({length:cores},()=>new Worker(url));workers.forEach(w=>{w.onmessage=({data})=>{results[data.idx]=data.res;if(++done===tasks){workers.forEach(x=>x.terminate());URL.revokeObjectURL(url);resolve(results.join(''));}};w.onerror=e=>{reject(e);workers.forEach(x=>x.terminate());URL.revokeObjectURL(url);};});for(let i=0;i<Math.min(cores,tasks);i++){workers[i].postMessage({blob,prefixLen,suffixLen,idx:i,offset:i*chunkSize,chunkSize});}});},decodeToBlob(str,mimeType='text/plain'){try{const b64=this.zeroWidthToBase64(str);const bin=atob(b64);const bytes=new Uint8Array(bin.length);for(let i=0;i<bin.length;i++)bytes[i]=bin.charCodeAt(i);return new Blob([bytes],{type:mimeType});}catch{return new Blob(['decode error'],{type:'text/plain'});}},getEmojiLength:s=>[...s].length};
|
||||
4
js/mapping-mode-4.js
Normal file
4
js/mapping-mode-4.js
Normal file
@@ -0,0 +1,4 @@
|
||||
const CharSets4={"A":["ザ","ヒ","ケ"],"B":["ょ","ぴ","ド"],"C":["ヵ","ひ","ㇱ"],"D":["か","ウ","び"],"E":["ㇰ","ピ","ト"],"F":["モ","ン","レ"],"G":["マ","ぼ","ィ"],"H":["い","ゲ","ㇲ"],"I":["ゖ","ㇵ","る"],"J":["グ","ゟ","サ"],"K":["ォ","ュ","ㇿ"],"L":["ん","ゥ","ゔ"],"M":["セ","〲","゛"],"N":["れ","す","わ"],"O":["ダ","ゴ","そ"],"P":["ㇷ","ヴ","ぞ"],"Q":["ぽ","づ","じ"],"R":["つ","み","バ"],"S":["デ","ま","ㇼ"],"T":["な","ヹ","ぁ"],"U":["〻","お","ㇻ"],"V":["プ","ギ","ベ"],"W":["ゎ","ョ","に"],"X":["ヷ","こ","が"],"Y":["ざ","ヘ","ス"],"Z":["ゃ","ア","ミ"],"a":["ね","ふ","ゝ"],"b":["〳","ソ","ば"],"c":["ズ","ぺ","ペ"],"d":["ㇽ","ぐ","ゑ"],"e":["ゾ","ァ","ぅ"],"f":["ポ","も","ぷ"],"g":["ご","ㇸ","し"],"h":["ル","む","ら"],"i":["ヾ","ㇹ","ろ"],"j":["ジ","ャ","ボ"],"k":["ヲ","シ","ゼ"],"l":["ゐ","ッ","え"],"m":["ワ","テ","ヿ"],"n":["〴","う","き"],"o":["パ","ㇶ","ガ"],"p":["を","ラ","っ"],"q":["リ","ヂ","ゕ"],"r":["ぎ","メ","ヶ"],"s":["ク","〵","ビ"],"t":["ヺ","で","く"],"u":["よ","ゆ","ㇺ"],"v":["の","ぉ","ノ"],"w":["フ","ム","せ"],"x":["ゞ","や","々"],"y":["め","タ","ぬ"],"z":["ㇳ","べ","チ"],"0":["ナ","だ","げ"],"1":["ぶ","け","ェ"],"2":["ヱ","ヨ","ぇ"],"3":["た","ゅ","ち"],"4":["ぃ","キ","ぢ"],"5":["ブ","ニ","ヸ"],"6":["ヮ","ほ","り"],"7":["ぱ","ぜ","ヌ"],"8":["ネ","ㇴ","ユ"],"9":["さ","ㇾ","ど"],"+":["て","イ","ヤ"],"/":["ツ","ず","ヅ"],"=":["は","と","あ"]};const JP2B64=new Map();for(const[b64,arr]of Object.entries(CharSets4)){const code=b64.charCodeAt(0);for(const ch of arr){JP2B64.set(ch,code);}}
|
||||
const mappingMode4={Base64ToJapanese(b64){const out=[];for(const ch of b64){const pool=CharSets4[ch];if(pool)out.push(pool[Math.floor(Math.random()*pool.length)]);}
|
||||
return out.join('');},JapaneseToBase64(str){const out=[];for(const ch of str){const code=JP2B64.get(ch);if(code!==undefined)out.push(String.fromCharCode(code));}
|
||||
return out.join('');}};
|
||||
@@ -1,8 +1,7 @@
|
||||
const DEFAULT_PASSWORD='a184f7b849ffffed24d266a30298c72ef2f5ad040db73bf37151fac767630728';const STORAGE_KEYS={THEME_MODE:'theme_mode',OUTPUT_MODE:'output_mode'};const ZERO_WIDTH_REGEX=/[\u200B-\u200F\uFEFF\u202A-\u202E\u2060-\u206F]/u;const BASE64_CHARS_REGEX=/[^A-Za-z0-9+/=]/g;const BASE64_FORMAT_REGEX=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u;const EMOJI_REGEX=/[\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;const CHINESE_REGEX=/[\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF]/u;const PASSWORD_CHARSET="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+~`|}{[]:;?><,./-='\"\\";const textEncoder=new TextEncoder();(function applyThemeEarly(){const savedThemeMode=localStorage.getItem(STORAGE_KEYS.THEME_MODE);const isDarkMode=savedThemeMode?savedThemeMode==='dark':window.matchMedia('(prefers-color-scheme: dark)').matches;if(isDarkMode){document.documentElement.classList.add('dark');}})();async function handleStream(stream,writerData){const writer=stream.writable.getWriter();await writer.write(writerData);await 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 toUint8Array=data=>data instanceof Uint8Array?data:new Uint8Array(typeof data==='string'?textEncoder.encode(data):data);const utils={arrayBufferToBase64:buffer=>sodium.to_base64(new Uint8Array(buffer),sodium.base64_variants.ORIGINAL),base64ToArrayBuffer:base64=>sodium.from_base64(base64,sodium.base64_variants.ORIGINAL).buffer,stringToArrayBuffer:str=>sodium.from_string(str).buffer,arrayBufferToString:buffer=>sodium.to_string(new Uint8Array(buffer)),generateRandomBytes:length=>sodium.randombytes_buf(length),compressData(data){if(typeof pako==='undefined'){throw new Error('压缩失败,pako库未加载');}
|
||||
const DEFAULT_PASSWORD='a184f7b849ffffed24d266a30298c72ef2f5ad040db73bf37151fac767630728';const STORAGE_KEYS={THEME_MODE:'theme_mode',OUTPUT_MODE:'output_mode',SYMMETRIC_PASSWORD:'symmetric_password'};const ZERO_WIDTH_REGEX=/[\u200B-\u200F\uFEFF\u202A-\u202E\u2060-\u206F]/u;const BASE64_CHARS_REGEX=/[^A-Za-z0-9+/=]/g;const BASE64_FORMAT_REGEX=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u;const EMOJI_REGEX=/[\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;const CHINESE_REGEX=/[\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF]/u;const JAPANESE_REGEX=/[\u3040-\u309F\u30A0-\u30FF\uFF66-\uFF9F]/u;const PASSWORD_CHARSET="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+~`|}{[]:;?><,./-='\"\\";const textEncoder=new TextEncoder();(function applyThemeEarly(){const savedThemeMode=localStorage.getItem(STORAGE_KEYS.THEME_MODE);const isDarkMode=savedThemeMode?savedThemeMode==='dark':window.matchMedia('(prefers-color-scheme: dark)').matches;if(isDarkMode){document.documentElement.classList.add('dark');}})();const toUint8Array=data=>data instanceof Uint8Array?data:new Uint8Array(typeof data==='string'?textEncoder.encode(data):data);const utils={arrayBufferToBase64:buffer=>sodium.to_base64(new Uint8Array(buffer),sodium.base64_variants.ORIGINAL),Base64ToArrayBuffer:base64=>sodium.from_base64(base64,sodium.base64_variants.ORIGINAL).buffer,stringToArrayBuffer:str=>sodium.from_string(str).buffer,arrayBufferToString:buffer=>sodium.to_string(new Uint8Array(buffer)),generateRandomBytes:length=>sodium.randombytes_buf(length),compressData(data){if(typeof pako==='undefined'){throw new Error('压缩失败,pako库未加载');}
|
||||
return pako.deflateRaw(toUint8Array(data),{level:9});},decompressData(data){if(typeof pako==='undefined'){throw new Error('解压失败,pako库未加载');}
|
||||
const uint8Data=toUint8Array(data);try{return pako.inflateRaw(uint8Data);}catch{return pako.inflate(uint8Data);}},generatePassword(length){return Array.from({length},()=>PASSWORD_CHARSET[sodium.randombytes_uniform(PASSWORD_CHARSET.length)]).join('');},copyToClipboard(text){navigator.clipboard.writeText(text).catch(err=>{console.error('复制失败:',err);showNotification('复制到剪贴板失败,请手动复制。',false);});},detectCiphertextType(ciphertext){const limitedText=ciphertext.slice(0,1000);if(ZERO_WIDTH_REGEX.test(limitedText)){return'zero-width';}
|
||||
const base64Chars=limitedText.replace(BASE64_CHARS_REGEX,'');const base64Ratio=base64Chars.length/limitedText.length;if(base64Ratio>0.8&&BASE64_FORMAT_REGEX.test(base64Chars)){return'base64';}
|
||||
if(EMOJI_REGEX.test(limitedText)){return'emoji';}
|
||||
if(CHINESE_REGEX.test(limitedText)){return'chinese';}},saveThemeMode(mode){if(mode==='light'||mode==='dark'){localStorage.setItem(STORAGE_KEYS.THEME_MODE,mode);}},getSavedThemeMode(){return localStorage.getItem(STORAGE_KEYS.THEME_MODE);},saveOutputMode(mode){const validModes=['chinese','base64','emoji','zero-width'];if(validModes.includes(mode)){localStorage.setItem(STORAGE_KEYS.OUTPUT_MODE,mode);}},getSavedOutputMode(){return localStorage.getItem(STORAGE_KEYS.OUTPUT_MODE)||'chinese';}};
|
||||
if(CHINESE_REGEX.test(limitedText)){return'chinese';}
|
||||
if(JAPANESE_REGEX.test(limitedText)){return'japanese';}},saveThemeMode(mode){if(mode==='light'||mode==='dark'){localStorage.setItem(STORAGE_KEYS.THEME_MODE,mode);}},getSavedThemeMode(){return localStorage.getItem(STORAGE_KEYS.THEME_MODE);},saveOutputMode(mode){const validModes=['chinese','base64','emoji','zero-width','japanese'];if(validModes.includes(mode)){localStorage.setItem(STORAGE_KEYS.OUTPUT_MODE,mode);}},getSavedOutputMode(){return localStorage.getItem(STORAGE_KEYS.OUTPUT_MODE)||'chinese';},savePassword(password){if(password){localStorage.setItem(STORAGE_KEYS.SYMMETRIC_PASSWORD,password);}else{localStorage.removeItem(STORAGE_KEYS.SYMMETRIC_PASSWORD);}},getSavedPassword(){return localStorage.getItem(STORAGE_KEYS.SYMMETRIC_PASSWORD);}};
|
||||
@@ -1,4 +1,4 @@
|
||||
function showNotification(message,isSuccess=true){const container=document.createElement('div');container.className='fixed right-4 transition-all duration-300 ease-in-out translate-x-full z-50';container.style.top=`${(document.querySelector('nav')?.offsetHeight || 0) + 10}px`;const setWidth=()=>container.style.width=window.innerWidth<640?'90%':'280px';setWidth();window.addEventListener('resize',setWidth);const isDark=document.documentElement.classList.contains('dark');const[color,icon]=isSuccess?['green','check']:['red','exclamation'];container.innerHTML=`
|
||||
function showNotification(message,isSuccess=true){const container=document.createElement('div');container.className='fixed right-4 transition-all duration-300 ease-in-out translate-x-full z-50';container.style.top=`${(document.querySelector('nav')?.offsetHeight || 0) + 10}px`;const setWidth=()=>container.style.width=window.innerWidth<640?'90%':'380px';setWidth();window.addEventListener('resize',setWidth);const isDark=document.documentElement.classList.contains('dark');const[color,icon]=isSuccess?['green','check']:['red','exclamation'];container.innerHTML=`
|
||||
<div class="rounded-lg shadow-lg p-3 flex items-start border-l-4 border-${color}-500 ${isDark ? 'bg-gray-800 border-gray-700 text-white' : 'bg-white'}">
|
||||
<div class="flex-shrink-0 mt-0.5 mr-2 text-${color}-500">
|
||||
<i class="fa-solid fa-${icon}-circle"></i>
|
||||
@@ -13,5 +13,5 @@ function showNotification(message,isSuccess=true){const container=document.creat
|
||||
`;document.body.appendChild(container);const closeBtn=container.querySelector('button');setTimeout(()=>container.classList.remove('translate-x-full'),10);const remove=()=>{container.classList.add('translate-x-full');setTimeout(()=>container.remove(),300);};const timeoutId=setTimeout(remove,5000);closeBtn.addEventListener('click',()=>{clearTimeout(timeoutId);remove();});return container;}
|
||||
function clearCryptoCache(){if(window.cryptoTempData){Object.values(window.cryptoTempData).forEach(data=>{if(data instanceof ArrayBuffer)new Uint8Array(data).fill(0);});window.cryptoTempData={};}
|
||||
window.cryptoContext=null;typeof window.gc==='function'&&window.gc();}
|
||||
document.addEventListener('DOMContentLoaded',()=>{const els=Object.fromEntries(['text-input','result-output','encryption-key','encrypt-btn','decrypt-btn','copy-btn','clear-btn','toggle-key-visibility','generate-password','result-status','theme-toggle','base64-toggle','paste-btn'].map(id=>[id.replace(/-./g,m=>m[1].toUpperCase()),document.getElementById(id)]));window.cryptoTempData={};window.cryptoContext=null;const savedTheme=utils.getSavedThemeMode();const isDark=(savedTheme||(window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'))==='dark';document.documentElement.classList.toggle('dark',isDark);els.themeToggle.innerHTML=`<i class="fa-solid fa-${isDark ? 'moon' : 'sun'} ${isDark ? 'text-white' : 'text-dark'}"></i>`;const modes=['chinese','base64','emoji','zero-width'];const modeNames={chinese:'中文',base64:'Base64',emoji:'Emoji','zero-width':'零宽'};const icons={chinese:'fa-solid fa-language',base64:'fa-solid fa-code',emoji:'fa-regular fa-face-smile','zero-width':'fa-brands fa-creative-commons-zero'};let outputMode=utils.getSavedOutputMode();els.base64Toggle.innerHTML=`<i class="${icons[outputMode]} theme-toggle-icon"></i>`;if(!window.crypto?.subtle){showNotification('不支持Web Crypto API,无法使用加/解密功能',false);els.encryptBtn.disabled=els.decryptBtn.disabled=true;}
|
||||
const baseStatusClass='absolute top-3 right-3 px-2 py-1 rounded-full text-xs font-medium';let isProcessing=false;const handleAction=async(action,inputGetter)=>{if(isProcessing)return showNotification('操作处理中,请稍候',false);const input=inputGetter();if(!input)return showNotification(`请输入要${action}的文本`,false);let password=els.encryptionKey.value.trim();const isDefault=!password;isDefault&&(password=DEFAULT_PASSWORD);try{isProcessing=true;els.encryptBtn.disabled=els.decryptBtn.disabled=true;els.resultStatus.textContent=`${action}中......`;els.resultStatus.className=`${baseStatusClass} bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200`;const result=await new Promise((resolve,reject)=>{setTimeout(async()=>{try{resolve(action==='加密'?await encryptionMethod1.encrypt(input,password,outputMode):await encryptionMethod1.decrypt(input,password));}catch(e){reject(e);}},0);});els.resultOutput.value=result;els.resultStatus.textContent=`${action}成功`;els.resultStatus.className=`${baseStatusClass} bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200`;showNotification(isDefault?`未检测到密码,现使用默认密码${action},建议更换为安全的密码。`:`文本已被${action}了`,true);}catch(e){els.resultOutput.value='';els.resultStatus.textContent=`${action}失败`;els.resultStatus.className=`${baseStatusClass} bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200`;showNotification(e.message,false);}finally{clearCryptoCache();setTimeout(()=>els.resultStatus.classList.add('hidden'),1200);isProcessing=false;els.encryptBtn.disabled=els.decryptBtn.disabled=false;}};Object.entries({generatePassword:()=>{const pwd=utils.generatePassword(25);els.encryptionKey.value=pwd;utils.copyToClipboard(pwd);showNotification('已生成并复制25位随机密码');},toggleKeyVisibility:()=>{const isPassword=els.encryptionKey.type==='password';els.encryptionKey.type=isPassword?'text':'password';els.toggleKeyVisibility.innerHTML=`<i class="fa-regular fa-${isPassword ? 'eye' : 'eye-slash'}"></i>`;},base64Toggle:()=>{outputMode=modes[(modes.indexOf(outputMode)+1)%modes.length];utils.saveOutputMode(outputMode);els.base64Toggle.innerHTML=`<i class="${icons[outputMode]} theme-toggle-icon"></i>`;showNotification(`已切换到 ${modeNames[outputMode]} 密文`);},pasteBtn:()=>navigator.clipboard.readText().then(text=>{els.textInput.value=text;showNotification('已从剪贴板粘贴文本');}).catch(()=>els.textInput.select()),encryptBtn:()=>handleAction('加密',()=>els.textInput.value),decryptBtn:()=>handleAction('解密',()=>els.textInput.value.trim()),copyBtn:()=>{if(!els.resultOutput.value)return showNotification('没有可复制的结果',false);utils.copyToClipboard(els.resultOutput.value);showNotification('结果已复制到剪贴板');},clearBtn:()=>{els.textInput.value=els.resultOutput.value=els.encryptionKey.value='';els.resultStatus.classList.add('hidden');showNotification('已清空所有内容');},themeToggle:()=>{const isDark=document.documentElement.classList.toggle('dark');utils.saveThemeMode(isDark?'dark':'light');els.themeToggle.innerHTML=`<i class="fa-solid fa-${isDark ? 'moon' : 'sun'} ${isDark ? 'text-white' : 'text-dark'}"></i>`;document.querySelectorAll('.fixed.right-4.transition-all.duration-300.ease-in-out.z-50').forEach(el=>el.remove());}}).forEach(([key,handler])=>els[key]?.addEventListener('click',handler));});
|
||||
document.addEventListener('DOMContentLoaded',()=>{const els=Object.fromEntries(['text-input','result-output','encryption-key','encrypt-btn','decrypt-btn','copy-btn','clear-btn','toggle-key-visibility','generate-password','save-password','result-status','theme-toggle','base64-toggle','paste-btn','encryption-mode-toggle','card-container'].map(id=>[id.replace(/-./g,m=>m[1].toUpperCase()),document.getElementById(id)]));window.cryptoTempData={};window.cryptoContext=null;const savedTheme=utils.getSavedThemeMode();const isDark=(savedTheme||(window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'))==='dark';document.documentElement.classList.toggle('dark',isDark);els.themeToggle.innerHTML=`<i class="fa-solid fa-${isDark ? 'moon' : 'sun'} ${isDark ? 'text-white' : 'text-dark'}"></i>`;els.encryptionKey.value=utils.getSavedPassword()||'';const modes=['chinese','base64','emoji','zero-width','japanese'];const modeNames={chinese:'中文',base64:'Base64',emoji:'Emoji','zero-width':'零宽',japanese:'日语'};const icons={chinese:'fa-solid fa-language',base64:'fa-solid fa-code',emoji:'fa-regular fa-face-smile','zero-width':'fa-brands fa-creative-commons-zero',japanese:'fa-regular fa-sun'};let outputMode=utils.getSavedOutputMode();els.base64Toggle.innerHTML=`<i class="${icons[outputMode]} theme-toggle-icon"></i>`;if(!window.crypto?.subtle){showNotification('不支持Web Crypto API,无法使用加/解密功能',false);els.encryptBtn.disabled=els.decryptBtn.disabled=true;}
|
||||
const baseStatusClass='absolute top-3 right-3 px-2 py-1 rounded-full text-xs font-medium';let isProcessing=false;const handleAction=async(action,inputGetter)=>{if(isProcessing)return showNotification('操作处理中,请稍候',false);const input=inputGetter();if(!input)return showNotification(`请输入要${action}的文本`,false);let password=els.encryptionKey.value.trim();const isDefault=!password;isDefault&&(password=DEFAULT_PASSWORD);try{isProcessing=true;els.encryptBtn.disabled=els.decryptBtn.disabled=true;els.resultStatus.textContent=`${action}中......`;els.resultStatus.className=`${baseStatusClass} bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200`;const result=await new Promise((resolve,reject)=>{setTimeout(async()=>{try{resolve(action==='加密'?await encryptionMethod1.encrypt(input,password,outputMode):await encryptionMethod1.decrypt(input,password));}catch(e){reject(e);}},0);});els.resultOutput.value=result;els.resultStatus.textContent=`${action}成功`;els.resultStatus.className=`${baseStatusClass} bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200`;showNotification(isDefault?`未检测到密码,现使用默认密码${action},建议更换为安全的密码。`:`文本已被${action}了`,true);}catch(e){els.resultOutput.value='';els.resultStatus.textContent=`${action}失败`;els.resultStatus.className=`${baseStatusClass} bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200`;showNotification(e.message,false);}finally{clearCryptoCache();setTimeout(()=>els.resultStatus.classList.add('hidden'),1200);isProcessing=false;els.encryptBtn.disabled=els.decryptBtn.disabled=false;}};Object.entries({generatePassword:()=>{const pwd=utils.generatePassword(25);els.encryptionKey.value=pwd;utils.copyToClipboard(pwd);showNotification('已生成并复制25位随机密码');},toggleKeyVisibility:()=>{const isPassword=els.encryptionKey.type==='password';els.encryptionKey.type=isPassword?'text':'password';els.toggleKeyVisibility.innerHTML=`<i class="fa-regular fa-${isPassword ? 'eye' : 'eye-slash'}"></i>`;},savePassword:()=>{const password=els.encryptionKey.value.trim();utils.savePassword(password);showNotification(password?'密码已存本地(需要确保设备安全),清空密码再保存即删除。':'本地密码已删除');},base64Toggle:()=>{outputMode=modes[(modes.indexOf(outputMode)+1)%modes.length];utils.saveOutputMode(outputMode);els.base64Toggle.innerHTML=`<i class="${icons[outputMode]} theme-toggle-icon"></i>`;showNotification(`已切换到 ${modeNames[outputMode]} 密文`);},pasteBtn:()=>navigator.clipboard.readText().then(text=>{els.textInput.value=text;showNotification('已从剪贴板粘贴文本');}).catch(()=>els.textInput.select()),encryptBtn:()=>handleAction('加密',()=>els.textInput.value),decryptBtn:()=>handleAction('解密',()=>els.textInput.value.trim()),copyBtn:()=>{if(!els.resultOutput.value)return showNotification('没有可复制的结果',false);utils.copyToClipboard(els.resultOutput.value);showNotification('结果已复制到剪贴板');},clearBtn:()=>{els.textInput.value=els.resultOutput.value='';els.resultStatus.classList.add('hidden');showNotification('已清空内容');},themeToggle:()=>{const isDark=document.documentElement.classList.toggle('dark');utils.saveThemeMode(isDark?'dark':'light');els.themeToggle.innerHTML=`<i class="fa-solid fa-${isDark ? 'moon' : 'sun'} ${isDark ? 'text-white' : 'text-dark'}"></i>`;document.querySelectorAll('.fixed.right-4.transition-all.duration-300.ease-in-out.z-50').forEach(el=>el.remove());}}).forEach(([key,handler])=>els[key]?.addEventListener('click',handler));let isFirstClick=true;els.encryptionModeToggle?.addEventListener('click',function(){const cardContainer=els.cardContainer;if(!cardContainer)return;const icon=this.querySelector('i');if(isFirstClick){navigator.clipboard.writeText('https://github.com/fzxx/XiangYue').catch(()=>{});alert('非对称加密视 Github Stars(关注)量再考虑,太少人用没有增加的必要\n\nGithub地址已复制 https://github.com/fzxx/XiangYue\n\n再次点按钮预可览非对称加密页面。');isFirstClick=false;}else{cardContainer.classList.toggle('flipped');if(cardContainer.classList.contains('flipped')){icon.classList.remove('fa-exchange');icon.classList.add('fa-sync-alt');}else{icon.classList.remove('fa-sync-alt');icon.classList.add('fa-exchange');}}});});
|
||||
Reference in New Issue
Block a user